@blueking/chat-x 0.0.48-beta.1 → 0.0.49-beta.1

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 (38) hide show
  1. package/dist/ag-ui/types/file.d.ts +23 -0
  2. package/dist/ag-ui/types/index.d.ts +1 -0
  3. package/dist/ag-ui/types/messages.d.ts +2 -0
  4. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-file-card.vue.d.ts +14 -0
  5. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/artifact-preview-host.vue.d.ts +8 -0
  6. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/preview-strategy.d.ts +9 -0
  7. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/html-preview.vue.d.ts +6 -0
  8. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/markdown-preview.vue.d.ts +6 -0
  9. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/txt-preview.vue.d.ts +6 -0
  10. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/url-iframe-preview.vue.d.ts +6 -0
  11. package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/use-artifact-preview-loader.d.ts +21 -0
  12. package/dist/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue.d.ts +12 -0
  13. package/dist/components/chat-message/assistant-message/message-artifacts/file-icon.d.ts +29 -0
  14. package/dist/components/chat-message/assistant-message/message-artifacts/message-artifacts.vue.d.ts +10 -0
  15. package/dist/composables/index.d.ts +1 -0
  16. package/dist/composables/use-artifact-preview.d.ts +59 -0
  17. package/dist/composables/use-message-group.d.ts +434 -0
  18. package/dist/icons/file.d.ts +24 -0
  19. package/dist/icons/index.d.ts +1 -0
  20. package/dist/index.css +1 -1
  21. package/dist/index.js +2083 -1600
  22. package/dist/index.js.map +1 -1
  23. package/dist/lang/lang.d.ts +6 -1
  24. package/dist/mcp/generated/docs/activity-message.md +122 -97
  25. package/dist/mcp/generated/docs/assistant-message.md +125 -62
  26. package/dist/mcp/generated/docs/chat-container.md +94 -16
  27. package/dist/mcp/generated/docs/file-artifact-panel.md +279 -0
  28. package/dist/mcp/generated/docs/info-message.md +23 -7
  29. package/dist/mcp/generated/docs/loading-message.md +36 -17
  30. package/dist/mcp/generated/docs/message-render.md +44 -33
  31. package/dist/mcp/generated/docs/reasoning-message.md +15 -11
  32. package/dist/mcp/generated/docs/tool-message.md +61 -45
  33. package/dist/mcp/generated/docs/use-artifact-preview.md +237 -0
  34. package/dist/mcp/generated/docs/use-message-group.md +24 -0
  35. package/dist/mcp/generated/docs/user-message.md +185 -121
  36. package/dist/mcp/generated/index.json +79 -9
  37. package/dist/mcp/index.js +0 -0
  38. package/package.json +20 -21
@@ -1,7 +1,7 @@
1
1
  <!-- AI SUMMARY -->
2
2
  ## 快速了解
3
3
 
4
- 渲染工具返回内容,JSON 场景交给 DescPanel 展示。 源码位置:src/components/chat-message/tool-message/tool-message.vue。
4
+ 渲染工具返回内容;DescPanel 解析 JSON 为 key-value,嵌套值 JSON.stringify 后经 HighlightKeyword 展示。 源码位置:src/components/chat-message/tool-message/tool-message.vue。
5
5
 
6
6
  ### 关联组件
7
7
  - **assistant-message** — 结果常作为 assistant 消息中 toolCall.toolMessage 内联展示
@@ -12,51 +12,57 @@
12
12
  <!-- FULL DOC -->
13
13
 
14
14
  # ToolMessage 工具消息
15
+
15
16
  ## 源码事实
16
17
 
17
18
  - **源码位置**:`src/components/chat-message/tool-message/tool-message.vue`
18
19
  - **能力域**:消息系统
19
- - **能力说明**:渲染工具返回内容,JSON 场景交给 DescPanel 展示。
20
+ - **能力说明**:渲染工具返回内容,JSON 场景交给 DescPanel + HighlightKeyword 展示。
20
21
 
