@blueking/chat-x 0.0.45-beta.2 → 0.0.45-beta.3

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 (51) hide show
  1. package/dist/ag-ui/types/contents.d.ts +5 -0
  2. package/dist/ag-ui/types/interrupt.d.ts +35 -5
  3. package/dist/components/ai-buttons/tool-btn/tool-btn.vue.d.ts +11 -1
  4. package/dist/components/chat-content/flow-agent-content/flow-agent-content.vue.d.ts +4 -0
  5. package/dist/components/chat-content/flow-agent-content/flow-agent-node-detail.vue.d.ts +2 -0
  6. package/dist/components/chat-content/flow-agent-content/flow-agent-state.d.ts +34 -0
  7. package/dist/components/chat-content/flow-agent-content/use-flow-agent.d.ts +51 -0
  8. package/dist/components/chat-content/flow-agent-content/use-flow-node-actions.d.ts +32 -0
  9. package/dist/components/chat-content/flow-agent-content/use-flow-tab.d.ts +18 -0
  10. package/dist/components/chat-input/ai-slash-input/ai-skill-list/ai-skill-list.vue.d.ts +8 -0
  11. package/dist/components/chat-input/ai-slash-input/ai-slash-input.vue.d.ts +3 -1
  12. package/dist/components/chat-input/chat-input.vue.d.ts +3 -1
  13. package/dist/components/chat-message/activity-message/activity-message.vue.d.ts +4 -1
  14. package/dist/components/index.d.ts +4 -1
  15. package/dist/composables/index.d.ts +1 -0
  16. package/dist/composables/use-common.d.ts +1 -1
  17. package/dist/composables/use-custom-tab.d.ts +1 -1
  18. package/dist/composables/use-full-screen.d.ts +17 -0
  19. package/dist/composables/use-message-group.d.ts +9 -0
  20. package/dist/icons/execution.d.ts +3 -0
  21. package/dist/icons/index.d.ts +1 -0
  22. package/dist/icons/screen.d.ts +6 -0
  23. package/dist/index.css +1 -1
  24. package/dist/index.js +4335 -4108
  25. package/dist/index.js.map +1 -1
  26. package/dist/lang/lang.d.ts +5 -1
  27. package/dist/mcp/generated/docs/activity-message.md +33 -9
  28. package/dist/mcp/generated/docs/chat-container.md +78 -19
  29. package/dist/mcp/generated/docs/file-upload-btn.md +1 -1
  30. package/dist/mcp/generated/docs/flow-agent-content.md +47 -7
  31. package/dist/mcp/generated/docs/interrupt-message.md +2 -2
  32. package/dist/mcp/generated/docs/interrupt.md +80 -8
  33. package/dist/mcp/generated/docs/markdown-content.md +23 -0
  34. package/dist/mcp/generated/docs/message-container.md +1 -1
  35. package/dist/mcp/generated/docs/message-render.md +3 -3
  36. package/dist/mcp/generated/docs/message-tools.md +3 -3
  37. package/dist/mcp/generated/docs/shortcut-render.md +5 -1
  38. package/dist/mcp/generated/docs/tool-approval-card.md +2 -2
  39. package/dist/mcp/generated/docs/tool-btn.md +37 -6
  40. package/dist/mcp/generated/docs/use-flow-node-actions.md +124 -0
  41. package/dist/mcp/generated/docs/use-full-screen.md +114 -0
  42. package/dist/mcp/generated/index.json +33 -1
  43. package/dist/plugins/index.d.ts +1 -0
  44. package/dist/plugins/markdown-bk-inline-style.d.ts +20 -0
  45. package/dist/types/custom.d.ts +1 -0
  46. package/dist/types/editor.d.ts +6 -0
  47. package/dist/types/input.d.ts +1 -1
  48. package/dist/types/tool.d.ts +3 -3
  49. package/dist/utils/index.d.ts +1 -0
  50. package/dist/utils/utils.d.ts +1 -0
  51. package/package.json +1 -1
