agent-tool-chat 1.2.0 → 1.6.0

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  基于 **ToolPackage** 架构的 AI 对话插件,工具由宿主控制执行。基于 Vue 3 + Element Plus。
4
4
 
5
- **版本:** 1.0.1
5
+ **版本:** 1.5.0
6
6
 
7
7
  ## 安装
8
8
 
@@ -140,7 +140,7 @@ const myToolPackage: ToolPackage = {
140
140
 
141
141
  | 字段 | 作用 |
142
142
  |---|---|
143
- | `suggestComponent` | 建议使用组件名(`ToolPackage.components` 中注册)渲染 |
143
+ | `suggestComponent` | 建议使用组件名(`ToolPackage.components` 中注册)渲染;组件在本轮消息末尾显示(最终文字之后、建议条之前) |
144
144
  | `resultFormat` | `table` / `card` / `list` / `text` / `map`,无组件时按此降级渲染 |
145
145
  | `previewFields` | 表格列字段(→ `RenderBlock.fields`),空数组/缺省时全量显示 |
146
146
  | `summaryTemplate` | 渲染块标题模板,`{key}` 从工具 `args` 插值 |
@@ -149,6 +149,42 @@ const myToolPackage: ToolPackage = {
149
149
  | `suggestedNext` | 后续建议文本 |
150
150
  | `suggestedNextEvent` | 延迟建议:等待该业务事件到达后再显示 |
151
151
 
152
+ ### 工具前置条件(`requires`)
153
+
154
+ 有些工具必须在满足业务条件后才能执行(如"设置河段模型"需要已选中方案、"创建水文方案"需要先确认出口站)。用 `requires` 声明条件:插件把条件写进系统提示词,并在执行前通过 `onCheckToolRequirement` 交给宿主校验;未满足时**不调用工具**,而是返回结构化结果给模型。
155
+
156
+ ```ts
157
+ const tool: ToolDefinition = {
158
+ name: 'handle_set_edge_model',
159
+ // ...
160
+ requires: {
161
+ description: '需要已选中/打开一个方案',
162
+ check: 'scheme:selected', // 宿主校验项 id(硬拦截)
163
+ hint: '当前没有选中的方案,请先打开方案或询问用户',
164
+ action: 'ask-user', // call-tool | ask-user | block
165
+ // tools: ['handle_get_current_scheme_id'], // 可选:仅提示词建议
166
+ },
167
+ }
168
+
169
+ const chat = useAgentChat({
170
+ toolPackages: [pkg],
171
+ onToolCall,
172
+ onCheckToolRequirement: async ({ check, toolName, args, sessionId }) => {
173
+ if (check === 'scheme:selected') {
174
+ return getCurrentSchemeId()
175
+ ? { ok: true }
176
+ : { ok: false, message: '当前没有选中的方案' }
177
+ }
178
+ return { ok: true }
179
+ },
180
+ })
181
+ ```
182
+
183
+ - `tools` 只是提示词建议("建议先调用"),插件**不做**"是否调用过前置工具"的硬校验(历史调用 ≠ 当前状态);需要硬拦截时用 `check`。
184
+ - 未满足时工具结果是结构化 JSON:`{ ok:false, code:'REQUIREMENT_UNMET', tool, requirements:[…], hint, suggestedTool, action, retryable:true }`,模型据此补齐条件后重试;UI 上该工具显示为「缺少前置条件」告警态,悬浮可见原因。
185
+ - 宿主未提供 `onCheckToolRequirement` 时 `check` 型条件放行(控制台告警一次),保持旧行为;校验钩子抛异常按"未满足"处理,避免半状态产生副作用。
186
+ - 未声明 `requires` 的工具行为完全不变。
187
+
152
188
  ### 表格行选择
153
189
 
154
190
  当工具结果渲染为表格且 `selectable: true` 时,多行结果每行出现"选择"按钮,点击后向对话发送:
@@ -233,11 +269,12 @@ interface FileAttachment {
233
269
  3. **渲染 `ChatPanel`** — 将返回的 `AgentChatAPI` 绑定到 `chat` prop;也可不传 `chat`,仅传可选 `options`(或不传,直接开箱即用内置工具)。
234
270
  4. **文件上传** — `ChatInput` 支持文件选择,工具通过 `fileId` 引用。
235
271
  5. **业务联动** — 页面操作完成后 `eventBus.dispatch` 回灌,驱动 LLM 回复或延迟建议。
236
- 6. **持久化** — 消息与设置自动保存(direct 模式 localStorage;proxy 模式 POST `/api/messages`,按 device_id 区分)。
272
+ 6. **选中文字发送** — 在消息区选中文字后,选区旁浮出「发送」按钮,点击即把选中文本作为一条用户消息发出。
273
+ 7. **持久化** — 消息与设置自动保存(direct 模式 localStorage 按会话分键;proxy 模式写服务端 `/api/sessions`,按 device_id 区分)。
237
274
 
238
275
  ## 模型提供方
239
276
 
240
- 插件内置 DeepSeek、OpenAI 和自定义 OpenAI 兼容三种提供方,可在设置面板切换,每个提供方独立保存 `apiKey / baseUrl / model / temperature / thinking`。
277
+ 插件内置 DeepSeek、上善、自定义(OpenAI 兼容)三种提供方,可在设置面板切换,每个提供方独立保存 `apiKey / baseUrl / model / temperature / thinking`(`openai` 预设目前处于注释状态,需要时在 `config.ts` 中取消注释即可启用)。
241
278
 
242
279
  ```ts
243
280
  const chat = useAgentChat({
@@ -262,14 +299,60 @@ const chat = useAgentChat({
262
299
  - 旧字段 `settings.apiKey/baseUrl/model` 仍可读取,会始终镜像当前提供方的值。
263
300
  - 前端直连使用 `POST {baseUrl}/chat/completions`;后端代理使用 `POST {proxyUrl}/api/chat`,并在请求体携带 `providerId`。
264
301
  - 代理服务按 `<PROVIDER_ID>_API_KEY` / `<PROVIDER_ID>_BASE_URL` 环境变量路由,详见 `server/.env.example`。
302
+ - **上善**:前端设置面板选「上善」即可;代理模式下地址与 Key 都由服务端决定——只需配置 `SHANGSHAN_API_KEY`。地址默认使用内置上善地址(可用 `SHANGSHAN_BASE_URL` 覆盖),`SHANGSHAN_MODEL` 可在客户端未传模型时兜底。
303
+ - 服务端内置默认地址的提供方:`deepseek` / `openai` / `shangshan`;`openai-compatible`、`openai-compatible-custom` 没有默认地址,必须显式配置 `*_BASE_URL`。
265
304
  - 非 OpenAI 兼容协议(如 Claude/Gemini)可通过 `registerProvider(id, factory)` 或 `options.provider` 接入。
266
305
 
306
+ ## 会话管理(新开会话 / 历史会话)
307
+
308
+ 面板头部提供 **新对话**(➕)与 **历史对话**(🕘)两个入口:
309
+
310
+ - **新对话**:清空当前上下文、回到草稿;草稿在发出首条消息时才落库,历史列表里不会出现空会话;
311
+ - **历史对话**:右侧抽屉列出全部会话(标题、更新时间、消息数),点击切换、垃圾桶图标删除(二次确认);当前会话高亮;
312
+ - 标题自动取**首条用户消息前 24 字**(无文字时退化为文件名);
313
+ - 刷新后自动打开上次打开的会话(`{storagePrefix}current_session` 记录)。
314
+
315
+ ```ts
316
+ const chat = useAgentChat({ toolPackages: [], onToolCall })
317
+
318
+ await chat.newSession() // 新对话(当前会话先落盘)
319
+ await chat.switchSession(id) // 切换历史会话
320
+ await chat.deleteSession(id) // 删除会话
321
+ chat.sessions // Ref<SessionMeta[]>,按 updatedAt 倒序
322
+ chat.currentSessionId // Ref<string>,'' 表示草稿
323
+ ```
324
+
325
+ ### 存储位置
326
+
327
+ | providerMode | 会话 / 消息存储 | 说明 |
328
+ |---|---|---|
329
+ | `direct` | `localStorage` | 索引 `{storagePrefix}sessions`;每个会话 `{storagePrefix}session_{id}`;防抖 400ms 批量写入 |
330
+ | `proxy` | 服务端 `packages/agent-chat/server`(SQLite `data.db`) | `GET/POST /api/sessions`、`GET/PUT /api/sessions/:id/messages`、`PATCH/DELETE /api/sessions/:id`,按 `device_id` 归属 |
331
+
332
+ - 旧版单会话数据自动迁移:直连模式下 `{storagePrefix}messages` → 一条历史会话(迁移后删除旧键);服务端旧表 `messages/sessions` → `legacy_{device_id}` 会话,旧表重命名为 `*_legacy` 保留。
333
+ - 服务端旧接口 `/api/messages` 仍可用(映射到该设备最近一条会话),仅用于兼容老前端,建议新代码使用 `/api/sessions`。
334
+
267
335
  ## 数据持久化
268
336
 
269
337
  | providerMode | 消息存储 | 说明 |
270
338
  |---|---|---|
271
- | `direct` | `localStorage` | 键 `{storagePrefix}messages` |
272
- | `proxy` | 服务端 `/api/messages` | 按 `device_id` 标识,防抖 400ms 批量提交 |
339
+ | `direct` | `localStorage` | 按会话分键,见上表 |
340
+ | `proxy` | 服务端 `/api/sessions/:id/messages` | 按 `device_id` 标识,防抖 400ms 批量提交 |
341
+
342
+ ## 会话上限与生命周期
343
+
344
+ ```ts
345
+ const chat = useAgentChat({
346
+ maxContextTokens: 48000, // 历史 token 预算(估算值),超出从前往后裁剪
347
+ maxHistoryMessages: 60, // 历史消息硬上限
348
+ maxToolResultChars: 30000, // 单次工具结果写入模型的字符上限;0 = 不限制
349
+ disposeOnUnmount: false, // 组件卸载时是否自动 dispose,默认 false
350
+ })
351
+ ```
352
+
353
+ - **工具结果截断**:仅截断写入模型上下文的部分(保留首尾 + 截断标记),UI 中工具结果始终完整。默认 30000 字符,正常工具返回不受影响;设 `0` 完全关闭。
354
+ - **生命周期**:业务事件(`EventEngine`)按应用级常驻,组件卸载默认**不会**停止它——这样宿主缓存 chat 实例(例如跨路由复用)时,路由往返后事件依然有效。需要"卸载即清理"的旧行为时传 `disposeOnUnmount: true`;也可随时手动调用 `chat.dispose()` 停止事件引擎、待定监听与持久化定时器。
355
+ - **同步失败可见**:`chat.syncError` 记录 proxy/本地存储的同步失败原因,`ChatPanel` 会与 `errorMsg` 合并展示,不再静默丢数据。
273
356
 
274
357
  ## API 概览
275
358
 
@@ -285,8 +368,14 @@ createProvider(id, mode) // 按 providerId + 模式路由
285
368
  registerProvider(id, factory) // 注册非 OpenAI 兼容协议提供方
286
369
  createEventBus() // 创建独立 EventBus
287
370
  Agent, ToolRegistry // 底层 Agent 多轮循环器 / 工具注册表
371
+ clampText(text, limit) // 工具结果截断工具函数
372
+ createLocalSessionStore(prefix) // 直连模式会话存储(localStorage)
373
+ createProxySessionStore(base, id) // 代理模式会话存储(/api/sessions)
374
+ deriveSessionTitle(text, files) // 会话标题生成(首条消息前 24 字)
288
375
  ```
289
376
 
377
+ `AgentChatAPI` 除 `messages / settings / loading / errorMsg / sendMessage / stopGeneration / clearMessages / saveSettings` 外,还提供 `sessions / currentSessionId / sessionsLoading / newSession / switchSession / deleteSession`(多会话)、`syncError`(同步失败提示)与 `dispose()`(主动释放)。
378
+
290
379
  ## 详细文档
291
380
 
292
381
  - [DESIGN.md](./DESIGN.md) — 完整架构设计(事件系统、渲染流程、SuggestedNext 延迟显示逻辑等)
@@ -6,8 +6,14 @@ export interface AgentConfig {
6
6
  provider: LLMProvider;
7
7
  providerConfig: LLMProviderConfig;
8
8
  tools: ToolRegistry;
9
+ /**
10
+ * @deprecated 消息持久化由上层(useAgentChat 的防抖持久化)负责,
11
+ * 这里仅保留接口兼容;不传时 agent 不做任何落盘。
12
+ */
9
13
  storage?: StorageAdapter;
10
14
  maxToolRounds?: number;
15
+ /** 单次工具结果写入模型的字符上限;0 表示不限制。默认 30000 */
16
+ maxToolResultChars?: number;
11
17
  systemPrompt?: string;
12
18
  maxContextTokens?: number;
13
19
  maxHistoryMessages?: number;
@@ -27,5 +33,7 @@ export declare class Agent {
27
33
  private emit;
28
34
  sendMessage(text: string): Promise<void>;
29
35
  stop(): void;
36
+ /** 移除末尾的空 assistant 占位(请求失败后调用) */
37
+ private dropTrailingEmptyAssistant;
30
38
  private buildApiMessages;
31
39
  }
@@ -2,6 +2,7 @@ export { Agent } from './agent';
2
2
  export type { AgentConfig } from './agent';
3
3
  export { ToolRegistry } from './tool-registry';
4
4
  export { createLocalStorageAdapter } from './storage';
5
+ export { clampText } from './truncate';
5
6
  export type { StorageAdapter } from './storage';
6
7
  export type { LLMProvider, StreamChatParams } from './provider';
7
- export type { CoreMessage, ToolCall, ToolOutput, ToolDefinition, ToolHandler, StreamResult, LLMProviderConfig, AgentEvent, AgentEventListener, MessageRole, } from './types';
8
+ export type { CoreMessage, ToolCall, ToolOutput, ToolDefinition, ToolHandler, StreamResult, LLMProviderConfig, AgentEvent, AgentEventListener, MessageRole, TokenUsage, } from './types';
@@ -3,4 +3,8 @@ export interface StorageAdapter {
3
3
  load(): CoreMessage[] | Promise<CoreMessage[]>;
4
4
  save(messages: CoreMessage[]): void | Promise<void>;
5
5
  }
6
+ /**
7
+ * @deprecated 消息持久化由 useAgentChat 的防抖持久化负责(direct → localStorage,
8
+ * proxy → POST /api/messages)。此适配器仅为兼容旧的 Agent 用法保留。
9
+ */
6
10
  export declare function createLocalStorageAdapter(key: string): StorageAdapter;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 工具结果截断:避免超长结果(geojson、长时间序列等)把上下文预算一次性吃满。
3
+ * 仅截断写入模型上下文的内容,UI 上的工具结果始终保留完整值。
4
+ *
5
+ * @param text 工具返回的原始文本
6
+ * @param limit 字符上限;<=0 表示不限制
7
+ */
8
+ export declare function clampText(text: string, limit: number): string;
@@ -17,6 +17,8 @@ export interface CoreMessage {
17
17
  export interface ToolOutput {
18
18
  success: boolean;
19
19
  result: string;
20
+ /** 结构化错误码,例如 REQUIREMENT_UNMET(前置条件未满足) */
21
+ code?: string;
20
22
  }
21
23
  export interface ToolDefinition {
22
24
  type: 'function';
@@ -27,11 +29,18 @@ export interface ToolDefinition {
27
29
  };
28
30
  }
29
31
  export type ToolHandler = (args: any) => Promise<ToolOutput> | ToolOutput;
32
+ /** 上游返回的 token 用量(部分提供方/网关不返回) */
33
+ export interface TokenUsage {
34
+ prompt_tokens?: number;
35
+ completion_tokens?: number;
36
+ total_tokens?: number;
37
+ }
30
38
  export interface StreamResult {
31
39
  content: string;
32
40
  reasoning: string;
33
41
  tool_calls: ToolCall[];
34
42
  finish_reason: string;
43
+ usage?: TokenUsage;
35
44
  }
36
45
  export interface ThinkingConfig {
37
46
  type: 'enabled' | 'disabled';
@@ -63,6 +72,7 @@ export type AgentEvent = {
63
72
  type: 'stream:end';
64
73
  content: string;
65
74
  reasoning: string;
75
+ finishReason?: string;
66
76
  } | {
67
77
  type: 'tool:start';
68
78
  call: ToolCall;
@@ -1 +1 @@
1
- .render-block[data-v-ea599d5b]{margin:8px 0;padding:8px 0;width:100%}.render-block__component[data-v-ea599d5b]{border-radius:8px;background:var(--el-fill-color-lighter);box-shadow:0 0 10px #0000001a}.render-block__title[data-v-ea599d5b]{font-weight:600;font-size:14px;margin-bottom:6px;color:var(--el-text-color-primary)}.render-block__table[data-v-ea599d5b]{width:100%;max-width:900px;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px #0000000f}.render-block__cards[data-v-ea599d5b]{display:flex;flex-direction:column;gap:8px}.card-item[data-v-ea599d5b]{border:1px solid var(--el-border-color);border-radius:8px;padding:10px 14px;background:var(--el-fill-color-lighter);box-shadow:0 2px 6px #0000000d}.card-field[data-v-ea599d5b]{display:flex;gap:6px;font-size:13px;line-height:1.8}.card-label[data-v-ea599d5b]{font-weight:600;color:var(--el-text-color-secondary);min-width:80px}.card-value[data-v-ea599d5b]{color:var(--el-text-color-primary)}.render-block__list[data-v-ea599d5b]{display:flex;flex-direction:column;gap:4px}.list-item[data-v-ea599d5b]{padding:6px 10px;font-size:13px;border-left:3px solid var(--el-color-primary);background:var(--el-fill-color-lighter);border-radius:0 4px 4px 0;box-shadow:0 1px 4px #0000000a}.suggested-next[data-v-0e0e9b65]{display:flex;align-items:center;gap:8px;padding:8px 12px;margin:6px 0;background:var(--el-color-primary-light-9);border:1px solid var(--el-color-primary-light-7);border-radius:8px;font-size:13px}.suggested-next__icon[data-v-0e0e9b65]{font-size:15px;flex-shrink:0}.suggested-next__text[data-v-0e0e9b65]{flex:1;color:var(--el-text-color-regular)}.chat-message[data-v-7317da3a]{display:flex;gap:10px;padding:12px 0;animation:msg-in-7317da3a .25s ease both}@keyframes msg-in-7317da3a{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-message.role-user[data-v-7317da3a]{flex-direction:row-reverse}.chat-message.role-user .message-time[data-v-7317da3a]{text-align:right}.message-avatar[data-v-7317da3a]{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px;color:#fff;box-shadow:0 0 0 2px var(--el-bg-color),0 2px 6px #0000001f}.role-user .message-avatar[data-v-7317da3a]{background:linear-gradient(135deg,var(--el-color-primary),var(--el-color-primary-light-3))}.role-assistant .message-avatar[data-v-7317da3a]{background:linear-gradient(135deg,#009688,#2d8cf0)}.message-time[data-v-7317da3a]{display:block;font-size:11px;line-height:1;color:var(--el-text-color-placeholder);margin-bottom:4px}.message-body[data-v-7317da3a]{max-width:80%;min-width:0}.role-user .message-body[data-v-7317da3a]{background:linear-gradient(135deg,var(--el-color-primary-light-9),var(--el-color-primary-light-8));padding:8px 14px;border-radius:12px 4px 12px 12px;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a}.role-assistant .message-body[data-v-7317da3a]{position:relative;background:var(--el-fill-color-lighter);padding:8px 14px;border-radius:4px 12px 12px;display:flex;flex-direction:column;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a}.message-actions[data-v-7317da3a]{position:absolute;top:0;right:0;opacity:0;transition:opacity .2s;z-index:1}.role-assistant .message-body:hover .message-actions[data-v-7317da3a]{opacity:1}.role-assistant .message-body .message-time[data-v-7317da3a]{float:left;width:10px}.message-action-btn[data-v-7317da3a]{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;color:var(--el-text-color-secondary);cursor:pointer;font-size:15px}.message-action-btn[data-v-7317da3a]:hover{background:var(--el-fill-color);color:var(--el-color-primary)}.message-user-text[data-v-7317da3a]{font-size:14px;line-height:1.5;color:var(--el-text-color-primary);white-space:pre-wrap;word-break:break-word}.message-content[data-v-7317da3a]{font-size:14px;line-height:1.6;color:var(--el-text-color-primary);word-break:break-word;background:transparent;border:none;outline:none;box-shadow:none;height:auto;overflow-wrap:break-word}.message-content[data-v-7317da3a] p{margin:0 0 8px}.message-content[data-v-7317da3a] p:last-child{margin-bottom:0}.message-content[data-v-7317da3a] h1,.message-content[data-v-7317da3a] h2,.message-content[data-v-7317da3a] h3,.message-content[data-v-7317da3a] h4,.message-content[data-v-7317da3a] h5,.message-content[data-v-7317da3a] h6{margin:12px 0 8px;font-weight:600;line-height:1.4;color:var(--el-text-color-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.message-content[data-v-7317da3a] h1[data-collapsed]:after,.message-content[data-v-7317da3a] h2[data-collapsed]:after,.message-content[data-v-7317da3a] h3[data-collapsed]:after,.message-content[data-v-7317da3a] h4[data-collapsed]:after,.message-content[data-v-7317da3a] h5[data-collapsed]:after,.message-content[data-v-7317da3a] h6[data-collapsed]:after{content:" ▸";font-size:.8em;color:var(--el-text-color-secondary)}.message-content[data-v-7317da3a] h1{font-size:20px}.message-content[data-v-7317da3a] h2{font-size:18px;padding-bottom:4px;border-bottom:1px solid var(--el-border-color-lighter)}.message-content[data-v-7317da3a] h3{font-size:16px}.message-content[data-v-7317da3a] h4{font-size:15px}.message-content[data-v-7317da3a] h5,.message-content[data-v-7317da3a] h6{font-size:14px}.message-content[data-v-7317da3a] strong{font-weight:600}.message-content[data-v-7317da3a] em{font-style:italic}.message-content[data-v-7317da3a] ul,.message-content[data-v-7317da3a] ol{margin:0 0 8px;padding-left:20px}.message-content[data-v-7317da3a] ul:last-child,.message-content[data-v-7317da3a] ol:last-child{margin-bottom:0}.message-content[data-v-7317da3a] li{margin:2px 0}.message-content[data-v-7317da3a] li>ul,.message-content[data-v-7317da3a] li>ol{margin:0}.message-content[data-v-7317da3a] code{padding:1px 5px;margin:0 2px;font-size:12px;font-family:Consolas,Monaco,Courier New,monospace;background:var(--el-fill-color);color:var(--el-color-primary);border-radius:4px}.message-content[data-v-7317da3a] pre{margin:8px 0;padding:10px 12px;background:var(--el-bg-color);border:1px solid var(--el-border-color-lighter);border-radius:6px;overflow-x:auto}.message-content[data-v-7317da3a] .md-code-block{margin:8px 0}.message-content[data-v-7317da3a] .md-code-block__head{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:var(--el-fill-color);border:1px solid var(--el-border-color-lighter);border-bottom:none;border-radius:6px 6px 0 0;font-size:12px}.message-content[data-v-7317da3a] .md-code-block__lang{color:var(--el-text-color-secondary);font-family:Consolas,Monaco,Courier New,monospace}.message-content[data-v-7317da3a] .md-code-block__copy{color:var(--el-color-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .2s}.message-content[data-v-7317da3a] .md-code-block__copy:hover{opacity:.7}.message-content[data-v-7317da3a] .md-code-block pre{margin:0;border-radius:0 0 6px 6px}.message-content[data-v-7317da3a] pre code{display:block;padding:0;margin:0;font-size:12px;line-height:1.6;background:transparent;color:var(--el-text-color-primary);border-radius:0;white-space:pre}.message-content[data-v-7317da3a] blockquote{margin:8px 0;padding:4px 12px;border-left:3px solid var(--el-color-primary-light-5);background:var(--el-fill-color-lighter);border-radius:0 6px 6px 0;color:var(--el-text-color-regular)}.message-content[data-v-7317da3a] blockquote p{margin:0}.message-content[data-v-7317da3a] table{width:100%;margin:8px 0;border-collapse:collapse;font-size:13px}.message-content[data-v-7317da3a] th,.message-content[data-v-7317da3a] td{padding:6px 10px;border:1px solid var(--el-border-color-lighter);text-align:left}.message-content[data-v-7317da3a] th{background:var(--el-fill-color);font-weight:600;color:var(--el-text-color-primary)}.message-content[data-v-7317da3a] tbody tr:nth-child(2n){background:var(--el-fill-color-lighter)}.message-content.is-table-clickable[data-v-7317da3a] tbody tr{cursor:pointer;transition:background .15s}.message-content.is-table-clickable[data-v-7317da3a] tbody tr:hover{background:var(--el-color-primary-light-9)}.message-content.is-list-clickable[data-v-7317da3a] li{cursor:pointer;border-radius:4px;transition:background .15s}.message-content.is-list-clickable[data-v-7317da3a] li:hover{background:var(--el-color-primary-light-9)}.message-content[data-v-7317da3a] a{color:var(--el-color-primary);text-decoration:none}.message-content[data-v-7317da3a] a:hover{text-decoration:underline}.message-content[data-v-7317da3a] img{max-width:100%;border-radius:6px}.message-content[data-v-7317da3a] hr{margin:12px 0;border:none;border-top:1px solid var(--el-border-color-lighter)}.typing-dots[data-v-7317da3a]{display:inline-flex;align-items:center;gap:4px;padding:2px 0}.typing-dots i[data-v-7317da3a]{width:6px;height:6px;border-radius:50%;background:var(--el-color-primary);animation:typing-bounce-7317da3a 1.2s ease-in-out infinite}.typing-dots i[data-v-7317da3a]:nth-child(2){animation-delay:.15s}.typing-dots i[data-v-7317da3a]:nth-child(3){animation-delay:.3s}@keyframes typing-bounce-7317da3a{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}.message-error[data-v-7317da3a]{margin-top:4px;font-size:12px;color:var(--el-color-danger)}.message-reasoning[data-v-7317da3a]{margin-bottom:8px}.reasoning-toggle[data-v-7317da3a]{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border:1px solid var(--el-border-color);border-radius:999px;background:var(--el-fill-color);color:var(--el-text-color-regular);font-size:12px;line-height:1;cursor:pointer;transition:all .2s}.reasoning-toggle[data-v-7317da3a]:hover{background:var(--el-fill-color-light);color:var(--el-color-primary);border-color:var(--el-color-primary-light-5)}.reasoning-toggle__arrow[data-v-7317da3a]{display:inline-block;font-size:12px;transition:transform .2s}.reasoning-toggle__arrow.open[data-v-7317da3a]{transform:rotate(180deg)}.reasoning-content[data-v-7317da3a]{margin:6px 0 0;padding:8px;background:var(--el-fill-color);border-radius:6px;font-size:12px;white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto}.message-files[data-v-7317da3a]{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.message-file[data-v-7317da3a]{display:flex;align-items:center;gap:8px;padding:6px 10px;border:1px solid var(--el-border-color);border-radius:8px;background:var(--el-bg-color);font-size:12px}.message-file.is-image[data-v-7317da3a]{padding:4px;flex-direction:column;align-items:flex-start}.file-image[data-v-7317da3a]{max-width:200px;max-height:200px;border-radius:6px;object-fit:contain;cursor:zoom-in;transition:box-shadow .2s}.file-image[data-v-7317da3a]:hover{box-shadow:0 0 0 2px var(--el-color-primary-light-5)}.file-info[data-v-7317da3a]{display:flex;flex-direction:column;gap:2px}.file-name[data-v-7317da3a]{color:var(--el-text-color-primary);font-weight:500;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size[data-v-7317da3a]{color:var(--el-text-color-secondary);font-size:11px}.chat-input[data-v-e7db63f0]{padding:12px 16px;border-top:1px solid var(--el-border-color-lighter);background:var(--el-bg-color);display:flex;flex-direction:column}.chat-input__files[data-v-e7db63f0]{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.chat-input__preview[data-v-e7db63f0]{margin-left:auto;height:24px;padding:0 8px}.chat-input__files[data-v-e7db63f0] .el-tag{border-radius:999px}.chat-input__send.el-button--primary[data-v-e7db63f0]{background:linear-gradient(135deg,#009688,#2d8cf0);border-color:transparent;color:#fff;transition:box-shadow .2s,transform .2s}.chat-input__send.el-button--primary[data-v-e7db63f0]:hover{background:linear-gradient(135deg,#00807a,#256fce);box-shadow:0 4px 12px #2d8cf059;transform:translateY(-1px)}.chat-input__wrap[data-v-e7db63f0]{display:flex;align-items:flex-end;gap:8px;background:var(--el-fill-color-light);border:1px solid var(--el-border-color);border-radius:12px;padding:6px 6px 6px 12px;transition:border-color .2s,box-shadow .2s}.chat-input__wrap[data-v-e7db63f0]:focus-within{border-color:var(--el-color-primary);box-shadow:0 0 0 2px var(--el-color-primary-light-8)}.chat-input__wrap[data-v-e7db63f0] .el-textarea__inner{border:none;background:transparent;box-shadow:none;padding:4px 0;line-height:1.6;font-size:14px}.chat-input__wrap[data-v-e7db63f0] .el-textarea__inner:focus{box-shadow:none}.chat-input__actions[data-v-e7db63f0]{flex-shrink:0;display:flex;align-items:center;gap:2px;padding-bottom:2px}.form-tip[data-v-6282664f]{font-size:12px;color:var(--el-text-color-secondary);line-height:1.6;margin-top:4px}.form-tip-inline[data-v-6282664f]{margin-left:8px;font-size:12px;color:var(--el-text-color-secondary)}.temp-row[data-v-6282664f]{display:flex;align-items:center;gap:12px;width:100%}.temp-val[data-v-6282664f]{width:28px;text-align:right;font-size:13px;color:var(--el-text-color-secondary)}.chat-panel-root[data-v-5dde3b27]{position:fixed;z-index:1999}.ai-fab[data-v-5dde3b27]{position:fixed;right:22px;bottom:25px;z-index:2000;display:flex;align-items:center;gap:8px;height:52px;padding:0 22px 0 18px;border-radius:26px;color:#fff;font-size:15px;font-weight:600;cursor:pointer;background:linear-gradient(135deg,#009688,#2d8cf0);box-shadow:0 6px 24px #2d8cf080;transition:transform .25s cubic-bezier(.34,1.56,.64,1),box-shadow .25s;-webkit-user-select:none;user-select:none;overflow:hidden;isolation:isolate;animation:ai-fab-float-5dde3b27 3.6s ease-in-out infinite}@keyframes ai-fab-float-5dde3b27{0%,to{transform:translateY(0)}50%{transform:translateY(-6px)}}.ai-fab[data-v-5dde3b27]:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;background:linear-gradient(105deg,transparent 30%,rgba(255,255,255,.25) 50%,transparent 70%);background-size:200% 100%;background-position:100% 0;animation:ai-fab-shimmer-5dde3b27 4s ease-in-out infinite;z-index:-1;pointer-events:none}@keyframes ai-fab-shimmer-5dde3b27{0%,to{background-position:100% 0}50%{background-position:0 0}}.ai-fab[data-v-5dde3b27]:after{content:"";position:absolute;top:-4px;right:-4px;bottom:-4px;left:-4px;border-radius:inherit;border:2px solid rgba(45,140,240,.35);animation:ai-fab-pulse-5dde3b27 2.4s ease-in-out infinite;pointer-events:none;z-index:-2}@keyframes ai-fab-pulse-5dde3b27{0%,to{opacity:.6;transform:scale(1)}50%{opacity:0;transform:scale(1.12)}}.ai-fab__icon[data-v-5dde3b27]{font-size:22px;filter:drop-shadow(0 0 4px rgba(255,255,255,.4));transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.ai-fab__label[data-v-5dde3b27]{line-height:1;text-shadow:0 1px 4px rgba(0,0,0,.15)}.ai-fab[data-v-5dde3b27]:hover{animation:none;transform:translateY(-4px) scale(1.04);box-shadow:0 12px 32px #2d8cf099}.ai-fab[data-v-5dde3b27]:hover:before{animation:none;background-position:0 0}.ai-fab--active[data-v-5dde3b27]{animation:none;box-shadow:0 4px 16px #2d8cf066}.ai-fab--active[data-v-5dde3b27]:after{animation:none;opacity:0}.ai-fab--active[data-v-5dde3b27]:before{animation:none}.ai-fab--active .ai-fab__icon[data-v-5dde3b27]{transform:rotate(45deg)}.ai-fab--dragged[data-v-5dde3b27]{animation:none}.ai-fab--dragged[data-v-5dde3b27]:before,.ai-fab--dragged[data-v-5dde3b27]:after{animation:none;opacity:0}.chat-panel[data-v-5dde3b27]{position:fixed;bottom:25px;right:22px;z-index:9999;display:flex;flex-direction:column;height:680px;width:560px;min-height:400px;background:var(--el-bg-color);box-shadow:0 20px 50px #0000002e,0 6px 18px #00000014;border-radius:16px;transform-origin:bottom right}.panel-enter-active[data-v-5dde3b27],.panel-leave-active[data-v-5dde3b27]{transition:opacity .3s ease,transform .3s cubic-bezier(.34,1.56,.64,1)}.panel-enter-from[data-v-5dde3b27],.panel-leave-to[data-v-5dde3b27]{opacity:0;transform:translateY(16px) scale(.96)}.chat-panel__header[data-v-5dde3b27]{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;cursor:move;-webkit-user-select:none;user-select:none;border-bottom:1px solid var(--el-border-color-lighter);background:linear-gradient(135deg,var(--el-color-primary-light-7),var(--el-bg-color));border-radius:16px 16px 0 0;flex-shrink:0}.chat-panel__title[data-v-5dde3b27]{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:600;color:var(--el-text-color-primary)}.title-icon[data-v-5dde3b27]{color:var(--el-color-primary);font-size:18px}.chat-panel__ops[data-v-5dde3b27]{display:flex;gap:4px}.chat-panel__scroll-wrap[data-v-5dde3b27]{position:relative;flex:1;min-height:0;display:flex}.chat-panel__body[data-v-5dde3b27]{flex:1;overflow-y:auto;padding:0 16px;min-height:0;scrollbar-width:thin;scrollbar-color:var(--el-border-color-darker) transparent}.chat-panel__nav[data-v-5dde3b27]{position:absolute;right:6px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;align-items:center;gap:6px;padding:6px 4px;background:var(--el-bg-color);border:1px solid var(--el-border-color-lighter);border-radius:10px;box-shadow:0 2px 10px #00000014;opacity:.35;transition:opacity .2s}.chat-panel__nav[data-v-5dde3b27]:hover,.chat-panel__nav.is-suppressed-scroll[data-v-5dde3b27]{opacity:1}.chat-panel__nav-count[data-v-5dde3b27]{font-size:10px;line-height:1;color:var(--el-text-color-secondary);font-variant-numeric:tabular-nums}.chat-panel__nav-btn[data-v-5dde3b27]{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:none;border-radius:8px;background:transparent;color:var(--el-text-color-regular);cursor:pointer;font-size:14px;transition:background .15s,color .15s}.chat-panel__nav-btn[data-v-5dde3b27]:hover:not(:disabled){background:var(--el-color-primary-light-9);color:var(--el-color-primary)}.chat-panel__nav-btn[data-v-5dde3b27]:disabled{color:var(--el-text-color-placeholder);cursor:not-allowed;opacity:.5}.chat-panel__body[data-v-5dde3b27] [data-msg-id].is-locate{animation:locate-flash-5dde3b27 1.2s ease both}@keyframes locate-flash-5dde3b27{0%{outline:2px solid var(--el-color-primary);outline-offset:2px;border-radius:8px}to{outline:2px solid transparent;outline-offset:2px;border-radius:8px}}.chat-panel__body[data-v-5dde3b27]::-webkit-scrollbar{width:8px}.chat-panel__body[data-v-5dde3b27]::-webkit-scrollbar-track{background:var(--el-fill-color-lighter);border-radius:4px}.chat-panel__body[data-v-5dde3b27]::-webkit-scrollbar-thumb{background:var(--el-border-color-darker);border-radius:4px;border:2px solid transparent;background-clip:content-box}.chat-panel__body[data-v-5dde3b27]::-webkit-scrollbar-thumb:hover{background:var(--el-text-color-secondary);background-clip:content-box}.chat-empty[data-v-5dde3b27]{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:14px}.chat-empty__logo[data-v-5dde3b27]{width:64px;height:64px;border-radius:18px;background:linear-gradient(135deg,#009688,#2d8cf0);color:#fff;display:flex;align-items:center;justify-content:center;font-size:32px;box-shadow:0 8px 24px #2d8cf04d}.chat-empty h3[data-v-5dde3b27]{margin:0;font-size:18px;color:var(--el-text-color-primary)}.chat-empty__desc[data-v-5dde3b27]{margin:0;font-size:13px;color:var(--el-text-color-secondary);line-height:1.6}.chat-empty__desc .hl[data-v-5dde3b27]{color:var(--el-color-primary);font-weight:600}.chat-empty__commands[data-v-5dde3b27]{display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.chat-empty__commands .el-button[data-v-5dde3b27]{margin-left:0}.chat-panel__footer[data-v-5dde3b27]{flex-shrink:0}.chat-panel__stop[data-v-5dde3b27]{display:flex;justify-content:center;padding:6px 0 0}.chat-panel__error[data-v-5dde3b27]{padding:8px 16px 0}.fade-enter-active[data-v-5dde3b27],.fade-leave-active[data-v-5dde3b27]{transition:opacity .2s}.fade-enter-from[data-v-5dde3b27],.fade-leave-to[data-v-5dde3b27]{opacity:0}.chat-panel--fullscreen[data-v-5dde3b27]{position:fixed;top:5%;right:10%;bottom:5%;left:10%;z-index:9999;height:90vh!important;width:80%!important}
1
+ .render-block[data-v-d7f1b4ad]{margin:8px 0;padding:8px 0;width:100%}.render-block__component[data-v-d7f1b4ad]{border-radius:8px;background:var(--el-fill-color-lighter);box-shadow:0 0 10px #0000001a}.render-block__title[data-v-d7f1b4ad]{font-weight:600;font-size:14px;margin-bottom:6px;color:var(--el-text-color-primary)}.render-block__table[data-v-d7f1b4ad]{width:100%;max-width:900px;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px #0000000f}.render-block__cards[data-v-d7f1b4ad]{display:flex;flex-direction:column;gap:8px}.card-item[data-v-d7f1b4ad]{border:1px solid var(--el-border-color);border-radius:8px;padding:10px 14px;background:var(--el-fill-color-lighter);box-shadow:0 2px 6px #0000000d}.card-field[data-v-d7f1b4ad]{display:flex;gap:6px;font-size:13px;line-height:1.8}.card-label[data-v-d7f1b4ad]{font-weight:600;color:var(--el-text-color-secondary);min-width:80px}.card-value[data-v-d7f1b4ad]{color:var(--el-text-color-primary)}.render-block__list[data-v-d7f1b4ad]{display:flex;flex-direction:column;gap:4px}.list-item[data-v-d7f1b4ad]{padding:6px 10px;font-size:13px;border-left:3px solid var(--el-color-primary);background:var(--el-fill-color-lighter);border-radius:0 4px 4px 0;box-shadow:0 1px 4px #0000000a}.suggested-next[data-v-0e0e9b65]{display:flex;align-items:center;gap:8px;padding:8px 12px;margin:6px 0;background:var(--el-color-primary-light-9);border:1px solid var(--el-color-primary-light-7);border-radius:8px;font-size:13px}.suggested-next__icon[data-v-0e0e9b65]{font-size:15px;flex-shrink:0}.suggested-next__text[data-v-0e0e9b65]{flex:1;color:var(--el-text-color-regular)}.chat-message[data-v-25c0f8d1]{display:flex;gap:10px;padding:12px 0;animation:msg-in-25c0f8d1 .25s ease both}@keyframes msg-in-25c0f8d1{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-message.role-user[data-v-25c0f8d1]{flex-direction:row-reverse}.chat-message.role-user .message-time[data-v-25c0f8d1]{text-align:right}.message-avatar[data-v-25c0f8d1]{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px;color:#fff;box-shadow:0 0 0 2px var(--el-bg-color),0 2px 6px #0000001f}.role-user .message-avatar[data-v-25c0f8d1]{background:linear-gradient(135deg,var(--el-color-primary),var(--el-color-primary-light-3))}.role-assistant .message-avatar[data-v-25c0f8d1]{background:linear-gradient(135deg,#009688,#2d8cf0)}.message-time[data-v-25c0f8d1]{display:block;font-size:11px;line-height:1;color:var(--el-text-color-placeholder);margin-bottom:4px}.message-body[data-v-25c0f8d1]{max-width:80%;min-width:0}.role-user .message-body[data-v-25c0f8d1]{background:linear-gradient(135deg,var(--el-color-primary-light-9),var(--el-color-primary-light-8));padding:8px 14px;border-radius:12px 4px 12px 12px;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a}.role-assistant .message-body[data-v-25c0f8d1]{position:relative;background:var(--el-fill-color-lighter);padding:8px 14px;border-radius:4px 12px 12px;display:flex;flex-direction:column;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a}.message-actions[data-v-25c0f8d1]{position:absolute;top:0;right:0;opacity:0;transition:opacity .2s;z-index:1}.role-assistant .message-body:hover .message-actions[data-v-25c0f8d1]{opacity:1}.role-assistant .message-body .message-time[data-v-25c0f8d1]{float:left;width:10px}.message-action-btn[data-v-25c0f8d1]{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;color:var(--el-text-color-secondary);cursor:pointer;font-size:15px}.message-action-btn[data-v-25c0f8d1]:hover{background:var(--el-fill-color);color:var(--el-color-primary)}.message-user-text[data-v-25c0f8d1]{font-size:14px;line-height:1.5;color:var(--el-text-color-primary);white-space:pre-wrap;word-break:break-word}.message-content[data-v-25c0f8d1]{font-size:14px;line-height:1.6;color:var(--el-text-color-primary);word-break:break-word;background:transparent;border:none;outline:none;box-shadow:none;height:auto;overflow-wrap:break-word;min-height:8px!important}.message-content[data-v-25c0f8d1] p{margin:0 0 8px}.message-content[data-v-25c0f8d1] p:last-child{margin-bottom:0}.message-content[data-v-25c0f8d1] h1,.message-content[data-v-25c0f8d1] h2,.message-content[data-v-25c0f8d1] h3,.message-content[data-v-25c0f8d1] h4,.message-content[data-v-25c0f8d1] h5,.message-content[data-v-25c0f8d1] h6{margin:12px 0 8px;font-weight:600;line-height:1.4;color:var(--el-text-color-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.message-content[data-v-25c0f8d1] h1[data-collapsed]:after,.message-content[data-v-25c0f8d1] h2[data-collapsed]:after,.message-content[data-v-25c0f8d1] h3[data-collapsed]:after,.message-content[data-v-25c0f8d1] h4[data-collapsed]:after,.message-content[data-v-25c0f8d1] h5[data-collapsed]:after,.message-content[data-v-25c0f8d1] h6[data-collapsed]:after{content:" ▸";font-size:.8em;color:var(--el-text-color-secondary)}.message-content[data-v-25c0f8d1] h1{font-size:20px}.message-content[data-v-25c0f8d1] h2{font-size:18px;padding-bottom:4px;border-bottom:1px solid var(--el-border-color-lighter)}.message-content[data-v-25c0f8d1] h3{font-size:16px}.message-content[data-v-25c0f8d1] h4{font-size:15px}.message-content[data-v-25c0f8d1] h5,.message-content[data-v-25c0f8d1] h6{font-size:14px}.message-content[data-v-25c0f8d1] strong{font-weight:600}.message-content[data-v-25c0f8d1] em{font-style:italic}.message-content[data-v-25c0f8d1] ul,.message-content[data-v-25c0f8d1] ol{margin:0 0 8px;padding-left:20px}.message-content[data-v-25c0f8d1] ul:last-child,.message-content[data-v-25c0f8d1] ol:last-child{margin-bottom:0}.message-content[data-v-25c0f8d1] li{margin:2px 0}.message-content[data-v-25c0f8d1] li>ul,.message-content[data-v-25c0f8d1] li>ol{margin:0}.message-content[data-v-25c0f8d1] code{padding:1px 5px;margin:0 2px;font-size:12px;font-family:Consolas,Monaco,Courier New,monospace;background:var(--el-fill-color);color:var(--el-color-primary);border-radius:4px}.message-content[data-v-25c0f8d1] pre{margin:8px 0;padding:10px 12px;background:var(--el-bg-color);border:1px solid var(--el-border-color-lighter);border-radius:6px;overflow-x:auto}.message-content[data-v-25c0f8d1] .md-code-block{margin:8px 0}.message-content[data-v-25c0f8d1] .md-code-block__head{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:var(--el-fill-color);border:1px solid var(--el-border-color-lighter);border-bottom:none;border-radius:6px 6px 0 0;font-size:12px}.message-content[data-v-25c0f8d1] .md-code-block__lang{color:var(--el-text-color-secondary);font-family:Consolas,Monaco,Courier New,monospace}.message-content[data-v-25c0f8d1] .md-code-block__copy{color:var(--el-color-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .2s}.message-content[data-v-25c0f8d1] .md-code-block__copy:hover{opacity:.7}.message-content[data-v-25c0f8d1] .md-code-block pre{margin:0;border-radius:0 0 6px 6px}.message-content[data-v-25c0f8d1] pre code{display:block;padding:0;margin:0;font-size:12px;line-height:1.6;background:transparent;color:var(--el-text-color-primary);border-radius:0;white-space:pre}.message-content[data-v-25c0f8d1] blockquote{margin:8px 0;padding:4px 12px;border-left:3px solid var(--el-color-primary-light-5);background:var(--el-fill-color-lighter);border-radius:0 6px 6px 0;color:var(--el-text-color-regular)}.message-content[data-v-25c0f8d1] blockquote p{margin:0}.message-content[data-v-25c0f8d1] table{width:100%;margin:8px 0;border-collapse:collapse;font-size:13px}.message-content[data-v-25c0f8d1] th,.message-content[data-v-25c0f8d1] td{padding:6px 10px;border:1px solid var(--el-border-color-lighter);text-align:left}.message-content[data-v-25c0f8d1] th{background:var(--el-fill-color);font-weight:600;color:var(--el-text-color-primary)}.message-content[data-v-25c0f8d1] tbody tr:nth-child(2n){background:var(--el-fill-color-lighter)}.message-content.is-table-clickable[data-v-25c0f8d1] tbody tr{cursor:pointer;transition:background .15s}.message-content.is-table-clickable[data-v-25c0f8d1] tbody tr:hover{background:var(--el-color-primary-light-9)}.message-content.is-list-clickable[data-v-25c0f8d1] li{cursor:pointer;border-radius:4px;transition:background .15s}.message-content.is-list-clickable[data-v-25c0f8d1] li:hover{background:var(--el-color-primary-light-9)}.message-content[data-v-25c0f8d1] a{color:var(--el-color-primary);text-decoration:none}.message-content[data-v-25c0f8d1] a:hover{text-decoration:underline}.message-content[data-v-25c0f8d1] img{max-width:100%;border-radius:6px}.message-content[data-v-25c0f8d1] hr{margin:12px 0;border:none;border-top:1px solid var(--el-border-color-lighter)}.typing-dots[data-v-25c0f8d1]{display:inline-flex;align-items:center;gap:4px;padding:2px 0}.typing-dots i[data-v-25c0f8d1]{width:6px;height:6px;border-radius:50%;background:var(--el-color-primary);animation:typing-bounce-25c0f8d1 1.2s ease-in-out infinite}.typing-dots i[data-v-25c0f8d1]:nth-child(2){animation-delay:.15s}.typing-dots i[data-v-25c0f8d1]:nth-child(3){animation-delay:.3s}@keyframes typing-bounce-25c0f8d1{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}.message-error[data-v-25c0f8d1]{margin-top:4px;font-size:12px;color:var(--el-color-danger)}.message-reasoning[data-v-25c0f8d1]{margin-bottom:8px}.reasoning-toggle[data-v-25c0f8d1]{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border:1px solid var(--el-border-color);border-radius:999px;background:var(--el-fill-color);color:var(--el-text-color-regular);font-size:12px;line-height:1;cursor:pointer;transition:all .2s}.reasoning-toggle[data-v-25c0f8d1]:hover{background:var(--el-fill-color-light);color:var(--el-color-primary);border-color:var(--el-color-primary-light-5)}.reasoning-toggle__arrow[data-v-25c0f8d1]{display:inline-block;font-size:12px;transition:transform .2s}.reasoning-toggle__arrow.open[data-v-25c0f8d1]{transform:rotate(180deg)}.reasoning-content[data-v-25c0f8d1]{margin:6px 0 0;padding:8px;background:var(--el-fill-color);border-radius:6px;font-size:12px;white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto}.message-tools[data-v-25c0f8d1]{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.message-tool[data-v-25c0f8d1]{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--el-border-color-lighter);border-radius:999px;background:var(--el-bg-color);color:var(--el-text-color-regular);font-size:12px;line-height:1.4}.message-tool.is-running[data-v-25c0f8d1]{border-color:var(--el-color-primary-light-5);color:var(--el-color-primary)}.message-tool.is-success[data-v-25c0f8d1]{border-color:var(--el-color-success-light-5);color:var(--el-color-success)}.message-tool.is-error[data-v-25c0f8d1]{border-color:var(--el-color-danger-light-5);color:var(--el-color-danger)}.message-tool.is-blocked[data-v-25c0f8d1]{border-color:var(--el-color-warning-light-5);color:var(--el-color-warning)}.message-tool__icon[data-v-25c0f8d1]{font-size:13px}.message-tool__status[data-v-25c0f8d1]{color:var(--el-text-color-secondary)}.message-tool.is-running .message-tool__status[data-v-25c0f8d1],.message-tool.is-success .message-tool__status[data-v-25c0f8d1],.message-tool.is-error .message-tool__status[data-v-25c0f8d1],.message-tool.is-blocked .message-tool__status[data-v-25c0f8d1]{color:inherit}.message-tool__icon.is-spinning[data-v-25c0f8d1]{animation:tool-spin-25c0f8d1 1s linear infinite}@keyframes tool-spin-25c0f8d1{to{transform:rotate(360deg)}}.message-files[data-v-25c0f8d1]{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.message-file[data-v-25c0f8d1]{display:flex;align-items:center;gap:8px;padding:6px 10px;border:1px solid var(--el-border-color);border-radius:8px;background:var(--el-bg-color);font-size:12px}.message-file.is-image[data-v-25c0f8d1]{padding:4px;flex-direction:column;align-items:flex-start}.file-image[data-v-25c0f8d1]{max-width:200px;max-height:200px;border-radius:6px;object-fit:contain;cursor:zoom-in;transition:box-shadow .2s}.file-image[data-v-25c0f8d1]:hover{box-shadow:0 0 0 2px var(--el-color-primary-light-5)}.file-info[data-v-25c0f8d1]{display:flex;flex-direction:column;gap:2px}.file-name[data-v-25c0f8d1]{color:var(--el-text-color-primary);font-weight:500;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size[data-v-25c0f8d1]{color:var(--el-text-color-secondary);font-size:11px}.chat-input[data-v-e7db63f0]{padding:12px 16px;border-top:1px solid var(--el-border-color-lighter);background:var(--el-bg-color);display:flex;flex-direction:column}.chat-input__files[data-v-e7db63f0]{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.chat-input__preview[data-v-e7db63f0]{margin-left:auto;height:24px;padding:0 8px}.chat-input__files[data-v-e7db63f0] .el-tag{border-radius:999px}.chat-input__send.el-button--primary[data-v-e7db63f0]{background:linear-gradient(135deg,#009688,#2d8cf0);border-color:transparent;color:#fff;transition:box-shadow .2s,transform .2s}.chat-input__send.el-button--primary[data-v-e7db63f0]:hover{background:linear-gradient(135deg,#00807a,#256fce);box-shadow:0 4px 12px #2d8cf059;transform:translateY(-1px)}.chat-input__wrap[data-v-e7db63f0]{display:flex;align-items:flex-end;gap:8px;background:var(--el-fill-color-light);border:1px solid var(--el-border-color);border-radius:12px;padding:6px 6px 6px 12px;transition:border-color .2s,box-shadow .2s}.chat-input__wrap[data-v-e7db63f0]:focus-within{border-color:var(--el-color-primary);box-shadow:0 0 0 2px var(--el-color-primary-light-8)}.chat-input__wrap[data-v-e7db63f0] .el-textarea__inner{border:none;background:transparent;box-shadow:none;padding:4px 0;line-height:1.6;font-size:14px}.chat-input__wrap[data-v-e7db63f0] .el-textarea__inner:focus{box-shadow:none}.chat-input__actions[data-v-e7db63f0]{flex-shrink:0;display:flex;align-items:center;gap:2px;padding-bottom:2px}.form-tip[data-v-6282664f]{font-size:12px;color:var(--el-text-color-secondary);line-height:1.6;margin-top:4px}.form-tip-inline[data-v-6282664f]{margin-left:8px;font-size:12px;color:var(--el-text-color-secondary)}.temp-row[data-v-6282664f]{display:flex;align-items:center;gap:12px;width:100%}.temp-val[data-v-6282664f]{width:28px;text-align:right;font-size:13px;color:var(--el-text-color-secondary)}.session-drawer[data-v-42d4ed19]{display:flex;flex-direction:column;gap:12px;height:100%}.session-drawer__new[data-v-42d4ed19]{width:100%}.session-drawer__loading[data-v-42d4ed19]{padding:24px 0;text-align:center;font-size:13px;color:var(--el-text-color-secondary)}.session-list[data-v-42d4ed19]{display:flex;flex-direction:column;gap:6px;overflow-y:auto;padding-right:2px}.session-item[data-v-42d4ed19]{display:flex;align-items:center;gap:6px;padding:8px 10px;border:1px solid var(--el-border-color-lighter);border-radius:8px;cursor:pointer;transition:background .15s,border-color .15s}.session-item[data-v-42d4ed19]:hover{background:var(--el-fill-color-light)}.session-item.is-active[data-v-42d4ed19]{border-color:var(--el-color-primary-light-5);background:var(--el-color-primary-light-9)}.session-item__body[data-v-42d4ed19]{flex:1;min-width:0}.session-item__title[data-v-42d4ed19]{font-size:13px;font-weight:600;color:var(--el-text-color-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-item__meta[data-v-42d4ed19]{display:flex;gap:8px;margin-top:2px;font-size:11px;color:var(--el-text-color-secondary)}.session-item__del[data-v-42d4ed19]{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--el-text-color-secondary);cursor:pointer;opacity:.6;transition:background .15s,color .15s,opacity .15s}.session-item__del[data-v-42d4ed19]:hover{opacity:1;color:var(--el-color-danger);background:var(--el-color-danger-light-9)}.ai-selection-send[data-v-a4fae753]{position:fixed;z-index:10000;display:inline-flex;align-items:center;gap:4px;height:30px;padding:0 12px;border:none;border-radius:15px;background:linear-gradient(135deg,#009688,#2d8cf0);color:#fff;font-size:12px;font-weight:600;line-height:1;cursor:pointer;-webkit-user-select:none;user-select:none;box-shadow:0 4px 14px #2d8cf073;transition:transform .15s,box-shadow .15s,opacity .15s}.ai-selection-send[data-v-a4fae753]:hover:not(.is-disabled){transform:translateY(-1px);box-shadow:0 8px 20px #2d8cf08c}.ai-selection-send.is-disabled[data-v-a4fae753]{opacity:.55;cursor:not-allowed}.ai-selection-send__icon[data-v-a4fae753]{font-size:14px}.sel-send-fade-enter-active[data-v-a4fae753],.sel-send-fade-leave-active[data-v-a4fae753]{transition:opacity .12s ease}.sel-send-fade-enter-from[data-v-a4fae753],.sel-send-fade-leave-to[data-v-a4fae753]{opacity:0}.chat-panel-root[data-v-597f8f19]{position:fixed;z-index:1999}.ai-fab[data-v-597f8f19]{position:fixed;right:22px;bottom:25px;z-index:2000;display:flex;align-items:center;gap:8px;height:52px;padding:0 22px 0 18px;border-radius:26px;color:#fff;font-size:15px;font-weight:600;cursor:pointer;background:linear-gradient(135deg,#009688,#2d8cf0);box-shadow:0 6px 24px #2d8cf080;transition:transform .25s cubic-bezier(.34,1.56,.64,1),box-shadow .25s;-webkit-user-select:none;user-select:none;overflow:hidden;isolation:isolate;animation:ai-fab-float-597f8f19 3.6s ease-in-out infinite}@keyframes ai-fab-float-597f8f19{0%,to{transform:translateY(0)}50%{transform:translateY(-6px)}}.ai-fab[data-v-597f8f19]:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;background:linear-gradient(105deg,transparent 30%,rgba(255,255,255,.25) 50%,transparent 70%);background-size:200% 100%;background-position:100% 0;animation:ai-fab-shimmer-597f8f19 4s ease-in-out infinite;z-index:-1;pointer-events:none}@keyframes ai-fab-shimmer-597f8f19{0%,to{background-position:100% 0}50%{background-position:0 0}}.ai-fab[data-v-597f8f19]:after{content:"";position:absolute;top:-4px;right:-4px;bottom:-4px;left:-4px;border-radius:inherit;border:2px solid rgba(45,140,240,.35);animation:ai-fab-pulse-597f8f19 2.4s ease-in-out infinite;pointer-events:none;z-index:-2}@keyframes ai-fab-pulse-597f8f19{0%,to{opacity:.6;transform:scale(1)}50%{opacity:0;transform:scale(1.12)}}.ai-fab__icon[data-v-597f8f19]{font-size:22px;filter:drop-shadow(0 0 4px rgba(255,255,255,.4));transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.ai-fab__label[data-v-597f8f19]{line-height:1;text-shadow:0 1px 4px rgba(0,0,0,.15)}.ai-fab[data-v-597f8f19]:hover{animation:none;transform:translateY(-4px) scale(1.04);box-shadow:0 12px 32px #2d8cf099}.ai-fab[data-v-597f8f19]:hover:before{animation:none;background-position:0 0}.ai-fab--active[data-v-597f8f19]{animation:none;box-shadow:0 4px 16px #2d8cf066}.ai-fab--active[data-v-597f8f19]:after{animation:none;opacity:0}.ai-fab--active[data-v-597f8f19]:before{animation:none}.ai-fab--active .ai-fab__icon[data-v-597f8f19]{transform:rotate(45deg)}.ai-fab--dragged[data-v-597f8f19]{animation:none}.ai-fab--dragged[data-v-597f8f19]:before,.ai-fab--dragged[data-v-597f8f19]:after{animation:none;opacity:0}.chat-panel[data-v-597f8f19]{position:fixed;bottom:25px;right:22px;z-index:9999;display:flex;flex-direction:column;height:680px;width:560px;min-height:400px;background:var(--el-bg-color);box-shadow:0 20px 50px #0000002e,0 6px 18px #00000014;border-radius:16px;transform-origin:bottom right}.panel-enter-active[data-v-597f8f19],.panel-leave-active[data-v-597f8f19]{transition:opacity .3s ease,transform .3s cubic-bezier(.34,1.56,.64,1)}.panel-enter-from[data-v-597f8f19],.panel-leave-to[data-v-597f8f19]{opacity:0;transform:translateY(16px) scale(.96)}.chat-panel__header[data-v-597f8f19]{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;cursor:move;-webkit-user-select:none;user-select:none;border-bottom:1px solid var(--el-border-color-lighter);background:linear-gradient(135deg,var(--el-color-primary-light-7),var(--el-bg-color));border-radius:16px 16px 0 0;flex-shrink:0}.chat-panel__title[data-v-597f8f19]{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:600;color:var(--el-text-color-primary)}.title-icon[data-v-597f8f19]{color:var(--el-color-primary);font-size:18px}.chat-panel__ops[data-v-597f8f19]{display:flex;gap:4px}.chat-panel__scroll-wrap[data-v-597f8f19]{position:relative;flex:1;min-height:0;display:flex}.chat-panel__body[data-v-597f8f19]{flex:1;overflow-y:auto;padding:0 16px;min-height:0;scrollbar-width:thin;scrollbar-color:var(--el-border-color-darker) transparent}.chat-panel__nav[data-v-597f8f19]{position:absolute;right:6px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;align-items:center;gap:6px;padding:6px 4px;background:var(--el-bg-color);border:1px solid var(--el-border-color-lighter);border-radius:10px;box-shadow:0 2px 10px #00000014;opacity:.35;transition:opacity .2s}.chat-panel__nav[data-v-597f8f19]:hover,.chat-panel__nav.is-suppressed-scroll[data-v-597f8f19]{opacity:1}.chat-panel__nav-count[data-v-597f8f19]{font-size:10px;line-height:1;color:var(--el-text-color-secondary);font-variant-numeric:tabular-nums}.chat-panel__nav-btn[data-v-597f8f19]{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:none;border-radius:8px;background:transparent;color:var(--el-text-color-regular);cursor:pointer;font-size:14px;transition:background .15s,color .15s}.chat-panel__nav-btn[data-v-597f8f19]:hover:not(:disabled){background:var(--el-color-primary-light-9);color:var(--el-color-primary)}.chat-panel__nav-btn[data-v-597f8f19]:disabled{color:var(--el-text-color-placeholder);cursor:not-allowed;opacity:.5}.chat-panel__body[data-v-597f8f19] [data-msg-id].is-locate{animation:locate-flash-597f8f19 1.2s ease both}@keyframes locate-flash-597f8f19{0%{outline:2px solid var(--el-color-primary);outline-offset:2px;border-radius:8px}to{outline:2px solid transparent;outline-offset:2px;border-radius:8px}}.chat-panel__body[data-v-597f8f19]::-webkit-scrollbar{width:8px}.chat-panel__body[data-v-597f8f19]::-webkit-scrollbar-track{background:var(--el-fill-color-lighter);border-radius:4px}.chat-panel__body[data-v-597f8f19]::-webkit-scrollbar-thumb{background:var(--el-border-color-darker);border-radius:4px;border:2px solid transparent;background-clip:content-box}.chat-panel__body[data-v-597f8f19]::-webkit-scrollbar-thumb:hover{background:var(--el-text-color-secondary);background-clip:content-box}.chat-empty[data-v-597f8f19]{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:14px}.chat-empty__logo[data-v-597f8f19]{width:64px;height:64px;border-radius:18px;background:linear-gradient(135deg,#009688,#2d8cf0);color:#fff;display:flex;align-items:center;justify-content:center;font-size:32px;box-shadow:0 8px 24px #2d8cf04d}.chat-empty h3[data-v-597f8f19]{margin:0;font-size:18px;color:var(--el-text-color-primary)}.chat-empty__desc[data-v-597f8f19]{margin:0;font-size:13px;color:var(--el-text-color-secondary);line-height:1.6}.chat-empty__desc .hl[data-v-597f8f19]{color:var(--el-color-primary);font-weight:600}.chat-empty__commands[data-v-597f8f19]{display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.chat-empty__commands .el-button[data-v-597f8f19]{margin-left:0}.chat-panel__footer[data-v-597f8f19]{flex-shrink:0}.chat-panel__stop[data-v-597f8f19]{display:flex;justify-content:center;padding:6px 0 0}.chat-panel__error[data-v-597f8f19]{padding:8px 16px 0}.fade-enter-active[data-v-597f8f19],.fade-leave-active[data-v-597f8f19]{transition:opacity .2s}.fade-enter-from[data-v-597f8f19],.fade-leave-to[data-v-597f8f19]{opacity:0}.chat-panel--fullscreen[data-v-597f8f19]{position:fixed;top:5%;right:10%;bottom:5%;left:10%;z-index:9999;height:90vh!important;width:80%!important}
@@ -1,11 +1,7 @@
1
- import { Component } from 'vue';
2
- import { ChatMessage } from '../types';
1
+ import { ChatMessage, ToolPackage } from '../types';
3
2
  type __VLS_Props = {
4
3
  message: ChatMessage;
5
- toolPackages: {
6
- id: string;
7
- components?: Record<string, Component>;
8
- }[];
4
+ toolPackages: ToolPackage[];
9
5
  mdTableClick?: boolean;
10
6
  mdListClick?: boolean;
11
7
  };
@@ -13,5 +9,5 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
13
9
  execute: (text: string) => any;
14
10
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
15
11
  onExecute?: (text: string) => any;
16
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
12
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
17
13
  export default _default;
@@ -1,20 +1,31 @@
1
- import { ChatMessage as ChatMessageType, ChatSettings as ChatSettingsType, ToolPackage, ChatCommand, FileAttachment } from '../types';
1
+ import { ChatMessage as ChatMessageType, ChatSettings as ChatSettingsType, SessionMeta, ToolPackage, ChatCommand, FileAttachment } from '../types';
2
2
  import { EventBus } from '../core/event-bus';
3
3
  export interface AgentChatAPI {
4
4
  messages: import('vue').Ref<ChatMessageType[]>;
5
5
  settings: import('vue').Ref<ChatSettingsType>;
6
6
  loading: import('vue').Ref<boolean>;
7
7
  errorMsg: import('vue').Ref<string>;
8
+ /** 会话记录同步(proxy 上传 / 本地存储)失败时的提示 */
9
+ syncError?: import('vue').Ref<string>;
8
10
  hasApiKey: import('vue').ComputedRef<boolean>;
9
11
  toolPackages: ToolPackage[];
10
12
  fileStore: Map<string, FileAttachment>;
11
13
  visible: import('vue').Ref<boolean>;
12
14
  eventBus: EventBus;
13
15
  deviceId: string;
16
+ /** 多会话(宿主自建实例可缺省) */
17
+ sessions?: import('vue').Ref<SessionMeta[]>;
18
+ currentSessionId?: import('vue').Ref<string>;
19
+ sessionsLoading?: import('vue').Ref<boolean>;
20
+ newSession?: () => Promise<void>;
21
+ switchSession?: (id: string) => Promise<void>;
22
+ deleteSession?: (id: string) => Promise<void>;
14
23
  sendMessage: (text: string, files?: FileAttachment[]) => Promise<void>;
15
24
  stopGeneration: () => void;
16
25
  clearMessages: () => void;
17
26
  saveSettings: (s: ChatSettingsType) => void;
27
+ /** 主动释放:停止业务事件引擎、待定监听与持久化定时器 */
28
+ dispose?: () => void;
18
29
  suggestedCommands: ChatCommand[];
19
30
  }
20
31
  type __VLS_Props = {
@@ -28,5 +39,35 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
28
39
  fabRef: HTMLDivElement;
29
40
  panelRef: HTMLDivElement;
30
41
  scrollRef: HTMLDivElement;
42
+ selectionSendRef: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
43
+ container: HTMLElement | null;
44
+ disabled?: boolean;
45
+ }> & Readonly<{
46
+ onSend?: (text: string) => any;
47
+ }>, {
48
+ hide: () => void;
49
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
50
+ send: (text: string) => any;
51
+ }, import('vue').PublicProps, {
52
+ disabled: boolean;
53
+ }, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
54
+ btnRef: HTMLButtonElement;
55
+ }, any, import('vue').ComponentProvideOptions, {
56
+ P: {};
57
+ B: {};
58
+ D: {};
59
+ C: {};
60
+ M: {};
61
+ Defaults: {};
62
+ }, Readonly<{
63
+ container: HTMLElement | null;
64
+ disabled?: boolean;
65
+ }> & Readonly<{
66
+ onSend?: (text: string) => any;
67
+ }>, {
68
+ hide: () => void;
69
+ }, {}, {}, {}, {
70
+ disabled: boolean;
71
+ }>;
31
72
  }, HTMLDivElement>;
32
73
  export default _default;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 选中文字后浮出「发送」按钮:
3
+ * - 只在 container(聊天面板消息区)内的选区才显示;
4
+ * - 点击发送把选中文本(trim)通过 send 事件交给宿主;
5
+ * - 滚动/点击别处/选区清空/面板关闭时自动隐藏。
6
+ */
7
+ type __VLS_Props = {
8
+ /** 允许触发选区的容器(通常是 .chat-panel__body) */
9
+ container: HTMLElement | null;
10
+ /** 生成中禁用发送 */
11
+ disabled?: boolean;
12
+ };
13
+ declare function hide(): void;
14
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {
15
+ hide: typeof hide;
16
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
17
+ send: (text: string) => any;
18
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ onSend?: (text: string) => any;
20
+ }>, {
21
+ disabled: boolean;
22
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
23
+ btnRef: HTMLButtonElement;
24
+ }, any>;
25
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import { SessionMeta } from '../types';
2
+ type __VLS_Props = {
3
+ modelValue: boolean;
4
+ sessions: SessionMeta[];
5
+ currentId?: string;
6
+ loading?: boolean;
7
+ };
8
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
9
+ select: (id: string) => any;
10
+ "update:modelValue": (value: boolean) => any;
11
+ create: () => any;
12
+ remove: (id: string) => any;
13
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onSelect?: (id: string) => any;
15
+ "onUpdate:modelValue"?: (value: boolean) => any;
16
+ onCreate?: () => any;
17
+ onRemove?: (id: string) => any;
18
+ }>, {
19
+ loading: boolean;
20
+ currentId: string;
21
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
22
+ export default _default;
@@ -1,5 +1,6 @@
1
1
  import { EventBus } from '../core/event-bus';
2
- import { AgentChatOptions, ChatMessage, ChatSettings, ChatCommand, ToolPackage, FileAttachment } from '../types';
2
+ import { AgentChatOptions, ChatMessage, ChatSettings, ChatCommand, SessionMeta, ToolPackage, FileAttachment } from '../types';
3
+ declare function setDeviceId(id: string): void;
3
4
  export declare function useAgentChat(options: AgentChatOptions): {
4
5
  messages: import('vue').Ref<{
5
6
  id: string;
@@ -48,10 +49,12 @@ export declare function useAgentChat(options: AgentChatOptions): {
48
49
  toolResults?: {
49
50
  callId: string;
50
51
  name: string;
52
+ alias?: string;
51
53
  arguments: string;
52
54
  result: string;
53
55
  success: boolean;
54
- status: "running" | "success" | "error";
56
+ status: "running" | "success" | "error" | "blocked";
57
+ hint?: string;
55
58
  }[];
56
59
  hideMarkdown?: boolean;
57
60
  }[], ChatMessage[] | {
@@ -101,10 +104,12 @@ export declare function useAgentChat(options: AgentChatOptions): {
101
104
  toolResults?: {
102
105
  callId: string;
103
106
  name: string;
107
+ alias?: string;
104
108
  arguments: string;
105
109
  result: string;
106
110
  success: boolean;
107
- status: "running" | "success" | "error";
111
+ status: "running" | "success" | "error" | "blocked";
112
+ hint?: string;
108
113
  }[];
109
114
  hideMarkdown?: boolean;
110
115
  }[]>;
@@ -139,15 +144,37 @@ export declare function useAgentChat(options: AgentChatOptions): {
139
144
  }>;
140
145
  loading: import('vue').Ref<boolean, boolean>;
141
146
  errorMsg: import('vue').Ref<string, string>;
147
+ syncError: import('vue').Ref<string, string>;
142
148
  hasApiKey: import('vue').ComputedRef<boolean>;
143
149
  toolPackages: ToolPackage[];
144
150
  fileStore: Map<string, FileAttachment>;
145
151
  visible: import('vue').Ref<boolean, boolean>;
146
152
  eventBus: EventBus;
147
153
  deviceId: string;
154
+ setDeviceId: typeof setDeviceId;
155
+ sessions: import('vue').Ref<{
156
+ id: string;
157
+ title: string;
158
+ createdAt: number;
159
+ updatedAt: number;
160
+ messageCount?: number;
161
+ }[], SessionMeta[] | {
162
+ id: string;
163
+ title: string;
164
+ createdAt: number;
165
+ updatedAt: number;
166
+ messageCount?: number;
167
+ }[]>;
168
+ currentSessionId: import('vue').Ref<string, string>;
169
+ sessionsLoading: import('vue').Ref<boolean, boolean>;
170
+ newSession: () => Promise<void>;
171
+ switchSession: (id: string) => Promise<void>;
172
+ deleteSession: (id: string) => Promise<void>;
148
173
  sendMessage: (text: string, files?: FileAttachment[]) => Promise<void>;
149
174
  stopGeneration: () => void;
150
175
  clearMessages: () => Promise<void>;
151
176
  saveSettings: (next: ChatSettings) => void;
177
+ dispose: () => void;
152
178
  suggestedCommands: ChatCommand[];
153
179
  };
180
+ export {};
@@ -2,3 +2,4 @@ import { ToolPackage } from '../types';
2
2
  export declare function buildPackageSection(pkg: ToolPackage): string;
3
3
  export declare function buildSystemPrompt(packages: ToolPackage[]): string;
4
4
  export declare const REPEAT_COMMAND_RULE = "--- \u91CD\u590D\u6307\u4EE4\u5904\u7406\u89C4\u5219 ---\n\u5982\u679C\u7528\u6237\u518D\u6B21\u53D1\u9001\u76F8\u540C\u6216\u7C7B\u4F3C\u7684\u6307\u4EE4\uFF08\u5373\u4F7F\u4E4B\u524D\u5DF2\u7ECF\u6267\u884C\u8FC7\uFF09\uFF0C\u5FC5\u987B\u91CD\u65B0\u8C03\u7528\u5BF9\u5E94\u7684\u5DE5\u5177\u518D\u6B21\u6267\u884C\u3002\n\u4E0D\u5F97\u4EE5\"\u5DF2\u7ECF\u6267\u884C\u8FC7\"\"\u4E4B\u524D\u505A\u8FC7\"\u7B49\u7406\u7531\u62D2\u7EDD\uFF0C\u4E5F\u4E0D\u80FD\u7528\u5386\u53F2\u4E2D\u7684\u5DE5\u5177\u7ED3\u679C\u4EE3\u66FF\u91CD\u65B0\u6267\u884C\u3002";
5
+ export declare const TOOL_REQUIREMENT_RULE = "--- \u5DE5\u5177\u524D\u7F6E\u6761\u4EF6\u89C4\u5219 ---\n1. \u8C03\u7528\u5DE5\u5177\u524D\u5148\u5BF9\u7167\u8BE5\u5DE5\u5177\u7684\"\u524D\u7F6E\u6761\u4EF6\"\uFF1B\u672A\u6EE1\u8DB3\u65F6\u5148\u8865\u9F50\uFF08\u8C03\u7528\u5EFA\u8BAE\u7684\u524D\u7F6E\u5DE5\u5177\uFF0C\u6216\u5411\u7528\u6237\u786E\u8BA4\uFF09\uFF0C\u4E0D\u8981\u76F4\u63A5\u8C03\u7528\u3002\n2. \u5982\u679C\u5DE5\u5177\u7ED3\u679C\u8FD4\u56DE {\"ok\":false,\"code\":\"REQUIREMENT_UNMET\"}\uFF0C\u7981\u6B62\u7F16\u9020\u6267\u884C\u7ED3\u679C\u3001\u7981\u6B62\u56DE\u590D\"\u5DF2\u5B8C\u6210\"\uFF1B\n \u5FC5\u987B\u6309 hint / suggestedTool \u8865\u9F50\u524D\u7F6E\u6761\u4EF6\u540E\u91CD\u8BD5\u540C\u4E00\u5DE5\u5177\u3002\n3. \u5C5E\u4E8E\"\u9700\u8981\u7528\u6237\u786E\u8BA4\"\u7684\u524D\u7F6E\u6761\u4EF6\u800C\u7528\u6237\u5C1A\u672A\u660E\u786E\u9009\u62E9\u65F6\uFF0C\u5FC5\u987B\u5148\u5411\u7528\u6237\u63D0\u95EE\uFF0C\u7981\u6B62\u81EA\u884C\u5047\u8BBE\u3002";
@@ -0,0 +1,21 @@
1
+ import { ChatMessage, SessionMeta } from '../types';
2
+ /**
3
+ * 会话存储抽象:
4
+ * - 直连模式 → createLocalSessionStore(localStorage)
5
+ * - 代理模式 → createProxySessionStore(server /api/sessions)
6
+ */
7
+ export interface SessionStore {
8
+ list(): Promise<SessionMeta[]>;
9
+ create(title: string): Promise<SessionMeta>;
10
+ load(id: string): Promise<ChatMessage[]>;
11
+ save(id: string, messages: ChatMessage[], patch?: {
12
+ title?: string;
13
+ }): Promise<SessionMeta | null>;
14
+ remove(id: string): Promise<void>;
15
+ }
16
+ /** 会话标题:首条用户消息前 24 字,其次文件名,最后回退"新对话" */
17
+ export declare function deriveSessionTitle(text: string, files?: Array<{
18
+ name?: string;
19
+ }>): string;
20
+ export declare function createLocalSessionStore(prefix: string): SessionStore;
21
+ export declare function createProxySessionStore(apiBase: string, deviceId: string): SessionStore;
@@ -0,0 +1,44 @@
1
+ import { ToolRequirement, ToolRequirementAction, ToolRequirementContext, ToolRequirementResult } from '../types';
2
+ /** 前置条件未满足的结构化错误码 */
3
+ export declare const REQUIREMENT_UNMET_CODE = "REQUIREMENT_UNMET";
4
+ export interface UnmetRequirement {
5
+ description: string;
6
+ check?: string;
7
+ message: string;
8
+ hint?: string;
9
+ suggestedTool?: string;
10
+ action: ToolRequirementAction;
11
+ }
12
+ export interface RequirementPayload {
13
+ ok: false;
14
+ code: typeof REQUIREMENT_UNMET_CODE;
15
+ tool: string;
16
+ requirements: Array<{
17
+ description: string;
18
+ check?: string;
19
+ message: string;
20
+ }>;
21
+ hint?: string;
22
+ suggestedTool?: string;
23
+ action: ToolRequirementAction;
24
+ retryable: true;
25
+ }
26
+ export type RequirementChecker = (ctx: ToolRequirementContext) => ToolRequirementResult | Promise<ToolRequirementResult>;
27
+ /** 单条 / 数组统一成数组 */
28
+ export declare function normalizeRequirements(input?: ToolRequirement | ToolRequirement[]): ToolRequirement[];
29
+ /**
30
+ * 评估前置条件。
31
+ * - 只声明了 tools 的条件是"提示词级建议",不拦截;
32
+ * - 声明了 check 的条件交给宿主 checker 判定(未提供 checker 时放行,由调用方告警);
33
+ * - checker 抛异常按未满足处理(fail-closed),避免半状态产生副作用。
34
+ */
35
+ export declare function evaluateRequirements(requirements: ToolRequirement[], ctx: {
36
+ sessionId: string;
37
+ packageId: string;
38
+ toolName: string;
39
+ args: Record<string, any>;
40
+ }, checker?: RequirementChecker): Promise<UnmetRequirement[]>;
41
+ /** 组装给模型看的结构化失败结果 */
42
+ export declare function buildRequirementPayload(toolName: string, unmet: UnmetRequirement[]): RequirementPayload;
43
+ /** 从失败结果里取一句可展示的原因(UI 用) */
44
+ export declare function summarizeRequirementFailure(result: string): string;