21
- > **能力域**:消息系统
22
+ > **导出说明**:`ToolMessage` **未**从包入口导出(入口同名是 TS interface)。消费方经 `MessageRender` / `ToolcallRender` 使用。下文 `ToolMessageComp` 为文档站内部示例。
22
23
 
23
- 工具(Function Call)执行结果展示组件。内部通过 `DescPanel` 渲染,标题固定为"返回内容",支持将 JSON 自动解析为 key-value 列表。
24
+ 工具执行结果展示组件。内部通过 `DescPanel` 渲染,标题固定为「返回内容」,可解析 JSON 为 key-value 列表。
24
25
 
25
- > **通常不需要直接使用此组件**。`ToolcallRender` 在 `toolCall.toolMessage` 有值时会自动内联渲染;`MessageContainer` 处理 `role: 'tool'` 消息时也会通过 `MessageRender` 自动渲染。
26
+ > **通常不需要直接使用**。`ToolcallRender` 在 `toolCall.toolMessage` 有值时内联渲染;`role: 'tool'` 也可由 `MessageRender` 渲染。
26
27
 
27
28
  ## 渲染架构
28
29
 
29
30
  ```
30
31
  ToolMessage
31
- └── DescPanel(desc="content || (typeof error === 'string' ? error : undefined)",title="返回内容")
32
- ├── JSON.parse(desc) 成功且结果为 object/array
33
- │ └── key-value 列表(v-for 遍历)
34
- │ 值超长时截断 + overflow-tips tooltip
35
- └── 其他(parse 失败 / 结果为基本类型)
36
- └── 纯文本展示
32
+ └── DescPanel(desc = content || (typeof error === 'string' ? error : undefined),title="返回内容")
33
+ ├── JSON.parse 成功且结果为 object/array(排除 null)
34
+ │ └── key-value 列表
35
+ │ · key / value 均经 HighlightKeyword 展示
36
+ │ · 嵌套 object/array:JSON.stringify 后展示(无 overflow-tips)
37
+ └── 其他(parse 失败 / 标量)
38
+ └── HighlightKeyword 纯文本
37
39
  ```
38
40
 
39
41
  ## 基础用法
40
42
 
41
43
  ```vue
42
44
  <template>
43
- <ToolMessage
44
- :content="content"
45
- tool-call-id="call_1"
46
- :duration="850"
47
- />
45
+ <MessageRender :message="message" />
48
46
  </template>
49
47
 
50
48
  <script setup lang="ts">
51
- import { ToolMessage } from '@blueking/chat-x';
52
-
53
- const content = JSON.stringify({
54
- city: '北京',
55
- temperature: 22,
56
- weather: '晴',
57
- humidity: '45%',
58
- wind: '东北风 3 级',
59
- });
49
+ import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
50
+
51
+ const message = {
52
+ id: 't1',
53
+ messageId: 't1',
54
+ role: MessageRole.Tool,
55
+ status: MessageStatus.Complete,
56
+ toolCallId: 'call_1',
57
+ duration: 850,
58
+ content: JSON.stringify({
59
+ city: '北京',
60
+ temperature: 22,
61
+ weather: '晴',
62
+ humidity: '45%',
63
+ wind: '东北风 3 级',
64
+ }),
65
+ };
60
66
  </script>
61
67
  ```
62
68
 
@@ -70,18 +76,22 @@ ToolMessage
70
76
 
71
77
  ```vue
72
78
  <template>
73
- <ToolMessage
74
- content=""
75
- :error="error"
76
- tool-call-id="call_3"
77
- :duration="5000"
78
- />
79
+ <MessageRender :message="message" />
79
80
  </template>
80
81
 
81
82
  <script setup lang="ts">
82
- import { ToolMessage } from '@blueking/chat-x';
83
-
84
- const error = 'Connection timeout: database server is unreachable (timeout: 5000ms)';
83
+ import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
84
+
85
+ const message = {
86
+ id: 't3',
87
+ messageId: 't3',
88
+ role: MessageRole.Tool,
89
+ status: MessageStatus.Error,
90
+ toolCallId: 'call_3',
91
+ duration: 5000,
92
+ content: '',
93
+ error: 'Connection timeout: database server is unreachable (timeout: 5000ms)',
94
+ };
85
95
  </script>
86
96
  ```