@@ -44,6 +44,7 @@ export declare const lang: {
44
44
  readonly 挂起: "Pending";
45
45
  readonly 待执行: "To Be Executed";
46
46
  readonly 详情: "Details";
47
+ readonly 重试: "Retry";
47
48
  readonly 节点: "Node";
48
49
  readonly 节点配置: "Node Config";
49
50
  readonly 节点输出: "Node Output";
@@ -115,6 +116,9 @@ export declare const lang: {
115
116
  readonly '\u4F60\u597D\uFF0C\u6211\u662F\u5C0F\u9CB8': "Hello, I am BlueKing AI Bot";
116
117
  readonly 清空搜索: "Clear Search";
117
118
  readonly 搜索结果为空: "Search Result is Empty";
119
+ readonly 有效证据: "Valid Evidence";
120
+ readonly 全屏: "Full Screen";
121
+ readonly 退出全屏: "Exit Full Screen";
118
122
  readonly 请选择以继续: "Please choose to continue";
119
123
  readonly 继续: "Continue";
120
124
  readonly '\u6536\u5230\u4FE1\u606F\uFF1A': "Received: ";
@@ -129,4 +133,4 @@ export declare const lang: {
129
133
  readonly 已回复: "Replied";
130
134
  readonly '\u8BF7\u8F93\u5165...': "Please enter...";
131
135
  };
132
- export declare const t: (key: keyof typeof lang) => "Send" | "Stop" | "Ask AI" | "Copy" | "Share" | "Like" | "Unsatisfied" | "Delete" | "Quote" | "Regenerate" | "Regenerating will clear the content below" | "Submit" | "Cancel" | "Preview Content" | "Jump to Detail" | "Call Tool:" | "Calling..." | "Call Success" | "Call Failed" | "Tell us your thoughts" | "What makes you satisfied?" | "What makes you dissatisfied?" | "Return Content" | "Edit" | "Deep Thinking" | "Loading image..." | "Failed to load image" | "Thinking..." | "Thinking Completed" | "Thinking Failed" | "Copy Success" | "Copy Failed" | "Return to bottom" | "Stop generating" | "Stopping" | "Duration" | "Parameters" | "Description" | "Execution Status" | "Running" | "Success" | "Failed" | "Pending" | "To Be Executed" | "Details" | "Node" | "Node Config" | "Node Output" | "Basic Info" | "Flow Template" | "Node Name" | "Step Name" | "Execution Plan" | "Optional" | "Failure Handler" | "Timeout Control" | "Yes" | "No" | "Input Params" | "Output Params" | "Param Name" | "Param Value" | "Name" | "Structured Output" | "Manual Skip" | "No Data" | "Call MCP:" | "More" | "Algorithm Plan Review Ticket" | "Reviewing" | "Abandoned" | "Approved" | "Cancelled" | "Expired" | "Rejected" | "Revoked" | "Ticket No." | "Submitted At" | "Current Handler" | "None" | "View Ticket Detail" | "Copy Ticket" | "Copy Ticket Link" | "Cancel Approval" | "There are {count} pending approval tickets in the current conversation. To continue, cancel approval first." | "Unsupported interrupt message" | "Searching" | "Search Completed" | "Upload File" | "Requesting..." | "Cancel satisfied" | "Cancel dissatisfied" | "Confirm delete this answer?" | "This operation cannot be undone. Please proceed with caution!" | "Preview" | "Zoom Out" | "Zoom In" | "Rotate" | "Download" | "Sorry, image loading failed. Please try reloading." | "Reset" | "Reload" | "W" | "H" | "Upload Image" | "Search keyword" | "Select date" | "Locate in Chat" | "Select All" | "Confirm" | "Upload Image, up to 3 images supported, max 2.4MB each" | "Hello, I am BlueKing AI Bot" | "Clear Search" | "Search Result is Empty" | "Please choose to continue" | "Continue" | "Received: " | "Done" | "Skip" | "Single" | "Multiple" | "Others" | "Answers" | "Replied" | "Please enter..." | "发送" | "停止" | "问问小鲸" | "复制" | "分享" | "点赞" | "不满意" | "删除" | "引用" | "重新生成" | "重新生成将清空下文内容" | "提交" | "取消" | "预览内容" | "跳转详情" | "调用工具:" | "调用中" | "调用成功" | "调用失败" | "说出您的想法" | "什么原因让你满意?" | "什么原因让你不满意?" | "返回内容" | "编辑" | "深度思考" | "图片加载中..." | "图片加载失败" | "思考中" | "已思考完成" | "思考失败" | "复制成功" | "复制失败" | "返回底部" | "停止生成" | "正在停止" | "耗时" | "参数" | "描述" | "执行情况" | "执行中" | "成功" | "失败" | "挂起" | "待执行" | "详情" | "节点" | "节点配置" | "节点输出" | "基础信息" | "流程模板" | "节点名称" | "步骤名称" | "执行方案" | "是否可选" | "失败处理" | "超时控制" | "是" | "否" | "输入参数" | "输出参数" | "参数名" | "参数值" | "名称" | "变量说明" | "结构化输出" | "手动跳过" | "暂无数据" | "调用 MCP:" | "更多" | "算法方案评审单" | "评审中" | "已废弃" | "已批准" | "已通过" | "已取消" | "已过期" | "已拒绝" | "已撤销" | "单据编号" | "提交时间" | "当前处理人" | "无" | "查看单据详情" | "复制单据" | "复制单据链接" | "取消审批" | "当前会话有 {count} 个待审批单,如需继续,请先取消审批" | "暂不支持的中断消息" | "检索中" | "检索完成" | "上传文件" | "请求中..." | "取消满意" | "取消不满意" | "确认删除该回答?" | "删除操作无法撤回,请谨慎操作!" | "预览" | "缩小" | "放大" | "旋转" | "下载" | "抱歉,图片加载失败,可尝试重新加载" | "重置" | "重新加载" | "宽" | "高" | "上传图片" | "搜索 关键字" | "选择日期" | "在对话中定位" | "全选" | "确定" | "上传图片, 最多支持上传 3 个, 最大支持 2.4MB" | "你好,我是小鲸" | "清空搜索" | "搜索结果为空" | "请选择以继续" | "继续" | "收到信息:" | "待审批" | "已审批" | "完成" | "跳过" | "单选" | "多选" | "其他" | "回答内容" | "已回复" | "请输入...";
136
+ export declare const t: (key: keyof typeof lang) => "Send" | "Stop" | "Ask AI" | "Copy" | "Share" | "Like" | "Unsatisfied" | "Delete" | "Quote" | "Regenerate" | "Regenerating will clear the content below" | "Submit" | "Cancel" | "Preview Content" | "Jump to Detail" | "Call Tool:" | "Calling..." | "Call Success" | "Call Failed" | "Tell us your thoughts" | "What makes you satisfied?" | "What makes you dissatisfied?" | "Return Content" | "Edit" | "Deep Thinking" | "Loading image..." | "Failed to load image" | "Thinking..." | "Thinking Completed" | "Thinking Failed" | "Copy Success" | "Copy Failed" | "Return to bottom" | "Stop generating" | "Stopping" | "Duration" | "Parameters" | "Description" | "Execution Status" | "Running" | "Success" | "Failed" | "Pending" | "To Be Executed" | "Details" | "Retry" | "Node" | "Node Config" | "Node Output" | "Basic Info" | "Flow Template" | "Node Name" | "Step Name" | "Execution Plan" | "Optional" | "Failure Handler" | "Timeout Control" | "Yes" | "No" | "Input Params" | "Output Params" | "Param Name" | "Param Value" | "Name" | "Structured Output" | "Manual Skip" | "No Data" | "Call MCP:" | "More" | "Algorithm Plan Review Ticket" | "Reviewing" | "Abandoned" | "Approved" | "Cancelled" | "Expired" | "Rejected" | "Revoked" | "Ticket No." | "Submitted At" | "Current Handler" | "None" | "View Ticket Detail" | "Copy Ticket" | "Copy Ticket Link" | "Cancel Approval" | "There are {count} pending approval tickets in the current conversation. To continue, cancel approval first." | "Unsupported interrupt message" | "Searching" | "Search Completed" | "Upload File" | "Requesting..." | "Cancel satisfied" | "Cancel dissatisfied" | "Confirm delete this answer?" | "This operation cannot be undone. Please proceed with caution!" | "Preview" | "Zoom Out" | "Zoom In" | "Rotate" | "Download" | "Sorry, image loading failed. Please try reloading." | "Reset" | "Reload" | "W" | "H" | "Upload Image" | "Search keyword" | "Select date" | "Locate in Chat" | "Select All" | "Confirm" | "Upload Image, up to 3 images supported, max 2.4MB each" | "Hello, I am BlueKing AI Bot" | "Clear Search" | "Search Result is Empty" | "Valid Evidence" | "Full Screen" | "Exit Full Screen" | "Please choose to continue" | "Continue" | "Received: " | "Done" | "Skip" | "Single" | "Multiple" | "Others" | "Answers" | "Replied" | "Please enter..." | "发送" | "停止" | "问问小鲸" | "复制" | "分享" | "点赞" | "不满意" | "删除" | "引用" | "重新生成" | "重新生成将清空下文内容" | "提交" | "取消" | "预览内容" | "跳转详情" | "调用工具:" | "调用中" | "调用成功" | "调用失败" | "说出您的想法" | "什么原因让你满意?" | "什么原因让你不满意?" | "返回内容" | "编辑" | "深度思考" | "图片加载中..." | "图片加载失败" | "思考中" | "已思考完成" | "思考失败" | "复制成功" | "复制失败" | "返回底部" | "停止生成" | "正在停止" | "耗时" | "参数" | "描述" | "执行情况" | "执行中" | "成功" | "失败" | "挂起" | "待执行" | "详情" | "重试" | "节点" | "节点配置" | "节点输出" | "基础信息" | "流程模板" | "节点名称" | "步骤名称" | "执行方案" | "是否可选" | "失败处理" | "超时控制" | "是" | "否" | "输入参数" | "输出参数" | "参数名" | "参数值" | "名称" | "变量说明" | "结构化输出" | "手动跳过" | "暂无数据" | "调用 MCP:" | "更多" | "算法方案评审单" | "评审中" | "已废弃" | "已批准" | "已通过" | "已取消" | "已过期" | "已拒绝" | "已撤销" | "单据编号" | "提交时间" | "当前处理人" | "无" | "查看单据详情" | "复制单据" | "复制单据链接" | "取消审批" | "当前会话有 {count} 个待审批单,如需继续,请先取消审批" | "暂不支持的中断消息" | "检索中" | "检索完成" | "上传文件" | "请求中..." | "取消满意" | "取消不满意" | "确认删除该回答?" | "删除操作无法撤回,请谨慎操作!" | "预览" | "缩小" | "放大" | "旋转" | "下载" | "抱歉,图片加载失败,可尝试重新加载" | "重置" | "重新加载" | "宽" | "高" | "上传图片" | "搜索 关键字" | "选择日期" | "在对话中定位" | "全选" | "确定" | "上传图片, 最多支持上传 3 个, 最大支持 2.4MB" | "你好,我是小鲸" | "清空搜索" | "搜索结果为空" | "有效证据" | "全屏" | "退出全屏" | "请选择以继续" | "继续" | "收到信息:" | "待审批" | "已审批" | "完成" | "跳过" | "单选" | "多选" | "其他" | "回答内容" | "已回复" | "请输入...";
@@ -23,6 +23,8 @@
23
23
 
24
24
  组件会将父级传入消息上的 **`uid`** 以 **`message-uid`** 形式透传给各活动子组件(`FlowAgentContent` / `KnowledgeRagContent` / `ReferenceDocContent`),用于侧栏自定义 Tab、`addCustomTab` 的 `data.messageUid` 与主对话区「在对话中定位」联动(详见 [ChatContainer](/components/setup/chat-container))。
25
25
 
26
+ `onInterruptResume` 仅由 `FlowAgentContent` 消费(失败节点「重试 / 跳过」),由 `MessageRender` 从 `ChatContainer` 透传;知识检索、引用文档等活动子组件忽略该 prop。
27
+
26
28
  ## 三种工作模式
27
29
 
28
30
  组件根据 `activityType` 的值决定渲染模式:
@@ -161,11 +163,15 @@
161
163
  ### 核心交互
162
164
 
163
165
  - **标题栏**:显示「执行情况」+ 所有任务聚合后的各状态计数(执行中 / 成功 / 失败 / 挂起),颜色区分
164
- - **任务组**:逐个展示任务行,带状态图标和总耗时
165
- - **节点列表**:每个节点显示状态圆点、名称和耗时;hover 时出现「详情」按钮
166
- - **节点详情**:点击「详情」按钮会通过 `useCustomTabConsumer` `ChatContainer` 侧边栏新增自定义 Tab,展示节点配置(基础信息、输入参数、输出参数)
166
+ - **任务组**:逐个展示任务行,带状态图标、总耗时;点击箭头图标可折叠/展开节点列表
167
+ - **有效证据**:`task.has_confidence === true` 时,任务行右侧展示「有效证据」按钮,点击后在侧栏打开置信度/证据详情 Tab(`props.has_confidence: true`)
168
+ - **默认激活**:当 `MessageContainer` 已注入滚动上下文(`useContainerScrollProvider`,供 `FlowAgentContent` `useContainerScrollConsumer` 读取)时,组件挂载后若存在 `task.is_active === true` 且 `task.has_confidence === true` 的任务,会自动在侧栏打开该任务的「有效证据」Tab;无滚动 Provider(例如独立演示)时不做自动打开。用户手动切换 Tab 后不再沿用 `is_active` 默认高亮
169
+ - **选中态**:当前侧栏 Tab 与任务行 / 节点行联动高亮(`is-selected`);任务 Tab 与「有效证据」Tab 均视为该任务的选中态
170
+ - **节点列表**:每个节点显示状态圆点、名称和耗时;hover 时出现行尾操作按钮组
171
+ - **失败节点操作**:失败且 `retryable` / `skippable` 为 `true` 的节点,hover 时额外展示「重试」「跳过」按钮,点击后通过 `onInterruptResume` 回传 Agent(不传 `interrupt`)
172
+ - **节点详情**:点击「详情」会通过 `useCustomTabConsumer` 在 `ChatContainer` 侧边栏新增自定义 Tab,展示节点配置(基础信息、输入参数、输出参数)
167
173
 
168
- > `FlowAgentContent` 会读取 `ChatContainer` 注入的 `renderMode`。当 `renderMode === RenderMode.Share` 时,节点列表仅展示状态和名称,不展示节点耗时与「详情」按钮,避免分享预览中出现可交互的节点详情入口。独立使用 `ActivityMessage` 且没有上层 Provider 时,默认按 `Chat` 模式渲染。
174
+ > `FlowAgentContent` 会读取 `ChatContainer` 注入的 `renderMode`。当 `renderMode === RenderMode.Share` 时,任务行不展示总耗时与「有效证据」,节点列表不展示耗时与「详情」按钮,避免分享预览中出现可交互入口。独立使用 `ActivityMessage` 且没有上层 Provider 时,默认按 `Chat` 模式渲染。
169
175
 
170
176
  ### 内部渲染结构
171
177
 
@@ -177,16 +183,19 @@ FlowAgentContent(activityType = 'flow_agent')
177
183
  │ └── 执行情况:执行中 N / 成功 N / 失败 N / 挂起 N
178
184
  └── #default
179
185
  └── TaskGroup × N
180
- ├── TaskHeader(可折叠/展开)
186
+ ├── TaskHeader(is-selected / has-confidence;箭头点击折叠)
181
187
  │ ├── 状态图标(running=Loading / success / failed / suspended)
182
188
  │ ├── task_name(HighlightKeyword 支持搜索高亮)
183
- │ └── 总耗时
189
+ │ └── trailing:总耗时 + 「有效证据」(has_confidence 时)
184
190
  └── NodeList
185
- └── NodeItem × N
191
+ └── NodeItem × N(is-selected 与侧栏 Tab 联动)
186
192
  ├── 状态圆点(颜色对应状态)
187
193
  ├── node.name(HighlightKeyword 支持搜索高亮)
188
194
  ├── node.elapsed_time
189
- └── 详情按钮(hover 显示)→ 打开自定义 Tab
195
+ └── 行尾操作按钮组(hover 显示)
196
+ ├── 重试(失败 + retryable)→ onInterruptResume
197
+ ├── 跳过(失败 + skippable)→ onInterruptResume
198
+ └── 详情 → 打开节点详情 Tab
190
199
  ```
191
200
 
192
201
  ### 用法示例
@@ -299,9 +308,19 @@ const messages = [
299
308
  | `suspended` | SUSPENDED | #F59500 |
300
309
  | `pending` | PENDING | #DCDEE5 |
301
310
 
311
+ ### 侧栏 Tab 命名规则
312
+
313
+ | 场景 | `tab.name` 格式 |
314
+ | ---------- | ---------------------------------------- |
315
+ | 任务详情 | `{task_id}` |
316
+ | 有效证据 | `{task_id}`(与任务 Tab 同名,label 为「有效证据」) |
317
+ | 节点详情 | `{task_id}\|{node.id}\|{node.name}` |
318
+
319
+ `CustomBkFlowTabData` 支持 `has_confidence?: boolean`,用于侧栏详情组件区分证据视图与节点视图。应用层可通过 `ChatContainer` 的 `getSideRenderComponent` 覆盖渲染组件。
320
+
302
321
  ### 节点详情 Tab
303
322
 
304
- 点击节点的「详情」按钮,内部调用 `useCustomTabConsumer().addCustomTab()` 在 `ChatContainer` 侧边栏新开一个 Tab,渲染 `FlowAgentNodeDetail` 组件,该组件提供:
323
+ 点击节点的「详情」按钮,内部调用 `useCustomTabConsumer().addCustomTab()` 在 `ChatContainer` 侧边栏新开一个 Tab,渲染 `BkFlowNodeDetail`(或 `getSideRenderComponent` 返回的自定义组件),该组件提供:
305
324
 
306
325
  - **节点配置** Tab:基础信息表单(流程模板、节点名称、步骤名称、失败处理、超时控制)+ 输入参数表 + 输出参数表
307
326
  - **节点输出** Tab:结构化输出参数表
@@ -315,6 +334,8 @@ import { MessageContentType, type BkFlowMessageContent, type BkFlowNode, type Bk
315
334
  type BkFlowMessageContent = BkFlowTask[];
316
335
 
317
336
  type BkFlowTask = {
337
+ has_confidence?: boolean; // 是否展示「有效证据」入口;与 is_active 同时为 true 且在消息容器滚动上下文中时,挂载后自动打开「有效证据」侧栏 Tab
338
+ is_active?: boolean; // 是否默认激活;需配合 has_confidence 且存在滚动 Provider 才会自动打开侧栏「有效证据」Tab
318
339
  nodes: Record<string, BkFlowNode>;
319
340
  statistics: { state_counts: Record<string, number>; total: number };
320
341
  task_id: number;
@@ -330,7 +351,9 @@ type BkFlowNode = {
330
351
  loop: number;
331
352
  name: string;
332
353
  retry: number;
354
+ retryable?: boolean;
333
355
  skip: boolean;
356
+ skippable?: boolean;
334
357
  start_time: string;
335
358
  state: string;
336
359
  type: string;
@@ -394,6 +417,7 @@ enum MessageContentType {
394
417
  | status | `MessageStatus` | - | 消息状态;在 `knowledge_rag` 模式下影响标题和图标,在 `flow_agent` 模式下影响标题 Loading 状态 |
395
418
  | id | `string \| number` | - | 消息 ID |
396
419
  | messageId | `string \| number` | - | 消息唯一标识 |
420
+ | onInterruptResume | `OnInterruptResume` | - | 仅 `flow_agent` 子组件消费;失败节点「重试 / 跳过」回调,由 `MessageRender` 透传 |
397
421
 
398
422
  ### v-model
399
423
 
@@ -26,12 +26,13 @@
26
26
 
27
27
  ## 核心能力
28
28
 
29
- - **分栏布局**:基于 `ResizeLayout` 的可拖拽分栏;**侧栏是否展示 Tab / 执行摘要、以及分栏是否进入折叠样式(`ai-is-collapse`)以 `executionGroups` 为准**(由 `useMessageGroup` 从消息中过滤出工具调用与 FlowAgent 执行记录),**不以原始 `messages.length` 判断**。因此仅有普通对话、尚无执行类消息时,侧栏内容与「执行情况」区域可能为空,布局上与无执行数据时一致
29
+ - **分栏布局**:基于 `ResizeLayout` 的可拖拽分栏;**侧栏是否展示 Tab / 执行摘要、以及分栏是否进入折叠样式(`ai-is-collapse`)以 `executionGroups` 与执行情况搜索关键词 `keyword` 共同决定**(`executionGroups` 由 `useMessageGroup` 从消息中过滤工具调用与 FlowAgent 记录;`keyword` 来自 `ExecutionSummary` 的 `@update-keyword`)。当 `executionGroups` 为空且未输入搜索词时,侧栏 Tab 与执行摘要不展示;**用户正在搜索执行情况时(`keyword` 非空),即使暂无执行类消息也会保留侧栏**,避免搜索态被折叠
30
30
  - **消息分组**:内置 `useMessageGroup` 自动处理消息分组、Tool 合并、Loading 注入
31
31
  - **输入区状态推导**:传给 `MessageContainer` 与 `ChatInput` 的 `messageStatus` 为内部计算值 `inputStatus`:当分组中存在 id 为 `LOADING_MESSAGE_ID`(`'__loading__'`,由 `useMessageGroup` 注入的占位 Loading 消息)时,对内使用 `MessageStatus.Fetching`;否则使用外部传入的 `messageStatus`。用于在「已发用户消息、尚未流式」阶段与流式中一致地展示停止能力,并避免输入区重复发送
32
32
  - **待审批发送阻塞**:当消息中存在 `AIDevToolApproval` 且状态为 `pending` / `draft` 的中断项时,`useMessageGroup` 会返回待审批提示,容器在输入区上方展示提示并通过 `ChatInput.sendDisabledTip` 禁止继续发送
33
33
  - **用户问题中断**:当消息中存在待回答 `UserQuestion` 中断时,容器会在输入区上方挂载 `UserQuestionCard`;若配置了 `onInterruptResume`,用户也可直接在输入框输入自由文本并作为 Others 回答 resume
34
34
  - **执行摘要**:侧边栏展示工具调用 / FlowAgent 执行记录,支持关键词搜索和对话定位
35
+ - **侧栏全屏**:Tab 栏右侧提供全屏/退出全屏按钮,基于 `useFullScreen` 将侧栏区域(`.ai-full-screen-wrapper`)以浏览器原生全屏展示;全屏时 Tippy 的 `appendTo` 自动切换为全屏容器,避免 tooltip 被遮挡
35
36
  - **自定义 Tab**:通过 `useCustomTabProvider` 支持动态添加自定义 Tab(如节点详情)
36
37
  - **分享模式**:内置消息多选分享流程,选中用户消息后确认分享
37
38
  - **渲染模式注入**:`renderMode` 会通过内部 Provider 下传给后代内容组件;例如 FlowAgent 节点在 `Share` 模式下隐藏耗时和「详情」入口
@@ -44,12 +45,14 @@ ai-chat-container
44
45
  ├── Loading(chatLoading 时)
45
46
  └── ResizeLayout
46
47
  ├── aside(侧边栏)
47
- │ ├── Tab 标签页
48
- │ │ ├── 执行情况(默认 Tab)
49
- │ │ └── 自定义 Tab × N(可关闭)
50
- │ ├── ExecutionSummary(执行情况 Tab 内容)
51
- ├── 自定义 Tab 组件(通过 component :is 渲染,可向子组件注入 #locateButton
52
- └── collapse-button(折叠按钮)
48
+ │ ├── .ai-full-screen-wrapper(全屏目标容器,ref=fullScreenRef)
49
+ │ │ ├── Tab 标签页
50
+ │ │ │ ├── 执行情况(默认 Tab
51
+ │ │ ├── 自定义 Tab × N(可关闭;标签可由 `getSideTabRenderComponent` 自定义)
52
+ │ │ └── #setting 插槽 全屏/退出全屏 ToolBtn(FullScreenIcon / UnFullScreenIcon
53
+ │ ├── ExecutionSummary(执行情况 Tab 内容)
54
+ │ │ └── 自定义 Tab 组件(`getSideRenderComponent` 优先,否则 `data.component`;可向子组件注入 #locateButton)
55
+ │ └── collapse-button(折叠按钮,CollapsedIcon;折叠时图标旋转,hover 高亮 #3a84ff)
53
56
  └── main(主内容区)
54
57
  ├── MessageContainer(有消息时)
55
58
  │ ├── #group 插槽(可选,自定义消息组内容)
@@ -118,9 +121,9 @@ ai-chat-container
118
121
 
119
122
  侧边栏默认包含「执行情况」Tab,展示所有工具调用和 FlowAgent 类型的 Activity 消息。支持关键词搜索过滤和点击定位到对话中的消息位置。
120
123
 
121
- **展示条件**:当 `executionGroups` 为空时,不渲染侧栏 Tab 与 `ExecutionSummary`(折叠按钮亦隐藏);主区域仍可正常展示 `messages` 中的对话内容。`renderMode === Share` 时侧栏隐藏,且分栏会应用与折叠一致的样式。
124
+ **展示条件**:当 `executionGroups` 为空且 `keyword` 为空时,不渲染侧栏 Tab 与 `ExecutionSummary`(折叠按钮亦隐藏);主区域仍可正常展示 `messages` 中的对话内容。用户在执行情况中输入搜索词后,侧栏会保持展示以显示「搜索结果为空」等状态。`renderMode === Share` 时侧栏隐藏,且分栏会应用与折叠一致的样式。
122
125
 
123
- **自定义 Tab 联动**:当 `executionGroups` 变为空(例如会话清空或仅剩无执行类消息)时,容器会**自动重置自定义 Tab**(`resetCustomTab`),避免残留节点详情等 Tab
126
+ **自定义 Tab 联动**:当 `executionGroups` 变为空且搜索词已清空时,容器会**自动重置自定义 Tab**(`resetCustomTab`),避免残留节点详情等 Tab;若用户仍在搜索(`keyword` 非空),不会触发重置。
124
127
 
125
128
  ```vue
126
129
  <template>
@@ -147,20 +150,72 @@ ai-chat-container
147
150
 
148
151
  侧边栏放置方向通过 `placement` 控制:
149
152
 
150
- | `placement` | 侧边栏位置 | 折叠按钮位置 |
151
- | ----------- | ------------ | ------------ |
152
- | `left` | 左侧(默认) | 主区域左边缘 |
153
- | `right` | 右侧 | 主区域右边缘 |
153
+ | `placement` | 侧边栏位置 | 折叠按钮位置 | 折叠图标旋转 |
154
+ | ----------- | ------------ | ------------ | ------------ |
155
+ | `left` | 左侧(默认) | 主区域左边缘 | 折叠时旋转 180° |
156
+ | `right` | 右侧 | 主区域右边缘 | 默认旋转 180°,折叠时恢复 0° |
157
+
158
+ > 折叠按钮仅展示 `CollapsedIcon`(不再显示「执行情况」文案),通过图标旋转方向指示展开/折叠状态。
154
159
 
155
160
  **placement 对比**(左右两种布局)
156
161
 
162
+ ## 侧栏全屏
163
+
164
+ 当侧栏 Tab 区域可见时,Tab 栏右侧(`#setting` 插槽)内置全屏切换按钮:
165
+
166
+ - 点击 **全屏** 图标:调用 `useFullScreen(fullScreenRef).enter()`,将 `.ai-full-screen-wrapper` 进入浏览器原生全屏
167
+ - 点击 **退出全屏** 图标:调用 `exit()` 退出;用户按 ESC 退出时 `isFullScreen` 也会自动同步
168
+ - 全屏状态下,侧栏内 `v-overflow-tips` 的 `appendTo` 会指向全屏容器,避免 tooltip 挂载到 `document.body` 后被全屏层遮挡
169
+
170
+ 该能力由内部 `useFullScreen` composable 提供,详见 [useFullScreen](../../composables/use-full-screen.md)。
171
+
157
172
  ## 自定义 Tab
158
173
 
159
- 通过 `ref` 获取组件实例后,使用 `addCustomTab` / `removeCustomTab` 动态管理侧边栏 Tab。若 **`executionGroups` 变为空**,容器会清空自定义 Tab 状态(与侧栏执行数据联动,见上文「侧边栏与执行摘要」)。
174
+ 通过 `ref` 获取组件实例后,使用 `addCustomTab` / `removeCustomTab` 动态管理侧边栏 Tab。若 **`executionGroups` 变为空且搜索词已清空**,容器会清空自定义 Tab 状态(与侧栏执行数据联动,见上文「侧边栏与执行摘要」)。
175
+
176
+ ### 侧栏渲染扩展
177
+
178
+ 应用层可通过以下 Props 覆盖默认 Tab 标签与侧栏内容区的渲染逻辑(例如 FlowAgent 节点详情使用业务自定义组件):
179
+
180
+ | Prop | 说明 |
181
+ | ---- | ---- |
182
+ | `getSideTabRenderComponent` | `(h, tab, { removeCustomTab }) => VNode \| undefined`。返回自定义 Tab 标签 VNode;未返回时使用默认图标 + `tab.label` + 关闭按钮 |
183
+ | `getSideRenderComponent` | `(h, props) => VNode \| undefined`。返回侧栏内容区组件 VNode;未返回时使用 `selectedTab.data.component` |
184
+
185
+ 侧栏内容区实现上会以 **`selectedTab.name` 作为外层 `key`**,切换 Tab 时重建子树,避免插槽与局部状态残留;当前 Tab 的 `:is` 由内部 **`computed`** 根据 `getSideRenderComponent(h, selectedTab.data.props)` 与 `data.component` 解析,保证 Tab 切换或 `onCustomTabChange` 异步更新 props 后内容类型与数据一致。
186
+
187
+ ```vue
188
+ <template>
189
+ <ChatContainer
190
+ :get-side-tab-render-component="renderSideTab"
191
+ :get-side-render-component="renderSidePanel"
192
+ ...
193
+ />
194
+ </template>
195
+
196
+ <script setup lang="ts">
197
+ import { h } from 'vue';
198
+ import type { CustomTab } from '@blueking/chat-x';
199
+
200
+ const renderSideTab = (createElement, tab, { removeCustomTab }) => {
201
+ if (tab.name.startsWith('custom-')) {
202
+ return createElement('span', {}, tab.label);
203
+ }
204
+ return undefined; // 走默认 Tab 标签
205
+ };
206
+
207
+ const renderSidePanel = (createElement, props) => {
208
+ if (props?.has_confidence) {
209
+ return createElement(MyConfidencePanel, props);
210
+ }
211
+ return undefined; // 走 tab.data.component
212
+ };
213
+ </script>
214
+ ```
160
215
 
161
216
  ### 自定义 Tab 与「在对话中定位」
162
217
 
163
- `addCustomTab` 的 `data` 可携带 **`messageUid`**(与对应活动消息的 `message.uid` 一致)。`ChatContainer` 在侧栏用 `<component :is>` 渲染自定义 Tab 时,会向子组件提供 **`locateButton` 插槽**:默认渲染「在对话中定位」按钮,点击后调用内部 `handleLocateMessageGroup(messageUid)`,优先滚动到主区域 `document.getElementById(messageUid)`;若不存在该节点,则在当前 `messageGroups` 中查找包含 `message.uid === messageUid` 的消息组,并滚动到该组的容器(`MessageGroup.uid` 作为组级 `id`)。
218
+ `addCustomTab` 的 `data` 可携带 **`messageUid`**(与对应活动消息的 `message.uid` 一致)。`ChatContainer` 在侧栏用 `<component :is="sideRenderComponent">`(内部计算属性,见上文「侧栏渲染扩展」)渲染自定义 Tab 时,会向子组件提供 **`locateButton` 插槽**:默认渲染「在对话中定位」按钮,点击后调用内部 `handleLocateMessageGroup(messageUid)`,优先滚动到主区域 `document.getElementById(messageUid)`;若不存在该节点,则在当前 `messageGroups` 中查找包含 `message.uid === messageUid` 的消息组,并滚动到该组的容器(`MessageGroup.uid` 作为组级 `id`)。
164
219
 
165
220
  子组件若需展示该按钮,请在模板中声明 `<slot name="locateButton" />`(例如 FlowAgent 节点详情标题栏)。`FlowAgentContent` 等会在打开节点详情 Tab 时将 `messageUid` 写入 `data`,与 `ActivityMessage` 下传给内容区的 `message-uid` 对齐。
166
221
 
@@ -435,9 +490,11 @@ ChatContainer 的 Props 继承自 `ChatInputProps` 和 `MessageContainerProps`
435
490
 
436
491
  | 属性名 | 类型 | 默认值 | 说明 |
437
492
  | ------------------ | ---------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
438
- | chatLoading | `boolean` | — | 整体加载状态,`true` 时显示 Loading 遮罩 |
439
- | commonTippyOptions | `AITippyProps` | — | 通用 Tippy 配置,传入的选项会注入到所有使用 `v-overflow-tips` 的子组件中 |
440
- | openingRemark | `string` | — | 开场白,无消息时显示,支持 Markdown |
493
+ | chatLoading | `boolean` | — | 整体加载状态,`true` 时显示 Loading 遮罩 |
494
+ | commonTippyOptions | `AITippyProps` | — | 通用 Tippy 配置(`appendTo` / `placement` / `zIndex`),传入的选项会注入到所有使用 `v-overflow-tips` 的子组件中 |
495
+ | getSideRenderComponent | `(h, props?) => VNode \| undefined` | — | 自定义侧栏内容区渲染;未返回时使用 `selectedTab.data.component` |
496
+ | getSideTabRenderComponent | `(h, tab, { removeCustomTab }) => VNode \| undefined` | — | 自定义侧栏 Tab 标签渲染;未返回时使用默认图标 + 文案 + 关闭按钮 |
497
+ | openingRemark | `string` | — | 开场白,无消息时显示,支持 Markdown |
441
498
  | placement | `'left' \| 'right'` | `'left'` | 侧边栏位置 |
442
499
  | resizeProps | `{ disabled?: boolean; initialDivide?: number \| string; max?: number; min?: number }` | — | 透传给内部 `ResizeLayout` 的可选配置,与默认 `collapsible: false`、`immediate: true`、`min: 400` 合并;`placement` 始终取自本组件 `placement`;`initialDivide` 可为像素数字或百分比等字符串(与 bkui ResizeLayout 一致) |
443
500
  | onCustomTabChange | `(tab: CustomTab) => Promise<any>` | — | 自定义 Tab 切换回调,返回值作为 Tab 组件 props |
@@ -554,4 +611,6 @@ interface Shortcut {
554
611
  - [ChatInput](/components/input/chat-input) — 输入与快捷指令
555
612
  - [ShortcutRender](/components/input/shortcut-render) — 快捷指令表单
556
613
  - [ExecutionSummary](/components/agent/execution-summary) — 执行摘要侧栏
557
- - [SelectionFooter](/components/input/selection-footer) — 多选操作栏
614
+ - [SelectionFooter](/components/input/selection-footer) — 多选操作栏
615
+ - [ToolBtn](/components/feedback/tool-btn) — 侧栏全屏按钮(自定义插槽)
616
+ - [useFullScreen](/composables/use-full-screen) — 侧栏全屏控制
@@ -154,7 +154,7 @@
154
154
  ```typescript
155
155
  import type { TippyOptions } from 'vue-tippy';
156
156
 
157
- type AITippyProps = Partial<Omit<TippyOptions, 'content' | 'getReferenceClientRect' | 'theme' | 'triggerTarget'>>;
157
+ type AITippyProps = Partial<Pick<TippyOptions, 'appendTo' | 'placement' | 'zIndex'>>;
158
158
  ```
159
159
 
160
160
  ## 关联组件
@@ -29,8 +29,9 @@
29
29
  - **状态聚合统计**:汇总所有任务的 `statistics.state_counts`,在标题栏按「执行中 / 成功 / 失败 / 挂起 / 待执行」分类展示带颜色的计数(超过 99 显示 `99+`)
30
30
  - **两级折叠**:任务整体由 `ActivityLayout` 折叠;每个任务节点列表可单独展开/收起
31
31
  - **耗时格式化**:节点耗时与任务总耗时按 `d/h/m/s` 紧凑展示,小于 1 秒显示 `<1s`
32
- - **详情入口联动**:hover 节点行显示「详情」按钮,点击后通过自定义 Tab 挂载 `FlowAgentNodeDetail`
33
- - **分享态降级**:`RenderMode.Share` 下隐藏耗时与详情入口,仅保留只读的执行状态
32
+ - **节点行尾操作**:hover 失败节点行显示「重试 / 跳过 / 详情」按钮组(间距 12px);成功 / 运行中等非失败节点仅显示「详情」。重试 / 跳过依赖节点 `retryable` / `skippable` 能力位,通过 `onInterruptResume` 回传 Agent
33
+ - **详情入口联动**:「详情」按钮点击后通过自定义 Tab 挂载 `FlowAgentNodeDetail`
34
+ - **分享态降级**:`RenderMode.Share` 下隐藏耗时与行尾操作按钮,仅保留只读的执行状态
34
35
 
35
36
  ## 状态映射
36
37
 
@@ -53,16 +54,20 @@
53
54
  <FlowAgentContent
54
55
  :content="flowContent"
55
56
  :message-uid="messageUid"
57
+ :on-interrupt-resume="handleInterruptResume"
56
58
  :status="status"
57
59
  />
58
60
  </template>
59
61
 
60
62
  <script setup lang="ts">
61
63
  import { FlowAgentContent } from '@blueking/chat-x';
62
- import type { BkFlowMessageContent } from '@blueking/chat-x';
64
+ import type { BkFlowMessageContent, OnInterruptResume } from '@blueking/chat-x';
63
65
 
64
66
  const messageUid = 'flow-msg-1';
65
67
  const status = 'success';
68
+ const handleInterruptResume: OnInterruptResume = async payload => {
69
+ console.log('flow node resume:', payload);
70
+ };
66
71
  const flowContent: BkFlowMessageContent = [
67
72
  {
68
73
  task_id: 100,
@@ -73,19 +78,45 @@
73
78
  nodes: {
74
79
  n1: { id: 'n1', name: '采集主机指标', state: 'FINISHED', elapsed_time: 12, type: 'task', loop: 0, retry: 0, skip: false, start_time: '', finish_time: '' },
75
80
  n2: { id: 'n2', name: '分析异常项', state: 'FINISHED', elapsed_time: 65, type: 'task', loop: 0, retry: 0, skip: false, start_time: '', finish_time: '' },
76
- n3: { id: 'n3', name: '推送告警', state: 'FAILED', elapsed_time: 3, type: 'task', loop: 0, retry: 1, skip: false, start_time: '', finish_time: '' },
81
+ n3: { id: 'n3', name: '推送告警', state: 'FAILED', elapsed_time: 3, type: 'task', loop: 0, retry: 1, skip: false, retryable: true, skippable: true, start_time: '', finish_time: '' },
77
82
  },
78
83
  },
79
84
  ];
80
85
  </script>
81
86
  ```
82
87
 
83
- **渲染效果**(hover 节点行可看到耗时切换为「详情」按钮,点击会向侧栏自定义 Tab 注入节点详情)
88
+ **渲染效果**(hover 失败节点行可看到「重试 / 跳过 / 详情」按钮组;点击「详情」会向侧栏自定义 Tab 注入节点详情)
84
89
 
85
90
  ## 执行中状态
86
91
 
87
92
  `status` 为 `pending` / `streaming` 时,标题栏图标显示为加载动画;运行中的节点显示旋转 Loading,待执行 / 挂起节点显示对应颜色的状态点。
88
93
 
94
+ ## 失败节点重试 / 跳过
95
+
96
+ 失败节点(`convergedState === 'failed'`)且具备对应能力位时,hover 行尾展示「重试」或「跳过」按钮。点击后调用 `onInterruptResume`,**不传** `interrupt` 参数:
97
+
98
+ ```typescript
99
+ // 重试
100
+ onInterruptResume?.({
101
+ operation: InterruptResumeOperation.FlowNodeRetry,
102
+ payload: { node_id: node.id, task_id: task.task_id },
103
+ });
104
+
105
+ // 跳过
106
+ onInterruptResume?.({
107
+ operation: InterruptResumeOperation.FlowNodeSkip,
108
+ payload: { node_id: node.id, task_id: task.task_id },
109
+ });
110
+ ```
111
+
112
+ | 按钮 | 显隐条件 | `operation` |
113
+ | ---- | ------------------------------------- | ---------------------- |
114
+ | 重试 | 失败态且 `node.retryable === true` | `flow_node_retry` |
115
+ | 跳过 | 失败态且 `node.skippable === true` | `flow_node_skip` |
116
+ | 详情 | 始终展示(Share 模式除外) | —(打开侧栏 Tab,不走 resume) |
117
+
118
+ 行尾操作由内部 composable [`useFlowNodeActions`](/composables/use-flow-node-actions) 聚合为声明式列表,组件层仅遍历渲染。
119
+
89
120
  ## 节点详情联动
90
121
 
91
122
  点击节点「详情」按钮时,组件调用 `addCustomTab` 注入一个以 `${task_id}|${node.id}|${node.name}` 为唯一 `name` 的 Tab,渲染组件为 `FlowAgentNodeDetail`:
@@ -130,7 +161,10 @@ ActivityLayout(activity-type=flow_agent,v-model:collapsed)
130
161
  ├── node-name(HighlightKeyword + 溢出提示)
131
162
  └── node-trailing(非 Share 态)
132
163
  ├── node-time(节点耗时,hover 时隐藏)
133
- └── node-detail-btn(hover 时显示,点击挂载详情 Tab
164
+ └── node-actions(hover 时显示按钮组,间距 12px
165
+ ├── node-action-btn「重试」(失败 + retryable)
166
+ ├── node-action-btn「跳过」(失败 + skippable)
167
+ └── node-action-btn「详情」(始终,点击挂载详情 Tab)
134
168
  ```
135
169
 
136
170
  ## API
@@ -141,6 +175,7 @@ ActivityLayout(activity-type=flow_agent,v-model:collapsed)
141
175
  | ---------- | ------------------------ | ---- | ----------- | -------------------------------------------------------------------------- |
142
176
  | content | `BkFlowMessageContent` | 否 | `[{}]` | 任务数组;传入单个 `BkFlowTask` 时自动包装为单元素数组 |
143
177
  | messageUid | `string` | 否 | — | 所属消息唯一标识,注入到节点详情 Tab 的 `data.messageUid`,用于异步回填数据 |
178
+ | onInterruptResume | `OnInterruptResume` | 否 | — | 节点「重试 / 跳过」与第三方审批取消复用同一回调,按 `payload.operation` 分流;流程节点操作时不传 `interrupt` |
144
179
  | status | `MessageStatus` | 否 | — | 消息状态;`pending` / `streaming` 时标题栏显示加载动画 |
145
180
 
146
181
  ### Emits
@@ -181,7 +216,9 @@ interface BkFlowNode {
181
216
  type: string;
182
217
  loop: number;
183
218
  retry: number;
219
+ retryable?: boolean; // 是否可重试(失败节点「重试」按钮显隐)
184
220
  skip: boolean;
221
+ skippable?: boolean; // 是否可跳过(失败节点「跳过」按钮显隐)
185
222
  start_time: string;
186
223
  finish_time: string;
187
224
  }
@@ -202,11 +239,14 @@ interface BkFlowNode {
202
239
  3. **任务总耗时为节点累加**:`task-time` 由各节点 `elapsed_time` 求和得到,并非任务级独立字段。
203
240
  4. **`task_outputs` 暂不渲染**:模板中任务输出展示区块已注释,传入也不会显示。
204
241
  5. **未知状态兜底为 `running`**:`getConvergedState` 对未识别的原始状态统一归为运行中。
205
- 6. **Share 模式降级**:`RenderMode.Share` 下不渲染节点耗时与「详情」按钮,仅保留只读执行状态。
242
+ 6. **Share 模式降级**:`RenderMode.Share` 下不渲染节点耗时与行尾操作按钮(重试 / 跳过 / 详情),仅保留只读执行状态。
243
+ 7. **`onInterruptResume` 透传链路**:`MessageRender` → `ActivityMessage` → `FlowAgentContent`;未传入时重试 / 跳过按钮仍展示但点击无回调。
206
244
 
207
245
  ## 关联组件
208
246
 
209
247
  - [FlowAgentNodeDetail](/components/agent/flow-agent-node-detail) — 节点详情面板
210
248
  - [ActivityLayout](/components/helper/activity-layout) — 可折叠活动容器
211
249
  - [ChatContainer](/components/setup/chat-container) — 侧栏自定义 Tab 挂载场景
250
+ - [useFlowNodeActions](/composables/use-flow-node-actions) — 节点行尾操作聚合 composable
251
+ - [中断类型 Interrupt](/types/interrupt) — `InterruptResumeOperation`、`FlowNodeResume`、`OnInterruptResume`
212
252
  - [使用建议] 优先通过上层组合组件(`MessageRender`)使用;直接使用前请确认 `content` 数据结构来自对应类型定义。
@@ -103,8 +103,8 @@ content.outcome.type === 'success'
103
103
  };
104
104
 
105
105
  const handleInterruptResume = async (payload, interrupt) => {
106
- // ToolApprovalCard 点击「取消审批」时,payload 为 { action: 'cancel' }
107
- console.log(payload, interrupt.id);
106
+ // ToolApprovalCard 点击「取消审批」时,payload 为 ToolApprovalResume
107
+ console.log(payload, interrupt?.id);
108
108
  };
109
109
  </script>
110
110
  ```
@@ -21,7 +21,24 @@ AG-UI [Interrupts](https://docs.ag-ui.com/drafts/interrupts) 协议相关类型
21
21
  中断链路分为两段:
22
22
 
23
23
  1. Agent 返回 `RUN_FINISHED { outcome: { type: 'interrupt', interrupts } }`,前端渲染等待用户处理的 UI。
24
- 2. 用户操作后调用 `onInterruptResume(payload, interrupt)`,业务侧将 `payload` 作为 `RunAgentInput.resume` 回传给 Agent。
24
+ 2. 用户操作后调用 `onInterruptResume(payload, interrupt?)`,业务侧按 `payload.operation` 分支处理,并将 `payload` 作为 `RunAgentInput.resume` 回传给 Agent。
25
+
26
+ ## InterruptResumeOperation
27
+
28
+ 统一标识用户在「中断消息 / 活动消息」上触发的、需回传 Agent 处理的动作:
29
+
30
+ ```typescript
31
+ enum InterruptResumeOperation {
32
+ /** 主动取消第三方工具审批 */
33
+ ApprovalCancel = 'approval_cancel',
34
+ /** 重试失败的流程节点(bkflow) */
35
+ FlowNodeRetry = 'flow_node_retry',
36
+ /** 跳过失败的流程节点(bkflow) */
37
+ FlowNodeSkip = 'flow_node_skip',
38
+ }
39
+ ```
40
+
41
+ 业务侧通过 `onInterruptResume` 回调的 `payload.operation` 字段进行分支处理;新增操作类型只需扩展此枚举,回调契约与透传链路保持不变。
25
42
 
26
43
  ## RunFinishedOutcome
27
44
 
@@ -118,6 +135,41 @@ type Interrupt =
118
135
  | BaseInterrupt<InterruptReason, Record<string, any>>;
119
136
  ```
120
137
 
138
+ ## InterruptResume 联合类型
139
+
140
+ ```typescript
141
+ type InterruptResume = FlowNodeResume | ToolApprovalResume | UserQuestionResume;
142
+ ```
143
+
144
+ | 类型 | `operation` / `reason` | 说明 |
145
+ | -------------------- | ----------------------------------- | ------------------------------------------------------------ |
146
+ | `ToolApprovalResume` | `InterruptResumeOperation.ApprovalCancel` | 第三方工具审批取消 |
147
+ | `FlowNodeResume` | `flow_node_retry` / `flow_node_skip` | FlowAgent 失败节点重试 / 跳过;**无**对应 `Interrupt` 项 |
148
+ | `UserQuestionResume` | `InterruptReason.UserQuestion`(`reason` 字段) | 用户回答问题 |
149
+
150
+ ### ToolApprovalResume
151
+
152
+ ```typescript
153
+ type ToolApprovalResume = {
154
+ operation: InterruptResumeOperation.ApprovalCancel;
155
+ payload: { interrupt_id: number | string };
156
+ };
157
+ ```
158
+
159
+ ### FlowNodeResume
160
+
161
+ 流程节点不属于 interrupt,节点定位信息(`task_id` / `node_id`)随 `payload` 回传;此时 `onInterruptResume` 的第二个 `interrupt` 参数**不传**。
162
+
163
+ ```typescript
164
+ type FlowNodeResume = {
165
+ operation: InterruptResumeOperation.FlowNodeRetry | InterruptResumeOperation.FlowNodeSkip;
166
+ payload: {
167
+ node_id: string;
168
+ task_id: number;
169
+ };
170
+ };
171
+ ```
172
+
121
173
  ## BaseResume / UserQuestionResume
122
174
 
123
175
  `UserQuestion` 的 resume payload 为单个对象,与 `chat-helper` 的 `IResume` 保持一致:
@@ -193,19 +245,25 @@ type InterruptMessage = BaseMessage<
193
245
 
194
246
  ## OnInterruptResume
195
247
 
196
- 用户完成中断操作后的回调(由 `ChatContainer` / `MessageContainer` / `MessageRender` 透传):
248
+ 用户完成中断操作或 FlowAgent 节点操作后的回调(由 `ChatContainer` / `MessageContainer` / `MessageRender` 透传):
197
249
 
198
250
  ```typescript
199
251
  type OnInterruptResume = (
200
252
  payload: InterruptResume,
201
- interrupt: Interrupt,
253
+ interrupt?: Interrupt,
202
254
  ) => Promise<void> | void;
203
255
  ```
204
256
 
205
- | 参数 | 说明 |
206
- | ----------- | -------------------------------------------------------------------------- |
207
- | `payload` | 用户操作产生的 resume 负载;审批取消为 `{ action: 'cancel' }`,用户问题为 `UserQuestionResume` |
208
- | `interrupt` | 原始中断项,业务侧可读取 `interrupt.id`、`toolCallId`、`metadata` 等上下文 |
257
+ | 参数 | 说明 |
258
+ | ----------- | ---------------------------------------------------------------------------------------------------------- |
259
+ | `payload` | 用户操作产生的 resume 负载;通过 `payload.operation`(或 `UserQuestionResume.reason`)区分动作类型 |
260
+ | `interrupt` | 原始中断项;审批取消、用户问题等中断来源**必传**;FlowAgent 节点重试 / 跳过等非中断来源**不传** |
261
+
262
+ | `payload.operation` | 触发场景 | `interrupt` |
263
+ | -------------------------------- | -------------------------------- | ----------- |
264
+ | `approval_cancel` | `ToolApprovalCard` 取消审批 | 必传 |
265
+ | `flow_node_retry` / `flow_node_skip` | `FlowAgentContent` 失败节点操作 | 不传 |
266
+ | —(`UserQuestionResume`) | `UserQuestionCard` 提交回答 | 必传 |
209
267
 
210
268
  ## 使用示例
211
269
 
@@ -213,6 +271,7 @@ type OnInterruptResume = (
213
271
  import {
214
272
  APPROVAL_STATUS,
215
273
  InterruptReason,
274
+ InterruptResumeOperation,
216
275
  MessageRole,
217
276
  MessageStatus,
218
277
  type InterruptMessage,
@@ -269,7 +328,20 @@ const message: InterruptMessage = {
269
328
  };
270
329
 
271
330
  const handleInterruptResume: OnInterruptResume = async (payload, interrupt) => {
272
- console.log('resume', interrupt.id, payload);
331
+ if ('operation' in payload) {
332
+ switch (payload.operation) {
333
+ case InterruptResumeOperation.ApprovalCancel:
334
+ console.log('取消审批', payload.payload.interrupt_id, interrupt?.id);
335
+ break;
336
+ case InterruptResumeOperation.FlowNodeRetry:
337
+ case InterruptResumeOperation.FlowNodeSkip:
338
+ console.log('流程节点操作', payload.operation, payload.payload);
339
+ break;
340
+ }
341
+ return;
342
+ }
343
+ // UserQuestionResume
344
+ console.log('用户问题 resume', interrupt?.id, payload);
273
345
  };
274
346
  ```
275
347
 
@@ -182,6 +182,7 @@ props.content → completeMarkdownSyntax → md.parse → groupTokens → groupe
182
182
 
183
183
  | 插件 | 语法 | 功能 |
184
184
  | --------------------------- | ------------------- | -------------------- |
185
+ | `markdownItBkInlineStyle` | 见下文「蓝鲸行内样式」 | 安全行内颜色/字号/粗斜体(非 HTML) |
185
186
  | `markdown-it-footnote` | `[^1]` | 脚注 |
186
187
  | `markdown-it-ins` | `++text++` | 下划线 |
187
188
  | `markdown-it-mark` | `==text==` | 高亮 |
@@ -192,8 +193,30 @@ props.content → completeMarkdownSyntax → md.parse → groupTokens → groupe
192
193
  | `markdownItLatex` | `$...$` / `$$...$$` | KaTeX 数学公式 token |
193
194
  | `markdownItContainer` | `::: hljs-left` 等 | 自定义对齐容器(class 与 highlight.js 命名对齐) |
194
195
 
196
+ ### 蓝鲸行内样式(`markdownItBkInlineStyle`)
197
+
198
+ 不开启 `html: true`,由专用语法生成带白名单 `style` 的 `<span class="bk-md-inline-style">`。
199
+
200
+ **语法**:`::bk{` *属性* `}` *正文* `:/bk::`
201
+
202
+ - 属性写在 `{}` 内,使用 `;` 分隔;每项为 `键=值` 或 `键:值`。
203
+ - 正文支持行内 Markdown(如 `**粗体**`)。
204
+ - 结束标记必须为字面量 `:/bk::`,请勿在正文中出现该序列。
205
+
206
+ **支持的键**:`color` / `c`、`background-color`、`font-size`、`bold`、`italic`(详见 `plugins/markdown-bk-inline-style.ts` 内注释)。
207
+
208
+ **示例**:
209
+
210
+ ```markdown
211
+ ::bk{color:#c00;font-size:18px}**重要**:/bk::
212
+ ::bk{background-color:yellow}高亮:/bk::
213
+ ::bk{bold;italic}强调:/bk::
214
+ ```
215
+
195
216
  ### 安全性
196
217
 
218
+ `MarkdownIt` **不**开启 `html: true`,用户无法插入任意 HTML 标签;行内彩色/字号等请使用上文「蓝鲸行内样式」扩展。
219
+
197
220
  `VNodeRenderer` 渲染的 HTML 统一经过 DOMPurify 过滤,并额外允许 KaTeX 所需标签:
198
221
 
199
222
  ```typescript