@blueking/chat-x 0.0.45-beta.7 → 0.0.45-beta.8
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 +1 -1
- package/dist/ag-ui/types/constants.d.ts +16 -0
- package/dist/ag-ui/types/contents.d.ts +2 -0
- package/dist/ag-ui/types/index.d.ts +2 -0
- package/dist/ag-ui/types/interrupt.d.ts +152 -0
- package/dist/ag-ui/types/messages.d.ts +2 -0
- package/dist/ag-ui/types/schema.d.ts +42 -0
- package/dist/components/ai-questions/questions-container.vue.d.ts +3 -0
- package/dist/components/ai-questions/selection-question.vue.d.ts +3 -0
- package/dist/components/chat-content/flow-agent-content/flow-agent-content.vue.d.ts +3 -0
- package/dist/components/chat-content/flow-agent-content/use-flow-agent.d.ts +4 -0
- package/dist/components/chat-content/flow-agent-content/use-flow-node-actions.d.ts +32 -0
- package/dist/components/chat-content/index.d.ts +2 -0
- package/dist/components/chat-input/ai-slash-input/command.d.ts +1 -2
- package/dist/components/chat-input/ai-slash-input/constants.d.ts +0 -4
- package/dist/components/chat-input/chat-input.vue.d.ts +14 -8
- package/dist/components/chat-input/input-attachment/input-attachment.vue.d.ts +1 -0
- package/dist/components/chat-input/input-info-alert.vue.d.ts +6 -0
- package/dist/components/chat-message/activity-message/activity-message.vue.d.ts +4 -1
- package/dist/components/chat-message/assistant-message/assistant-message.vue.d.ts +1 -1
- package/dist/components/chat-message/interrupt-message/index.d.ts +2 -0
- package/dist/components/chat-message/interrupt-message/interrupt-message.vue.d.ts +17 -0
- package/dist/components/chat-message/interrupt-message/tool-approval-card.vue.d.ts +9 -0
- package/dist/components/chat-message/interrupt-message/user-question/index.d.ts +5 -0
- package/dist/components/chat-message/interrupt-message/user-question/use-user-question.d.ts +35 -0
- package/dist/components/chat-message/interrupt-message/user-question/user-question-answered-card.vue.d.ts +25 -0
- package/dist/components/chat-message/interrupt-message/user-question/user-question-card.vue.d.ts +25 -0
- package/dist/components/chat-message/interrupt-message/user-question/user-question-choice.vue.d.ts +13 -0
- package/dist/components/chat-message/interrupt-message/user-question/user-question-option.vue.d.ts +17 -0
- package/dist/components/chat-message/message-container/message-container.vue.d.ts +14 -7
- package/dist/components/chat-message/message-render/message-render.vue.d.ts +4 -0
- package/dist/components/index.d.ts +3 -2
- package/dist/composables/use-global-config.d.ts +3 -0
- package/dist/composables/use-message-group.d.ts +2224 -355
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/interrupt.d.ts +24 -0
- package/dist/index.css +1 -1
- package/dist/index.js +2557 -1937
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +34 -2
- package/dist/mcp/generated/docs/activity-layout.md +138 -0
- package/dist/mcp/generated/docs/activity-message.md +21 -7
- package/dist/mcp/generated/docs/ai-image.md +9 -4
- package/dist/mcp/generated/docs/ai-loading.md +11 -6
- package/dist/mcp/generated/docs/ai-prompt-list.md +42 -0
- package/dist/mcp/generated/docs/ai-selection.md +10 -5
- package/dist/mcp/generated/docs/ai-slash-editor.md +43 -0
- package/dist/mcp/generated/docs/ai-slash-input.md +43 -0
- package/dist/mcp/generated/docs/ai-slash-menu.md +42 -0
- package/dist/mcp/generated/docs/animation-text.md +9 -4
- package/dist/mcp/generated/docs/assistant-message.md +10 -5
- package/dist/mcp/generated/docs/chat-container.md +173 -19
- package/dist/mcp/generated/docs/chat-input.md +78 -10
- package/dist/mcp/generated/docs/cite-content.md +8 -3
- package/dist/mcp/generated/docs/code-content.md +9 -4
- package/dist/mcp/generated/docs/common-error-content.md +10 -5
- package/dist/mcp/generated/docs/constants.md +68 -3
- package/dist/mcp/generated/docs/content-render.md +10 -5
- package/dist/mcp/generated/docs/delete-tool.md +9 -4
- package/dist/mcp/generated/docs/desc-panel.md +9 -4
- package/dist/mcp/generated/docs/detail-section.md +93 -0
- package/dist/mcp/generated/docs/execution-summary.md +10 -5
- package/dist/mcp/generated/docs/file-content.md +9 -4
- package/dist/mcp/generated/docs/file-upload-btn.md +10 -5
- package/dist/mcp/generated/docs/flow-agent-content.md +252 -0
- package/dist/mcp/generated/docs/flow-agent-node-detail.md +240 -0
- package/dist/mcp/generated/docs/highlight-keyword.md +10 -5
- package/dist/mcp/generated/docs/image-content.md +9 -4
- package/dist/mcp/generated/docs/image-preview-group.md +9 -4
- package/dist/mcp/generated/docs/image-preview.md +10 -5
- package/dist/mcp/generated/docs/info-message.md +7 -2
- package/dist/mcp/generated/docs/input-attachment.md +43 -0
- package/dist/mcp/generated/docs/input-info-alert.md +42 -0
- package/dist/mcp/generated/docs/interrupt-message.md +216 -0
- package/dist/mcp/generated/docs/interrupt.md +377 -0
- package/dist/mcp/generated/docs/key-value-content.md +9 -4
- package/dist/mcp/generated/docs/knowledge-rag-content.md +126 -0
- package/dist/mcp/generated/docs/latex-content.md +9 -4
- package/dist/mcp/generated/docs/loading-message.md +11 -6
- package/dist/mcp/generated/docs/markdown-container.md +1 -1
- package/dist/mcp/generated/docs/markdown-content.md +13 -8
- package/dist/mcp/generated/docs/markdown-latex.md +3 -3
- package/dist/mcp/generated/docs/markdown-mermaid.md +3 -3
- package/dist/mcp/generated/docs/mermaid-content.md +9 -4
- package/dist/mcp/generated/docs/message-container.md +74 -16
- package/dist/mcp/generated/docs/message-loading.md +120 -0
- package/dist/mcp/generated/docs/message-render.md +23 -11
- package/dist/mcp/generated/docs/message-tools.md +10 -5
- package/dist/mcp/generated/docs/messages.md +75 -11
- package/dist/mcp/generated/docs/overflow-tips.md +4 -4
- package/dist/mcp/generated/docs/preview-toolbar.md +42 -0
- package/dist/mcp/generated/docs/questions-container.md +88 -0
- package/dist/mcp/generated/docs/reasoning-message.md +10 -5
- package/dist/mcp/generated/docs/reference-content.md +10 -5
- package/dist/mcp/generated/docs/reference-doc-content.md +112 -0
- package/dist/mcp/generated/docs/schema.md +93 -0
- package/dist/mcp/generated/docs/scroll-btn.md +8 -3
- package/dist/mcp/generated/docs/selection-footer.md +9 -4
- package/dist/mcp/generated/docs/selection-question.md +91 -0
- package/dist/mcp/generated/docs/shortcut-btn.md +10 -5
- package/dist/mcp/generated/docs/shortcut-btns.md +10 -5
- package/dist/mcp/generated/docs/shortcut-render.md +10 -5
- package/dist/mcp/generated/docs/simple-table.md +103 -0
- package/dist/mcp/generated/docs/text-content.md +9 -4
- package/dist/mcp/generated/docs/theme.md +50 -4
- package/dist/mcp/generated/docs/tool-approval-card.md +164 -0
- package/dist/mcp/generated/docs/tool-btn.md +9 -4
- package/dist/mcp/generated/docs/tool-message.md +10 -5
- package/dist/mcp/generated/docs/toolcall-render.md +10 -5
- package/dist/mcp/generated/docs/use-animation-text.md +4 -4
- package/dist/mcp/generated/docs/use-clipboard.md +3 -3
- package/dist/mcp/generated/docs/use-command-selection.md +1 -1
- package/dist/mcp/generated/docs/use-custom-tab.md +1 -1
- package/dist/mcp/generated/docs/use-flow-node-actions.md +124 -0
- package/dist/mcp/generated/docs/use-full-screen.md +3 -3
- package/dist/mcp/generated/docs/use-global-config.md +20 -5
- package/dist/mcp/generated/docs/use-menu-keydown.md +1 -1
- package/dist/mcp/generated/docs/use-message-group.md +42 -10
- package/dist/mcp/generated/docs/use-observer-visible-list.md +1 -1
- package/dist/mcp/generated/docs/user-feedback.md +8 -3
- package/dist/mcp/generated/docs/user-message.md +18 -13
- package/dist/mcp/generated/docs/user-question-answered-card.md +106 -0
- package/dist/mcp/generated/docs/user-question-card.md +228 -0
- package/dist/mcp/generated/docs/user-question-choice.md +108 -0
- package/dist/mcp/generated/docs/user-question-option.md +42 -0
- package/dist/mcp/generated/docs/vnode-renderer.md +126 -0
- package/dist/mcp/generated/index.json +1105 -562
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/list-components.d.ts +7 -6
- package/dist/mcp/tools/list-components.js +23 -29
- package/dist/mcp/tools/list-components.js.map +1 -1
- package/dist/mcp/tools/search-docs.js +1 -1
- package/dist/mcp/tools/search-docs.js.map +1 -1
- package/dist/mcp/utils/doc-loader.d.ts +1 -1
- package/dist/types/editor.d.ts +0 -1
- package/dist/utils/utils.d.ts +2 -2
- package/package.json +4 -3
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<!-- AI SUMMARY -->
|
|
2
|
+
## 快速了解
|
|
3
|
+
|
|
4
|
+
渲染 human-in-the-loop 中断消息,分发工具审批,并按 reason 回显 resume 结果(审批单 / 用户回答)。 源码位置:src/components/chat-message/interrupt-message/interrupt-message.vue。
|
|
5
|
+
|
|
6
|
+
### 关联组件
|
|
7
|
+
- **message-render** — role 为 interrupt 时渲染本组件
|
|
8
|
+
- **user-question-card** — UserQuestion 待回答面板与回答回显
|
|
9
|
+
- **tool-approval-card** — AIDevToolApproval 专用子卡片
|
|
10
|
+
- **message-container** — 透传 onInterruptResume;末条为 interrupt 时不触发组 hover
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
<!-- FULL DOC -->
|
|
14
|
+
|
|
15
|
+
# InterruptMessage 中断消息
|
|
16
|
+
## 源码事实
|
|
17
|
+
|
|
18
|
+
- **源码位置**:`src/components/chat-message/interrupt-message/interrupt-message.vue`
|
|
19
|
+
- **能力域**:Agent 能力
|
|
20
|
+
- **能力说明**:渲染 human-in-the-loop 中断消息,分发工具审批,并按 `result.reason` 回显 resume 结果。
|
|
21
|
+
|
|
22
|
+
> **能力域**:Agent 能力
|
|
23
|
+
|
|
24
|
+
human-in-the-loop 中断消息渲染器(导出名 **`InterruptMessageRender`**)。对应 `MessageRole.Interrupt`,解析 `content.outcome` 渲染审批卡片或兜底提示。
|
|
25
|
+
|
|
26
|
+
> 通常由 [MessageRender](/components/message/message-render) 自动调用,无需业务侧直接引入。
|
|
27
|
+
> `UserQuestion` 的待回答卡片由 [ChatContainer](/components/setup/chat-container) 放在输入区上方,本组件在 `outcome.success` 时按 `result.reason` 回显审批单或用户回答。
|
|
28
|
+
|
|
29
|
+
## 渲染架构
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
InterruptMessageRender
|
|
33
|
+
├── content.message(可选)→ 顶部说明文案
|
|
34
|
+
└── content.outcome.type === 'interrupt'
|
|
35
|
+
└── v-for interrupts
|
|
36
|
+
├── reason === aidev:tool_approval → ToolApprovalCard(透传 onInterruptResume,用于取消审批)
|
|
37
|
+
├── reason === aidev:user_question → 不在消息内渲染,交由 ChatContainer 输入区挂载
|
|
38
|
+
└── 未注册 reason → 兜底块(item.message 或「暂不支持的中断消息」)
|
|
39
|
+
|
|
40
|
+
content.outcome.type === 'success'
|
|
41
|
+
└── resultRenderers[result.reason]
|
|
42
|
+
├── aidev:tool_approval → ToolApprovalCard(readonly,隐藏取消审批)
|
|
43
|
+
└── aidev:user_question → UserQuestionAnsweredCard(支持 #answeredQuestion 透传 #answer)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| `InterruptReason` | 子组件 |
|
|
47
|
+
| ------------------------------ | ------------------- |
|
|
48
|
+
| `aidev:tool_approval`(待审批) | `ToolApprovalCard` |
|
|
49
|
+
| `aidev:tool_approval`(已处理) | `ToolApprovalCard`(`readonly`,只读回显) |
|
|
50
|
+
| `aidev:user_question`(待回答) | 输入区 `UserQuestionCard`,本组件不渲染 |
|
|
51
|
+
| `aidev:user_question`(已回答) | `UserQuestionAnsweredCard` |
|
|
52
|
+
| 其他 / 未注册 | 兜底文案区域 |
|
|
53
|
+
|
|
54
|
+
## 基础用法(待审批)
|
|
55
|
+
|
|
56
|
+
```vue
|
|
57
|
+
<template>
|
|
58
|
+
<InterruptMessageRender
|
|
59
|
+
:id="message.id"
|
|
60
|
+
:message-id="message.messageId"
|
|
61
|
+
:role="message.role"
|
|
62
|
+
:status="message.status"
|
|
63
|
+
:content="message.content"
|
|
64
|
+
:on-interrupt-resume="handleInterruptResume"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import {
|
|
70
|
+
InterruptMessageRender,
|
|
71
|
+
APPROVAL_STATUS,
|
|
72
|
+
InterruptReason,
|
|
73
|
+
MessageRole,
|
|
74
|
+
MessageStatus,
|
|
75
|
+
type InterruptMessage,
|
|
76
|
+
} from '@blueking/chat-x';
|
|
77
|
+
|
|
78
|
+
const message: InterruptMessage = {
|
|
79
|
+
id: 'msg_interrupt',
|
|
80
|
+
messageId: 'msg_interrupt',
|
|
81
|
+
role: MessageRole.Interrupt,
|
|
82
|
+
status: MessageStatus.Pending,
|
|
83
|
+
content: {
|
|
84
|
+
message: '算法方案评审单需要您关注',
|
|
85
|
+
outcome: {
|
|
86
|
+
type: 'interrupt',
|
|
87
|
+
interrupts: [
|
|
88
|
+
{
|
|
89
|
+
id: 'interrupt_1',
|
|
90
|
+
reason: InterruptReason.AIDevToolApproval,
|
|
91
|
+
toolCallId: 'tool_call_1',
|
|
92
|
+
metadata: {
|
|
93
|
+
ticket: {
|
|
94
|
+
approvers: ['张三'],
|
|
95
|
+
sn: 'REV-2026-04-24-001',
|
|
96
|
+
status: APPROVAL_STATUS.PENDING,
|
|
97
|
+
submit_time: '2026-04-24 14:30:15',
|
|
98
|
+
title: '算法方案评审单',
|
|
99
|
+
url: 'https://example.com/tickets/001',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const handleInterruptResume = async (payload, interrupt) => {
|
|
109
|
+
// ToolApprovalCard 点击「取消审批」时,payload 为 ToolApprovalResume
|
|
110
|
+
console.log(payload, interrupt?.id);
|
|
111
|
+
};
|
|
112
|
+
</script>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**渲染效果**
|
|
116
|
+
|
|
117
|
+
## UserQuestion 待回答
|
|
118
|
+
|
|
119
|
+
待回答的 `UserQuestion` 不在消息内渲染;`ChatContainer` 会找到最近一条待回答中断,并在 `ChatInput` 上方挂载 [UserQuestionCard](/components/agent/user-question-card)。
|
|
120
|
+
|
|
121
|
+
```vue
|
|
122
|
+
<InterruptMessageRender
|
|
123
|
+
:content="userQuestionMessage.content"
|
|
124
|
+
role="interrupt"
|
|
125
|
+
/>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**渲染效果**
|
|
129
|
+
|
|
130
|
+
## AIDevToolApproval 已处理回显(outcome.success)
|
|
131
|
+
|
|
132
|
+
`outcome.type === 'success'` 且 `result.reason === InterruptReason.AIDevToolApproval` 时,会话内以只读 `ToolApprovalCard` 回显审批单。`result.payload.metadata` 需透传中断时的 `metadata`(含 `ticket`):
|
|
133
|
+
|
|
134
|
+
```vue
|
|
135
|
+
<InterruptMessageRender
|
|
136
|
+
:content="resumedMessage.content"
|
|
137
|
+
role="interrupt"
|
|
138
|
+
/>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**渲染效果**
|
|
142
|
+
|
|
143
|
+
## UserQuestion 已回答回显(outcome.success)
|
|
144
|
+
|
|
145
|
+
`outcome.type === 'success'` 且 `result.reason === InterruptReason.UserQuestion` 时,会话内回显用户回答。可通过 `#answeredQuestion` slot 自定义单题回显:
|
|
146
|
+
|
|
147
|
+
```vue
|
|
148
|
+
<InterruptMessageRender
|
|
149
|
+
:content="userQuestionAnsweredMessage.content"
|
|
150
|
+
role="interrupt"
|
|
151
|
+
>
|
|
152
|
+
<template #answeredQuestion="{ item, index, status }">
|
|
153
|
+
<MyCustomAnswerView :data="item" :index="index" :status="status" />
|
|
154
|
+
</template>
|
|
155
|
+
</InterruptMessageRender>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**渲染效果**
|
|
159
|
+
|
|
160
|
+
## 不支持的中断类型(兜底)
|
|
161
|
+
|
|
162
|
+
```vue
|
|
163
|
+
<InterruptMessageRender :content="unsupportedContent" role="interrupt" />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**渲染效果**
|
|
167
|
+
|
|
168
|
+
## 在 MessageContainer 中使用
|
|
169
|
+
|
|
170
|
+
配置 `onInterruptResume`,由容器经 `MessageRender` 透传到本组件:
|
|
171
|
+
|
|
172
|
+
```vue
|
|
173
|
+
<MessageContainer
|
|
174
|
+
:messages="messages"
|
|
175
|
+
:on-interrupt-resume="handleInterruptResume"
|
|
176
|
+
/>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
当消息组**最后一条**为 `role: 'interrupt'` 时,容器**不会**在鼠标移入时设置 `isHover`,避免误显 AI 工具栏遮挡审批卡片。
|
|
180
|
+
|
|
181
|
+
## API
|
|
182
|
+
|
|
183
|
+
### Props
|
|
184
|
+
|
|
185
|
+
继承 `Partial<InterruptMessage>` 的字段(`id`、`messageId`、`role`、`content`、`status` 等),并额外支持:
|
|
186
|
+
|
|
187
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
188
|
+
| ----------------- | ------------------ | ------ | ----------------------------------------- |
|
|
189
|
+
| content | `InterruptMessage['content']` | — | 含 `message`、`outcome`、`result` 等 |
|
|
190
|
+
| onInterruptResume | `OnInterruptResume` | — | 用户完成中断操作后的回调(可选) |
|
|
191
|
+
|
|
192
|
+
### Slots
|
|
193
|
+
|
|
194
|
+
| 插槽名 | 参数 | 说明 |
|
|
195
|
+
| ---------------- | ------------------------------------------------- | -------------------------------------------------------------------- |
|
|
196
|
+
| answeredQuestion | `{ item, index, status }` | 自定义 UserQuestion 已回答内容回显,透传给 `UserQuestionAnsweredCard` 的 `#answer` |
|
|
197
|
+
|
|
198
|
+
slot 参数与 [UserQuestionAnsweredCard](/components/agent/user-question-answered-card) 的 `#answer` 一致。
|
|
199
|
+
|
|
200
|
+
### Events / Expose
|
|
201
|
+
|
|
202
|
+
无。
|
|
203
|
+
|
|
204
|
+
## 类型定义
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import type { Interrupt, InterruptMessage, OnInterruptResume } from '@blueking/chat-x';
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
详见 [中断类型 Interrupt](../../types/interrupt.md)。
|
|
211
|
+
|
|
212
|
+
## 关联组件
|
|
213
|
+
|
|
214
|
+
- [ToolApprovalCard](/components/agent/tool-approval-card) — AI Dev 审批单卡片
|
|
215
|
+
- [MessageRender](/components/message/message-render) — 按 `role` 派发
|
|
216
|
+
- [MessageContainer](/components/setup/message-container) — 列表容器与 `onInterruptResume` 透传
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
<!-- AI SUMMARY -->
|
|
2
|
+
## 快速了解
|
|
3
|
+
|
|
4
|
+
定义 RunFinishedOutcome、BaseInterrupt、AIDevToolApprovalInterrupt、AIDevToolApprovalResume、UserQuestionInterrupt、InterruptResult、BaseResume、InterruptMessage 与 OnInterruptResume。 与 MessageRole.Interrupt、InterruptMessageRender、UserQuestionCard、ToolApprovalCard 配合,对应 RUN_FINISHED outcome。
|
|
5
|
+
|
|
6
|
+
### 关联组件
|
|
7
|
+
- **interrupt-message** — 根据 outcome.interrupts 与 reason 渲染中断 UI,success 时按 result.reason 回显审批单或用户回答
|
|
8
|
+
- **user-question-card** — UserQuestion 交互面板,挂载在 ChatInput 上方
|
|
9
|
+
- **tool-approval-card** — AIDevToolApproval 专用卡片
|
|
10
|
+
- **message-render** — role 为 interrupt 时派发 InterruptMessageRender
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
<!-- FULL DOC -->
|
|
14
|
+
|
|
15
|
+
# 中断类型 Interrupt
|
|
16
|
+
|
|
17
|
+
> **分类**:type
|
|
18
|
+
|
|
19
|
+
AG-UI [Interrupts](https://docs.ag-ui.com/drafts/interrupts) 协议相关类型,定义在 `src/ag-ui/types/interrupt.ts`,由 `@blueking/chat-x` 导出。
|
|
20
|
+
|
|
21
|
+
中断链路分为两段:
|
|
22
|
+
|
|
23
|
+
1. Agent 返回 `RUN_FINISHED { outcome: { type: 'interrupt', interrupts } }`,前端渲染等待用户处理的 UI。
|
|
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` 字段进行分支处理;新增操作类型只需扩展此枚举,回调契约与透传链路保持不变。
|
|
42
|
+
|
|
43
|
+
## RunFinishedOutcome
|
|
44
|
+
|
|
45
|
+
`RUN_FINISHED` 事件的 `outcome` 联合类型:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
type RunFinishedOutcome =
|
|
49
|
+
| { interrupts: Interrupt[]; type: 'interrupt' }
|
|
50
|
+
| { type: 'success' };
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| `type` | 说明 |
|
|
54
|
+
| ------------- | -------------------------------------------------------------------- |
|
|
55
|
+
| `'interrupt'` | 等待用户响应;`interrupts` 驱动 UI 渲染审批卡片、用户问题面板等 |
|
|
56
|
+
| `'success'` | 用户已通过 `resume` 处理;按 `result.reason` 在会话内回显审批单(`AIDevToolApproval`)或用户回答(`UserQuestion`) |
|
|
57
|
+
|
|
58
|
+
## BaseInterrupt
|
|
59
|
+
|
|
60
|
+
所有中断项的公共结构:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
type BaseInterrupt<T extends InterruptReason, M extends Record<string, any>> = {
|
|
64
|
+
expiresAt?: string;
|
|
65
|
+
id: string;
|
|
66
|
+
message?: string;
|
|
67
|
+
metadata?: M;
|
|
68
|
+
properties?: Record<string, any>;
|
|
69
|
+
reason: T;
|
|
70
|
+
toolCallId: string;
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## AIDevToolApprovalInterrupt
|
|
75
|
+
|
|
76
|
+
AI Dev 第三方工具审批中断,`reason` 为 `InterruptReason.AIDevToolApproval`(`'aidev:tool_approval'`):
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
type AIDevToolApprovalInterruptPayloadMetaData = {
|
|
80
|
+
ticket: {
|
|
81
|
+
approvers: string[];
|
|
82
|
+
sn: string;
|
|
83
|
+
status: APPROVAL_STATUS;
|
|
84
|
+
submit_time: string;
|
|
85
|
+
title: string;
|
|
86
|
+
url: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type AIDevToolApprovalInterrupt = BaseInterrupt<
|
|
91
|
+
InterruptReason.AIDevToolApproval,
|
|
92
|
+
AIDevToolApprovalInterruptPayloadMetaData
|
|
93
|
+
>;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## AIDevToolApprovalResume
|
|
97
|
+
|
|
98
|
+
AI Dev 第三方工具审批中断响应(resume 后用于 `outcome.success` 时会话内回显审批单)。`payload.metadata` 透传中断时的 `metadata`(含 `ticket`),以便复用 `ToolApprovalCard` 只读渲染:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
type AIDevToolApprovalResume = BaseResume<
|
|
102
|
+
InterruptReason.AIDevToolApproval,
|
|
103
|
+
{ metadata: AIDevToolApprovalInterruptPayloadMetaData }
|
|
104
|
+
>;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## UserQuestionInterrupt
|
|
108
|
+
|
|
109
|
+
用户回答问题中断,`reason` 为 `InterruptReason.UserQuestion`(`'aidev:user_question'`)。交互面板由 `ChatContainer` 挂载到 `ChatInput` 上方。
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
type UserQuestionInterrupt = BaseInterrupt<
|
|
113
|
+
InterruptReason.UserQuestion,
|
|
114
|
+
{
|
|
115
|
+
questions: UserQuestionItem[];
|
|
116
|
+
}
|
|
117
|
+
>;
|
|
118
|
+
|
|
119
|
+
type UserQuestionItem = {
|
|
120
|
+
header: string;
|
|
121
|
+
/** 是否多选;仅选择题语义,自定义表单类问题可不传 */
|
|
122
|
+
multiSelect?: boolean;
|
|
123
|
+
options?: UserQuestionOptionItem[];
|
|
124
|
+
question: string;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
type UserQuestionOptionItem = {
|
|
128
|
+
description: string;
|
|
129
|
+
label: string;
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
约定:
|
|
134
|
+
|
|
135
|
+
- `multiSelect: false` 表示单选题,`true` 表示多选题;**不传**时 UI 不展示单选/多选标签,默认选择题组件仍按单选行为处理。
|
|
136
|
+
- 前端会为每道**选择题**追加 `label: 'others'` 的自由输入项;后端无需重复下发该选项。
|
|
137
|
+
- 当用户选择 Others 时,`answer[].description` 为用户输入文本。
|
|
138
|
+
- 业务可通过 `UserQuestionCard` 的 `#question` slot 渲染自定义表单;作答有效时调用 `setAnswer` 回传 `UserQuestionAnswerItem`,无效时传 `undefined`。
|
|
139
|
+
|
|
140
|
+
## Interrupt
|
|
141
|
+
|
|
142
|
+
当前支持的中断联合类型:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
type Interrupt =
|
|
146
|
+
| AIDevToolApprovalInterrupt
|
|
147
|
+
| UserQuestionInterrupt
|
|
148
|
+
| BaseInterrupt<InterruptReason, Record<string, any>>;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## InterruptResult
|
|
152
|
+
|
|
153
|
+
中断处理结果(resume 后回传/持久化,用于 `outcome.success` 时会话内回显)。按 `reason` 区分不同结果形态,统一具备 `BaseResume` 的 `{ interruptId, reason, status }`;新增中断类型的回显结果只需在此联合扩展:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
type InterruptResult = AIDevToolApprovalResume | UserQuestionResume;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## InterruptResume 联合类型
|
|
160
|
+
|
|
161
|
+
用户操作后通过 `onInterruptResume` 回传的负载(与 `InterruptResult` 用途不同:`InterruptResume` 侧重**动作回传**,`InterruptResult` 侧重**success 态会话内回显**):
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
type InterruptResume = FlowNodeResume | ToolApprovalResume | UserQuestionResume;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| 类型 | `operation` / `reason` | 说明 |
|
|
168
|
+
| -------------------- | ----------------------------------- | ------------------------------------------------------------ |
|
|
169
|
+
| `ToolApprovalResume` | `InterruptResumeOperation.ApprovalCancel` | 第三方工具审批取消 |
|
|
170
|
+
| `FlowNodeResume` | `flow_node_retry` / `flow_node_skip` | FlowAgent 失败节点重试 / 跳过;**无**对应 `Interrupt` 项 |
|
|
171
|
+
| `UserQuestionResume` | `InterruptReason.UserQuestion`(`reason` 字段) | 用户回答问题 |
|
|
172
|
+
|
|
173
|
+
### ToolApprovalResume
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
type ToolApprovalResume = {
|
|
177
|
+
operation: InterruptResumeOperation.ApprovalCancel;
|
|
178
|
+
payload: { interrupt_id: number | string };
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### FlowNodeResume
|
|
183
|
+
|
|
184
|
+
流程节点不属于 interrupt,节点定位信息(`task_id` / `node_id`)随 `payload` 回传;此时 `onInterruptResume` 的第二个 `interrupt` 参数**不传**。
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
type FlowNodeResume = {
|
|
188
|
+
operation: InterruptResumeOperation.FlowNodeRetry | InterruptResumeOperation.FlowNodeSkip;
|
|
189
|
+
payload: {
|
|
190
|
+
node_id: string;
|
|
191
|
+
task_id: number;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## BaseResume / UserQuestionResume
|
|
197
|
+
|
|
198
|
+
`UserQuestion` 的 resume payload 为单个对象,与 `chat-helper` 的 `IResume` 保持一致:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
type BaseResume<T extends InterruptReason, P extends Record<string, any>> = {
|
|
202
|
+
interruptId: string;
|
|
203
|
+
payload: P;
|
|
204
|
+
reason: T;
|
|
205
|
+
status: 'cancelled' | 'resolved';
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
type UserQuestionAnswerItem = {
|
|
209
|
+
answer: UserQuestionOptionItem[];
|
|
210
|
+
multiSelect?: boolean;
|
|
211
|
+
question: string;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
type UserQuestionResume = BaseResume<
|
|
215
|
+
InterruptReason.UserQuestion,
|
|
216
|
+
{
|
|
217
|
+
answers: UserQuestionAnswerItem[];
|
|
218
|
+
}
|
|
219
|
+
>;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
示例:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const resume: UserQuestionResume = {
|
|
226
|
+
interruptId: 'interrupt_user_question',
|
|
227
|
+
reason: InterruptReason.UserQuestion,
|
|
228
|
+
status: 'resolved',
|
|
229
|
+
payload: {
|
|
230
|
+
answers: [
|
|
231
|
+
{
|
|
232
|
+
question: '请选择语言',
|
|
233
|
+
multiSelect: true,
|
|
234
|
+
answer: [
|
|
235
|
+
{ label: 'Java', description: 'Java' },
|
|
236
|
+
{ label: 'others', description: 'Rust' },
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## InterruptMessage
|
|
245
|
+
|
|
246
|
+
`MessageRole.Interrupt` 对应的消息类型,`content` 承载 outcome、可选说明文案与 resume 结果:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
type InterruptMessage = BaseMessage<
|
|
250
|
+
MessageRole.Interrupt,
|
|
251
|
+
{
|
|
252
|
+
message?: string;
|
|
253
|
+
outcome?: RunFinishedOutcome;
|
|
254
|
+
result?: InterruptResult;
|
|
255
|
+
runId?: string;
|
|
256
|
+
threadId?: string;
|
|
257
|
+
}
|
|
258
|
+
>;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
| 字段 | 说明 |
|
|
262
|
+
| ---------- | ------------------------------------------------------------ |
|
|
263
|
+
| `message` | 消息组顶部可选说明文案,由 `InterruptMessageRender` 展示 |
|
|
264
|
+
| `outcome` | `type: 'interrupt'` 时从 `interrupts` 渲染交互;`success` 时进入已处理态 |
|
|
265
|
+
| `result` | 用户 resume 后回传/持久化的 `InterruptResult`;按 `reason` 在会话内回显审批单或用户回答 |
|
|
266
|
+
| `runId` | 关联 AG-UI run 标识 |
|
|
267
|
+
| `threadId` | 关联会话线程标识 |
|
|
268
|
+
|
|
269
|
+
## OnInterruptResume
|
|
270
|
+
|
|
271
|
+
用户完成中断操作或 FlowAgent 节点操作后的回调(由 `ChatContainer` / `MessageContainer` / `MessageRender` 透传):
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
type OnInterruptResume = (
|
|
275
|
+
payload: InterruptResume,
|
|
276
|
+
interrupt?: Interrupt,
|
|
277
|
+
) => Promise<void> | void;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
| 参数 | 说明 |
|
|
281
|
+
| ----------- | ---------------------------------------------------------------------------------------------------------- |
|
|
282
|
+
| `payload` | 用户操作产生的 resume 负载;通过 `payload.operation`(或 `UserQuestionResume.reason`)区分动作类型 |
|
|
283
|
+
| `interrupt` | 原始中断项;审批取消、用户问题等中断来源**必传**;FlowAgent 节点重试 / 跳过等非中断来源**不传** |
|
|
284
|
+
|
|
285
|
+
| `payload.operation` | 触发场景 | `interrupt` |
|
|
286
|
+
| -------------------------------- | -------------------------------- | ----------- |
|
|
287
|
+
| `approval_cancel` | `ToolApprovalCard` 取消审批 | 必传 |
|
|
288
|
+
| `flow_node_retry` / `flow_node_skip` | `FlowAgentContent` 失败节点操作 | 不传 |
|
|
289
|
+
| —(`UserQuestionResume`) | `UserQuestionCard` 提交回答 | 必传 |
|
|
290
|
+
|
|
291
|
+
## 使用示例
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import {
|
|
295
|
+
APPROVAL_STATUS,
|
|
296
|
+
InterruptReason,
|
|
297
|
+
InterruptResumeOperation,
|
|
298
|
+
MessageRole,
|
|
299
|
+
MessageStatus,
|
|
300
|
+
type InterruptMessage,
|
|
301
|
+
type OnInterruptResume,
|
|
302
|
+
} from '@blueking/chat-x';
|
|
303
|
+
|
|
304
|
+
const message: InterruptMessage = {
|
|
305
|
+
id: 'msg_interrupt_1',
|
|
306
|
+
messageId: 'msg_interrupt_1',
|
|
307
|
+
role: MessageRole.Interrupt,
|
|
308
|
+
status: MessageStatus.Pending,
|
|
309
|
+
content: {
|
|
310
|
+
message: '需要您处理以下中断',
|
|
311
|
+
outcome: {
|
|
312
|
+
type: 'interrupt',
|
|
313
|
+
interrupts: [
|
|
314
|
+
{
|
|
315
|
+
id: 'interrupt_approval_1',
|
|
316
|
+
reason: InterruptReason.AIDevToolApproval,
|
|
317
|
+
toolCallId: 'tool_call_approval_1',
|
|
318
|
+
metadata: {
|
|
319
|
+
ticket: {
|
|
320
|
+
approvers: ['张三'],
|
|
321
|
+
sn: 'REV-2026-04-24-001',
|
|
322
|
+
status: APPROVAL_STATUS.PENDING,
|
|
323
|
+
submit_time: '2026-04-24 14:30:15',
|
|
324
|
+
title: '算法方案评审单',
|
|
325
|
+
url: 'https://example.com/tickets/REV-2026-04-24-001',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
id: 'interrupt_question_1',
|
|
331
|
+
reason: InterruptReason.UserQuestion,
|
|
332
|
+
toolCallId: 'tool_call_question_1',
|
|
333
|
+
message: '请选择实现方案',
|
|
334
|
+
metadata: {
|
|
335
|
+
questions: [
|
|
336
|
+
{
|
|
337
|
+
header: '请选择实现方案',
|
|
338
|
+
multiSelect: false,
|
|
339
|
+
question: '你希望采用哪种排序实现?',
|
|
340
|
+
options: [
|
|
341
|
+
{ label: 'basic', description: '基础冒泡排序' },
|
|
342
|
+
{ label: 'optimized', description: '提前终止优化版' },
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const handleInterruptResume: OnInterruptResume = async (payload, interrupt) => {
|
|
354
|
+
if ('operation' in payload) {
|
|
355
|
+
switch (payload.operation) {
|
|
356
|
+
case InterruptResumeOperation.ApprovalCancel:
|
|
357
|
+
console.log('取消审批', payload.payload.interrupt_id, interrupt?.id);
|
|
358
|
+
break;
|
|
359
|
+
case InterruptResumeOperation.FlowNodeRetry:
|
|
360
|
+
case InterruptResumeOperation.FlowNodeSkip:
|
|
361
|
+
console.log('流程节点操作', payload.operation, payload.payload);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// UserQuestionResume
|
|
367
|
+
console.log('用户问题 resume', interrupt?.id, payload);
|
|
368
|
+
};
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## 关联文档
|
|
372
|
+
|
|
373
|
+
- [常量枚举 Constants](./constants.md) — `InterruptReason`、`APPROVAL_STATUS`
|
|
374
|
+
- [消息类型 Messages](./messages.md) — `InterruptMessage` 在消息联合类型中的位置
|
|
375
|
+
- [InterruptMessage 中断消息](../components/agent/interrupt-message)
|
|
376
|
+
- [UserQuestionCard 用户问题中断](../components/agent/user-question-card)
|
|
377
|
+
- [ToolApprovalCard 审批卡片](../components/agent/tool-approval-card)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
以键值列表展示结构化内容。 源码位置:src/components/chat-content/key-value-content/key-value-content.vue。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **user-message** — 用户消息内展示结构化附加信息
|
|
@@ -10,10 +10,15 @@ KeyValueContent 以多行 key : value 形式展示结构化数据,可选标题
|
|
|
10
10
|
<!-- FULL DOC -->
|
|
11
11
|
|
|
12
12
|
# KeyValueContent 键值对内容
|
|
13
|
+
## 源码事实
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
- **源码位置**:`src/components/chat-content/key-value-content/key-value-content.vue`
|
|
16
|
+
- **能力域**:内容渲染
|
|
17
|
+
- **能力说明**:以键值列表展示结构化内容。
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
> **能力域**:内容渲染
|
|
20
|
+
|
|
21
|
+
键值对列表展示基础组件,每行以 `key : value` 格式渲染一条数据,支持可选标题栏(带 `ThinkingIcon`)。
|
|
17
22
|
|
|
18
23
|
主要被 `UserMessage` 内部用于渲染结构化引用内容(`property.extra.cite.data`),通常不需要手动引入。
|
|
19
24
|
|
|
@@ -121,4 +126,4 @@ const message = {
|
|
|
121
126
|
|
|
122
127
|
## 关联组件
|
|
123
128
|
|
|
124
|
-
- [UserMessage](
|
|
129
|
+
- [UserMessage](/components/message/user-message) — 键值气泡展示
|