87
97
 
@@ -98,25 +108,27 @@ ToolMessage
98
108
  | 解析失败(非法 JSON) | 捕获异常,返回原字符串 | 纯文本 |
99
109
  | `content` 和 `error` 均为空 | `''` → 解析失败 | 空内容区 |
100
110
 
101
- > **注意**:JSON 数组在 JavaScript 中 `typeof [] === 'object'` 为 `true`,因此数组会以 `0:`、`1:`、`2:` 为键渲染为 key-value 列表,而**非**纯文本。
111
+ > **注意**:JSON 数组 `typeof [] === 'object'`,会以 `0:`、`1:`、`2:` 为键渲染为列表,而非整段纯文本。
102
112
 
103
- **嵌套对象值的处理**:当 value 本身是对象时,`{{ value }}` 会渲染为 `[object Object]`,但 hover 展示的 overflow-tips 会显示 `JSON.stringify(value)` 的完整字符串。
113
+ **嵌套对象 / 数组值**:`DescPanel` 用 `JSON.stringify(value)` 转成字符串后交给 `HighlightKeyword`,**不再**使用 overflow-tips。
104
114
 
105
115
  ```typescript
106
- // ✅ 渲染为 key-value 列表
116
+ // key-value 列表
107
117
  const jsonObject = '{"city":"北京","temperature":22}';
108
118
 
109
- // ✅ 渲染为 index-keyed 列表(0: item1, 1: item2)
119
+ // index-keyed 列表(0: item1, 1: item2)
110
120
  const jsonArray = '["item1","item2","item3"]';
111
121
 
112
- // ✅ 渲染为纯文本(基本类型)
113
- const jsonNumber = '42';
114
- const jsonBool = 'true';
122
+ // 嵌套值 stringify 展示
123
+ const nested = '{"result":"success","data":{"city":"北京","meta":{"humidity":45}}}';
115
124
 
116
- // ✅ 渲染为纯文本(解析失败)
125
+ // 标量 / 非法 JSON → 纯文本
126
+ const jsonNumber = '42';
117
127
  const plainText = '查询成功,共返回 10 条记录。';
118
128
  ```
119
129
 
130
+ **嵌套 JSON 渲染效果**
131
+
120
132
  ## 与 ToolcallRender 的关系
121
133
 
122
134
  `ToolcallRender` 在详情面板展开时,若 `toolCall.toolMessage` 有值,会在底部内联渲染 `ToolMessage`:
@@ -186,8 +198,8 @@ const messages = [
186
198
 
187
199
  | 属性名 | 类型 | 说明 |
188
200
  | ---------- | ------------------ | ------------------------------------------------------------------------------- |
189
- | content | `string` | 工具执行返回内容;与 `error` 通过 `\|\|` 决定优先级,**truthy 时 error 被忽略** |
190
- | error | `string` | 工具执行错误信息;仅当 `content` 为 falsy 且 `error` 为 `string` 类型时展示 |
201
+ | content | `string` | 工具执行返回内容;与 `error` 通过 `\|\|` 决定优先级,**truthy 时 error 被忽略** |
202
+ | error | `boolean \| string` | 类型上可为 boolean;**仅当为 `string` 且 content 为 falsy 时**才会展示 |
191
203
  | toolCallId | `string` | 关联的工具调用 ID(透传,组件内不使用) |
192
204
  | duration | `number` | 工具执行耗时(毫秒,透传,组件内不使用) |
193
205
  | status | `MessageStatus` | 消息状态(透传,组件内不使用) |
@@ -210,11 +222,15 @@ interface ToolMessage {
210
222
  content: string;
211
223
  toolCallId: string; // 关联的 ToolCall.id
212
224
  duration: number; // 工具执行耗时(毫秒)
213
- error?: string; // 执行错误信息
225
+ error?: boolean | string; // 仅 string 会展示在 DescPanel
214
226
  name?: string;
215
227
  }
216
228
  ```
217
229
 
230
+ ### Events / Slots / Expose
231
+
232
+ 无。
233
+
218
234
  ## 关联组件
219
235
 
220
236
  - [AssistantMessage](/components/message/assistant-message) — toolCall.toolMessage 内联场景
@@ -0,0 +1,237 @@
1
+ <!-- AI SUMMARY -->
2
+ ## 快速了解
3
+
4
+ useArtifactPreviewProvider 维护 activeArtifactId,openPreview 命中文件并触发 onOpen 打开侧栏 Tab; 并通过 getOnArtifactClick 封装 resolveArtifactUrls(按 outputId 缓存 download_url / preview_url); useArtifactPreviewConsumer 在后代注入同一套 API。buildArtifactId 用 messageUid#index#outputId 生成唯一 id。 正文加载与分类型渲染不在本 composable,由 FileArtifactPanel 内 ArtifactPreviewHost 完成。 FILE_ARTIFACT_TAB_NAME 标识固定「文件产物」Tab。
5
+
6
+ ### 关联组件
7
+ - **chat-container** — Provider 主场景,聚合 sessionArtifacts 并挂载 FileArtifactPanel
8
+ - **file-artifact-panel** — 侧栏面板消费 activeArtifactId 与 setActiveArtifactId;预览加载在面板内 Host
9
+ - **assistant-message** — 文件产物来源 property.artifacts
10
+
11
+ ---
12
+ <!-- FULL DOC -->
13
+
14
+ # useArtifactPreview 文件产物预览
15
+
16
+ > **分类**:composable
17
+
18
+ Provider/Consumer 模式的文件产物预览状态管理。Provider 在 `ChatContainer` 中创建,负责维护当前命中的文件 id;Consumer 在深层 `ArtifactFileCard` 中注入,用于点击卡片触发预览。
19
+
20
+ **职责边界**:
21
+
22
+ - **本 composable**:维护「命中文件」与「URL 解析缓存」;打开侧栏 Tab(`addCustomTab`)由容器通过 `onOpen` 注入
23
+ - **不在本 composable**:聚合会话文件列表、渲染预览面板、按类型 fetch 正文 / iframe 展示 —— 分别由 `useMessageGroup.sessionArtifacts`、`FileArtifactPanel`、内部 `ArtifactPreviewHost` + `useArtifactPreviewLoader` 承担
24
+
25
+ ## 函数签名
26
+
27
+ ### useArtifactPreviewProvider
28
+
29
+ ```typescript
30
+ function useArtifactPreviewProvider(options: {
31
+ /** 读取业务侧异步取链回调(getter 保持对 props 变更敏感) */
32
+ getOnArtifactClick?: () => OnArtifactClick | undefined;
33
+ /** 命中文件后触发:由容器负责 addCustomTab + 展开侧栏 + 选中 Tab */
34
+ onOpen: (artifactId: string) => void;
35
+ }): {
36
+ activeArtifactId: ShallowRef<string>;
37
+ canResolveArtifactUrl: ComputedRef<boolean>;
38
+ openPreview: (payload: OpenArtifactPreviewPayload) => void;
39
+ resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
40
+ setActiveArtifactId: (id: string) => void;
41
+ };
42
+ ```
43
+
44
+ ### useArtifactPreviewConsumer
45
+
46
+ ```typescript
47
+ function useArtifactPreviewConsumer():
48
+ | undefined
49
+ | {
50
+ activeArtifactId: Ref<string>;
51
+ canResolveArtifactUrl: ComputedRef<boolean>;
52
+ openPreview: (payload: OpenArtifactPreviewPayload) => void;
53
+ resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
54
+ setActiveArtifactId: (id: string) => void;
55
+ };
56
+ ```
57
+
58
+ ### buildArtifactId
59
+
60
+ ```typescript
61
+ function buildArtifactId(messageUid: string, index: number, outputId: string): string;
62
+ // => `${messageUid}#${index}#${outputId}`
63
+ ```
64
+
65
+ ## 使用示例
66
+
67
+ ### Provider(ChatContainer)
68
+
69
+ 会话级文件产物由 [`useMessageGroup`](./use-message-group) 统一聚合(`sessionArtifacts`),Provider 只负责命中与打开侧栏 Tab:
70
+
71
+ ```typescript
72
+ import {
73
+ useArtifactPreviewProvider,
74
+ useCustomTabProvider,
75
+ useMessageGroup,
76
+ FILE_ARTIFACT_TAB_NAME,
77
+ } from '@blueking/chat-x';
78
+ import { t } from '@blueking/chat-x/lang';
79
+
80
+ const { addCustomTab, removeCustomTab } = useCustomTabProvider({ /* ... */ });
81
+
82
+ // 会话级文件产物聚合已内聚在 useMessageGroup,直接消费
83
+ const { sessionArtifacts } = useMessageGroup({ keyword, messages, selectedUserMessages });
84
+
85
+ const { activeArtifactId, setActiveArtifactId } = useArtifactPreviewProvider({
86
+ getOnArtifactClick: () => props.onArtifactClick,
87
+ onOpen: () => {
88
+ addCustomTab({
89
+ closable: false,
90
+ label: t('文件产物'),
91
+ name: FILE_ARTIFACT_TAB_NAME,
92
+ order: -1,
93
+ });
94
+ },
95
+ });
96
+
97
+ // 无文件产物时清理 Tab 与命中态
98
+ watch(sessionArtifacts, list => {
99
+ if (!list.length) {
100
+ removeCustomTab(FILE_ARTIFACT_TAB_NAME);
101
+ setActiveArtifactId('');
102
+ }
103
+ });
104
+ ```
105
+
106
+ ### Consumer(ArtifactFileCard)
107
+
108
+ ```typescript
109
+ import { useArtifactPreviewConsumer } from '@blueking/chat-x';
110
+
111
+ const artifactPreview = useArtifactPreviewConsumer();
112
+
113
+ // 有 Provider 时卡片可点击;无 Provider 时返回 undefined,卡片不可点击
114
+ const clickable = computed(() => !!props.onPreview || !!artifactPreview);
115
+
116
+ const handleCardClick = () => {
117
+ if (props.onPreview) {
118
+ props.onPreview(props.file);
119
+ return;
120
+ }
121
+ artifactPreview?.openPreview({
122
+ file: props.file,
123
+ index: props.index ?? 0,
124
+ messageUid: props.messageUid ?? '',
125
+ });
126
+ };
127
+ ```
128
+
129
+ ### 侧栏列表内切换命中文件
130
+
131
+ ```typescript
132
+ // FileArtifactPanel 列表点击 → emit select → 容器调用 setActiveArtifactId
133
+ // 右侧预览由面板内 ArtifactPreviewHost 消费 activeArtifact,自行取链并按类型渲染
134
+ <FileArtifactPanel
135
+ :active-id="activeArtifactId"
136
+ :artifacts="sessionArtifacts"
137
+ @select="setActiveArtifactId"
138
+ />
139
+ ```
140
+
141
+ ### 业务侧取链(ChatContainer `onArtifactClick`)
142
+
143
+ 文本类预览需要可 `fetch` 的 `download_url`;iframe 类需要 `preview_url`(一般为后台转好的 PDF):
144
+
145
+ ```typescript
146
+ const onArtifactClick = async (file: AIFileInfo) => {
147
+ const res = await api.getArtifactUrls(file.outputId);
148
+ return {
149
+ download_url: res.download_url,
150
+ preview_url: res.preview_url,
151
+ };
152
+ };
153
+ ```
154
+
155
+ ## 内置常量
156
+
157
+ | 常量名 | 值 | 说明 |
158
+ | ------------------------- | ------------------ | ----------------------------------------- |
159
+ | `FILE_ARTIFACT_TAB_NAME` | `'file-artifact'` | 「文件产物」侧栏 Tab 的固定标识,不可关闭 |
160
+ | `ARTIFACT_PREVIEW_TOKEN` | `Symbol` | provide/inject 注入 Token |
161
+
162
+ ## 返回值说明
163
+
164
+ | 属性/方法名 | 类型 | 说明 |
165
+ | ------------------- | ----------------------------------------- | -------------------------------------------------------------------- |
166
+ | activeArtifactId | `ShallowRef<string>` | 当前命中的文件 id(`messageUid#index#outputId`) |
167
+ | canResolveArtifactUrl | `ComputedRef<boolean>` | 是否具备异步取链能力(有 `onArtifactClick` 时为 true,下载按钮据此显隐) |
168
+ | openPreview | `(payload: OpenArtifactPreviewPayload) => void` | 由文件卡片触发:计算 id、更新命中态、调用 `onOpen` |
169
+ | resolveArtifactUrls | `(file: AIFileInfo) => Promise<ArtifactUrlResult>` | 调用 `onArtifactClick` 并按 `outputId` 缓存结果;并发请求去重 |
170
+ | setActiveArtifactId | `(id: string) => void` | 直接设置命中文件 id;侧栏列表内切换选中时使用 |
171
+
172
+ ## 类型定义
173
+
174
+ ```typescript
175
+ import type { AIFileInfo, ArtifactUrlResult, OnArtifactClick } from '@blueking/chat-x';
176
+
177
+ /** 打开预览时的入参:文件 + 消息内下标 + 所属消息 uid */
178
+ type OpenArtifactPreviewPayload = {
179
+ file: AIFileInfo;
180
+ index: number;
181
+ messageUid: string;
182
+ };
183
+
184
+ /**
185
+ * 会话级文件产物:在 AIFileInfo 基础上补充命中所需的唯一 id 与所属消息。
186
+ * 同一会话可能出现多个 AssistantMessage + 同名文件,文件名不可作唯一键。
187
+ */
188
+ type SessionArtifact = AIFileInfo & {
189
+ artifactId: string;
190
+ messageUid: string;
191
+ };
192
+
193
+ /** onArtifactClick 返回值(snake_case) */
194
+ type ArtifactUrlResult = {
195
+ download_url?: string;
196
+ preview_url?: string;
197
+ };
198
+ ```
199
+
200
+ ## 唯一 id 规则
201
+
202
+ 同一会话可能存在多个 `AssistantMessage`,且不同消息里可能有同名文件,因此**文件名不可作为唯一键**。Provider 侧聚合与 Consumer 侧透传必须使用同一 `buildArtifactId` 规则:
203
+
204
+ ```typescript
205
+ buildArtifactId('msg-a', 2, 'output-9'); // => 'msg-a#2#output-9'
206
+ ```
207
+
208
+ - `messageUid`:所属 `AssistantMessage` 的 `uid`(回退 `String(id)`)
209
+ - `index`:文件在所属消息 `property.artifacts` 数组中的下标
210
+ - `outputId`:文件自身的 `AIFileInfo.outputId`
211
+
212
+ ## 完整触发链路
213
+
214
+ ```
215
+ ArtifactFileCard(点击)
216
+ └─ useArtifactPreviewConsumer().openPreview({ file, index, messageUid })
217
+ └─ useArtifactPreviewProvider(ChatContainer)
218
+ ├─ activeArtifactId = buildArtifactId(...)
219
+ └─ onOpen(artifactId) → addCustomTab(FILE_ARTIFACT_TAB_NAME)
220
+ └─ FileArtifactPanel(列表 + 下载头,@select → setActiveArtifactId)
221
+ └─ ArtifactPreviewHost(loader + 分类型 renderer)
222
+ ```
223
+
224
+ 分类型预览策略见 [FileArtifactPanel 预览机制](../components/message/file-artifact-panel#预览机制)。
225
+
226
+ ## 设计特点
227
+
228
+ - **职责单一**:composable 不直接调用 `useCustomTab`,侧栏 Tab 打开逻辑由 `onOpen` 注入;也不做正文 fetch / iframe 渲染
229
+ - **ShallowRef 优先**:`activeArtifactId` 使用 `shallowRef`,避免不必要的深层响应式开销
230
+ - **Consumer 兜底**:`useArtifactPreviewConsumer` 无 Provider 时返回 `undefined`,文件卡片在无容器上下文时自动不可点击
231
+ - **与 useCustomTab 协作**:「文件产物」Tab 通过 `addCustomTab` 按需添加(`order: -1`、`closable: false`),会话无文件产物时由容器 `removeCustomTab` 清理
232
+
233
+ ## 关联组件
234
+
235
+ - [ChatContainer](../components/setup/chat-container) — Provider 主场景,内置「文件产物」Tab
236
+ - [FileArtifactPanel](../components/message/file-artifact-panel) — 侧栏列表与预览 Host 挂载
237
+ - [AssistantMessage](../components/message/assistant-message) — 文件产物来源(`property.artifacts`)
@@ -28,6 +28,7 @@ function useMessageGroup(options: {
28
28
  }): {
29
29
  messageGroups: Ref<MessageGroup[]>;
30
30
  executionGroups: ComputedRef<MessageGroup[]>;
31
+ sessionArtifacts: ComputedRef<SessionArtifact[]>;
31
32
  pendingApprovalCount: ComputedRef<number>;
32
33
  pendingApprovalTipText: ComputedRef<string>;
33
34
  isShareMode: ShallowRef<boolean>;
@@ -116,6 +117,28 @@ const isExecutionMessage = (m: Message): boolean => {
116
117
  | toolCall | `function.name`、`mcpName`、`description`、`arguments`、`id` |
117
118
  | flow_agent | 各任务 `task_name`、各节点 `name` |
118
119
 
120
+ ## sessionArtifacts 会话级文件产物
121
+
122
+ `sessionArtifacts` 拍平当前会话所有 `AssistantMessage.property.artifacts`,供 `ChatContainer` 侧栏「文件产物」Tab 聚合预览。由于同一会话可能出现「多个 AssistantMessage + 同名文件」,文件名不可作为唯一键,统一用 `messageUid`(回退 `String(id)`)+ 消息内下标 + `outputId` 通过 [`buildArtifactId`](./use-artifact-preview) 生成全局唯一 `artifactId`:
123
+
124
+ ```typescript
125
+ const sessionArtifacts = computed(() => {
126
+ const list = [];
127
+ for (const message of messages.value) {
128
+ if (message.role !== MessageRole.Assistant) continue;
129
+ const artifacts = message.property?.artifacts;
130
+ if (!artifacts?.length) continue;
131
+ const messageUid = message.uid ?? String(message.id);
132
+ artifacts.forEach((file, index) => {
133
+ list.push({ ...file, artifactId: buildArtifactId(messageUid, index, file.outputId), messageUid });
134
+ });
135
+ }
136
+ return list;
137
+ });
138
+ ```
139
+
140
+ 预览命中与取链见 [useArtifactPreview](./use-artifact-preview);侧栏列表与分类型预览(`ArtifactPreviewHost`)见 [FileArtifactPanel](../components/message/file-artifact-panel)。
141
+
119
142
  ## 待审批统计
120
143
 
121
144
  `useMessageGroup` 会统计消息列表中处于待审批状态的 AI Dev 审批中断:
@@ -186,6 +209,7 @@ const {
186
209
  | ---------------- | ----------------------------- | --------------------------------------------------------------------------- |
187
210
  | messageGroups | `Ref<MessageGroup[]>` | 完整消息分组列表 |
188
211
  | executionGroups | `ComputedRef<MessageGroup[]>` | 仅包含执行类消息的分组(工具调用 + FlowAgent),自动提取 `userMessageTitle` |
212
+ | sessionArtifacts | `ComputedRef<SessionArtifact[]>` | 拍平会话所有 AssistantMessage 文件产物,含全局唯一 `artifactId` |
189
213
  | pendingApprovalCount | `ComputedRef<number>` | 当前消息中待审批 AI Dev 审批中断的数量 |
190
214
  | pendingApprovalTipText | `ComputedRef<string>` | 待审批阻塞发送提示文案;无待审批时为空字符串 |
191
215
  | isShareMode | `ShallowRef<boolean>` | 是否处于分享模式 |