weifuwu 0.89.1 → 0.90.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.
Files changed (83) hide show
  1. package/README.md +99 -461
  2. package/dist/client/components/AlertGroup/AlertGroup.d.ts +1 -1
  3. package/dist/client/components/Anchor/Anchor.d.ts +1 -1
  4. package/dist/client/components/AutoComplete/AutoComplete.d.ts +1 -1
  5. package/dist/client/components/AvatarGroup/AvatarGroup.d.ts +1 -1
  6. package/dist/client/components/BackTop/BackTop.d.ts +12 -5
  7. package/dist/client/components/Button/Button.d.ts +2 -0
  8. package/dist/client/components/Calendar/Calendar.d.ts +1 -1
  9. package/dist/client/components/Carousel/Carousel.d.ts +1 -1
  10. package/dist/client/components/Cascader/Cascader.d.ts +1 -1
  11. package/dist/client/components/CodeEditor/CodeEditor.d.ts +1 -1
  12. package/dist/client/components/ColorPicker/ColorPicker.d.ts +1 -1
  13. package/dist/client/components/DropZone/DropZone.d.ts +27 -0
  14. package/dist/client/components/Editor/Editor.d.ts +1 -1
  15. package/dist/client/components/Editor/edit-events.d.ts +1 -1
  16. package/dist/client/components/Editor/model/types.d.ts +1 -1
  17. package/dist/client/components/Editor/tools/table.d.ts +1 -1
  18. package/dist/client/components/Editor/tools/types.d.ts +1 -1
  19. package/dist/client/components/FilePreview/FilePreview.d.ts +1 -1
  20. package/dist/client/components/FloatButton/FloatButton.d.ts +1 -1
  21. package/dist/client/components/Grid/Grid.d.ts +1 -1
  22. package/dist/client/components/Img/Img.d.ts +7 -0
  23. package/dist/client/components/InputNumber/InputNumber.d.ts +1 -1
  24. package/dist/client/components/JSONViewer/JSONViewer.d.ts +1 -1
  25. package/dist/client/components/Link/Link.d.ts +1 -1
  26. package/dist/client/components/LogViewer/LogViewer.d.ts +1 -1
  27. package/dist/client/components/Markdown/parser.d.ts +1 -1
  28. package/dist/client/components/Math/Math.d.ts +1 -1
  29. package/dist/client/components/Mentions/Mentions.d.ts +1 -1
  30. package/dist/client/components/Menu/Menu.d.ts +1 -1
  31. package/dist/client/components/Menubar/Menubar.d.ts +1 -1
  32. package/dist/client/components/MessageBubble/MessageBubble.d.ts +1 -1
  33. package/dist/client/components/NavBar/NavBar.d.ts +32 -0
  34. package/dist/client/components/NavMenu/NavMenu.d.ts +1 -1
  35. package/dist/client/components/OfficeEditor/ai/ai-bridge.d.ts +1 -1
  36. package/dist/client/components/OfficeEditor/model/types.d.ts +1 -1
  37. package/dist/client/components/Popconfirm/Popconfirm.d.ts +1 -1
  38. package/dist/client/components/Result/Result.d.ts +1 -1
  39. package/dist/client/components/Scrollbar/Scrollbar.d.ts +1 -1
  40. package/dist/client/components/SheetGrid/SheetGrid.d.ts +1 -1
  41. package/dist/client/components/SlideCanvas/SlideCanvas.d.ts +1 -1
  42. package/dist/client/components/TagsInput/TagsInput.d.ts +1 -1
  43. package/dist/client/components/Timeline/Timeline.d.ts +1 -1
  44. package/dist/client/components/Transfer/Transfer.d.ts +1 -1
  45. package/dist/client/components/VirtualList/VirtualList.d.ts +1 -1
  46. package/dist/client/components/VirtualTable/VirtualTable.d.ts +1 -1
  47. package/dist/client/components/Watermark/Watermark.d.ts +1 -1
  48. package/dist/client/components/index.d.ts +4 -0
  49. package/dist/client/components/index.js +39 -39
  50. package/dist/client/components/style.css +238 -18
  51. package/dist/client/layout/weifuwu-layout.css +35 -16
  52. package/dist/client/office/docx.d.ts +1 -1
  53. package/dist/client/office/xlsx.d.ts +1 -1
  54. package/dist/client/vdom/core/async-guard.d.ts +1 -1
  55. package/dist/client/vdom/core/patch/state-machine.d.ts +1 -1
  56. package/dist/client/vdom/core/router.d.ts +1 -1
  57. package/dist/client/vdom/core/ssr/html.d.ts +1 -1
  58. package/dist/client/vdom/core/v2/cycle.d.ts +4 -2
  59. package/dist/client/vdom/core/v2/diff.d.ts +9 -0
  60. package/dist/client/vdom/core/v2/integrate.d.ts +1 -1
  61. package/dist/client/vdom/core/v2/schedule.d.ts +1 -1
  62. package/dist/client/vdom/core/vnode.d.ts +1 -1
  63. package/dist/client/vdom/hooks/drag-media.d.ts +4 -1
  64. package/dist/client/vdom/hooks/popup-manager.d.ts +1 -1
  65. package/dist/client/vdom/hooks/stable.d.ts +1 -1
  66. package/dist/client/vdom/index.js +3 -3
  67. package/dist/client/vdom/testing.js +2 -2
  68. package/dist/server/ai/client.d.ts +1 -1
  69. package/dist/server/ai/sse.d.ts +1 -1
  70. package/dist/server/ai/types.d.ts +2 -2
  71. package/dist/server/db/contracts.d.ts +5 -0
  72. package/dist/server/db/postgres/connection.d.ts +6 -0
  73. package/dist/server/index.js +32 -2
  74. package/dist/server/postgres/types.d.ts +3 -0
  75. package/docs/client.md +170 -0
  76. package/docs/server.md +112 -0
  77. package/package.json +1 -3
  78. package/docs/ai-contract.md +0 -418
  79. package/docs/compat-three-library.md +0 -290
  80. package/docs/components-map.md +0 -227
  81. package/docs/environment.md +0 -50
  82. package/docs/examples.md +0 -121
  83. package/docs/style-guide.md +0 -248
@@ -1,418 +0,0 @@
1
- # Weifuwu AI Stream Protocol (v1)
2
-
3
- > **weifuwu 前后端之间的 LLM/agent 对话协议。** 定义"后端如何把一次对话/一次 agent run 流式地告诉前端,前端如何回传人工决策"。
4
- >
5
- > - **协议是 weifuwu 自己的**(`wf:` 命名空间),不依赖任何 provider 的 wire format——前端只见 `wf:` 事件,换模型/换提供商前端零改动。
6
- > - **实现可换**:后端可以用自研 OpenAI 兼容客户端(`weifuwu/src/ai/`)、raw fetch 或任何库,只要输出 `wf:` 事件即可。协议不绑定实现。
7
- > - **错误即值**:`wf:error` 是正常协议消息,不是断流异常(对齐自研 DB 客户端 RESP `-ERR` 精神)。
8
- > - **前端参考实现**(`weifuwu/ui-dom`):`aiStream()` = 传输解码(POST + SSE 解析 + trace + abort);`ctx.ui.useChat()` = 会话语义层(消息累积、工具调用内嵌、HITL 审批、stop/retry,协议对页面透明)。
9
- > - **版本**:本文档为 v1。非破坏性演进(新事件)直接追加;破坏性变更升版本号,两端随 weifuwu 单包原子发布同步升级。
10
-
11
- ---
12
-
13
- ## 1. 传输
14
-
15
- ### 1.1 下行:SSE(`text/event-stream`)
16
-
17
- - 一次 `POST` = 一次对话 / 一次 agent run
18
- - 请求头:`Accept: text/event-stream`;认证与业务参数走 app 的既有中间件(auth / rateLimit 等)
19
- - 响应头:`Content-Type: text/event-stream`、`Cache-Control: no-cache`、`Connection: keep-alive`
20
- - 事件格式:`event: <名称>\ndata: <JSON>\n\n`
21
-
22
- ```
23
- event: wf:message_start
24
- data: {"id":"9f3a"}
25
-
26
- event: wf:token
27
- data: {"text":"你好"}
28
-
29
- event: wf:done
30
- data: {"content":"你好,有什么可以帮你?","usage":{"prompt_tokens":512,"completion_tokens":384}}
31
- ```
32
-
33
- 调试:`curl -N -X POST <url> -d '{"messages":[...]}'` 直接看裸事件。
34
-
35
- ### 1.2 上行:独立 POST
36
-
37
- SSE 单向,人工决策回传走独立 POST(低频、可鉴权、可审计):
38
-
39
- ```
40
- POST /api/ai/approve
41
- Content-Type: application/json
42
-
43
- {"id":"ap_01","decision":"approved","note":"OK"}
44
- // modified 决策:携带修改后参数(use-chat approve('modified', note, modifiedArgs) 自动带上)
45
- // {"id":"ap_01","decision":"modified","note":"数量改为 5","modifiedArgs":{"qty":5}}
46
- ```
47
-
48
- - 回传载荷形状由协议定义(见 §4.5);**路由路径是 app 的**(app 知道自己的 run 生命周期)。
49
- - 回传端点应挂 auth / rateLimit,审批记录带 `ctx.user` 进审计。
50
-
51
- ### 1.3 生命周期
52
-
53
- | 语义 | 规则 |
54
- |---|---|
55
- | 请求即会话 | 一次 POST + 一条 SSE 连接 = 一个会话;无连接池、无会话管理 |
56
- | 断开 = abort | 客户端断开 → `req.signal` → 取消 provider 请求(省 token) |
57
- | 无 token 超时 | 60s 内无任何事件 → `wf:error { code: 'timeout' }`,随后关闭连接 |
58
- | 审批超时 | `wf:approval_request.expiresAt` 到期无人批 → 工具以 `tool_result { error: { code: 'timeout' } }` 结束(见 §4.5) |
59
-
60
- ---
61
-
62
- ## 2. 事件集总表
63
-
64
- | 事件 | 层 | 方向 | 前端处理 | v1 状态 |
65
- |---|---|---|---|---|
66
- | `wf:message_start` | 核心 | 下行 | 创建会话/消息 | ✅ 实现 |
67
- | `wf:token` | 核心 | 下行 | **append** 文本 | ✅ 实现 |
68
- | `wf:usage` | 核心 | 下行 | 更新 token 计数 | ✅ 实现 |
69
- | `wf:done` | 核心 | 下行 | 收尾(内容 + usage + 可选 reasoning) | ✅ 实现 |
70
- | `wf:error` | 核心 | 下行 | 结构化降级(重试/提示) | ✅ 实现 |
71
- | `wf:tool_call` | 工具 | 下行 | 渲染工具卡片 | ✅ 实现 |
72
- | `wf:tool_result` | 工具 | 下行 | 卡片 → 结果态 | ✅ 实现 |
73
- | `wf:tool_progress` | 工具 | 下行 | 卡片 → 进度态 | ✅ 实现 |
74
- | `wf:step` | agent | 下行 | 步骤可视化 | ✅ 实现(agent 引擎) |
75
- | `wf:approval_request` | agent | 下行 | 渲染审批卡片(待批态) | ✅ 实现(agent 引擎) |
76
- | `wf:approval_response` | agent | **上行 POST** | 用户决策回传 | ✅ 实现(ctx.ai.approve) |
77
- | `x:*` | 自定义 | 双向 | **透传不解释** | ✅ 规则生效 |
78
-
79
- **层规则**:只要 chat 的 app 永远不接触工具/agent 事件;前端解码器按层订阅,未订阅的事件跳过不报错。
80
-
81
- ---
82
-
83
- ## 3. 核心事件(chat 必需)
84
-
85
- ### 3.1 `wf:message_start`
86
-
87
- ```jsonc
88
- { "id": "9f3a" } // 会话/消息 id
89
- ```
90
-
91
- - 一条 SSE 流的第一个事件。
92
- - **`id` 应取 `X-Trace-Id` 请求头**(见 §7 追踪关联),无则后端生成。
93
- - 前端以 `id` 关联后续所有事件。
94
-
95
- ### 3.2 `wf:token`
96
-
97
- ```jsonc
98
- { "text": "你好" } // 增量文本,直接 append
99
- ```
100
-
101
- - **纯增量**:前端把 `text` 追加到当前消息尾部,不做 diff/合并。
102
- - 一个 provider chunk 的 content delta → 一个 `wf:token`(后端不做聚合)。
103
-
104
- ### 3.3 `wf:usage`
105
-
106
- ```jsonc
107
- { "prompt_tokens": 512, "completion_tokens": 384 }
108
- ```
109
-
110
- - provider 返回 usage 时即发(可能出现在流中最后一 chunk,或聚合后一次发)。
111
- - 前端只更新计数,不改变消息内容。
112
-
113
- ### 3.4 `wf:done`
114
-
115
- ```jsonc
116
- {
117
- "content": "你好,有什么可以帮你?",
118
- "usage": { "prompt_tokens": 512, "completion_tokens": 384 },
119
- "reasoning": "先分析用户意图:……" // 可选:thinking 模式推理过程(reasoning_content)
120
- }
121
- ```
122
-
123
- - 正常收尾事件:完整内容 + 最终 usage。
124
- - **`reasoning`(可选,additive)**:thinking 模式(DeepSeek 等)的推理过程,
125
- 收尾时一次性下发——**v1 不进流式**(`wf:token` 只承载正文增量);
126
- 前端以 ReasoningBlock 折叠展示,下一轮回传(`toChatMessages` 带 `reasoning_content`)。
127
- - 前端标记会话完成(停止打字指示、启用输入框)。
128
-
129
- ### 3.5 `wf:error`
130
-
131
- ```jsonc
132
- { "code": "rate_limited", "message": "请求过于频繁,请稍后再试" }
133
- ```
134
-
135
- - **正常协议消息,连接保持**(除非 code 为致命错误如 `auth_failed`,后端可随后关闭)。
136
- - 前端按 `code` 分类降级:展示错误、给重试按钮、允许继续会话。
137
-
138
- **错误码表(v1 定稿)**:
139
-
140
- | code | 含义 | 前端建议 |
141
- |---|---|---|
142
- | `auth_failed` | API key 无效/未配置 | 引导配置 |
143
- | `rate_limited` | provider 限流 | 显示 + 延迟重试 |
144
- | `context_length` | 上下文超长 | 提示截断/新会话 |
145
- | `timeout` | 无 token 超时 / 审批超时 | 提示重试 |
146
- | `provider_error` | provider 返回错误(详情在 message) | 显示 message |
147
- | `invalid_request` | 请求参数错误 | 修复请求 |
148
- | `unsupported` | 能力不支持(诚实裁剪,CS-05) | 提示不可用 |
149
- | `aborted` | 服务端侧主动取消 | 静默 |
150
-
151
- ---
152
-
153
- ## 4. 工具事件(chat + tools)
154
-
155
- ### 4.1 `wf:tool_call`
156
-
157
- ```jsonc
158
- {
159
- "id": "tc_01", // 工具调用 id(provider 给 / 后端生成)
160
- "name": "query_orders", // 工具名(app 定义的业务语义,协议不解释)
161
- "args": { "userId": "u1" } // 完整参数
162
- }
163
- ```
164
-
165
- - **后端聚合完成后才发出**:provider 流式 chunk 中 `tool_calls` 的 id 可能只在首个 chunk(DeepSeek 如此),后端负责聚合出完整的 `{ id, name, args }` 再发。前端不接触增量。
166
- - 并行工具调用 = 连续多条 `wf:tool_call`(各带独立 id),前端可渲染多张卡片。
167
- - 前端按 `name` 分发渲染(`SearchCard` / `ProgressBar` / …),工具名是 app 的扩展面。
168
-
169
- ### 4.2 `wf:tool_result`
170
-
171
- ```jsonc
172
- { "id": "tc_01", "ok": true, "output": { "rows": 3 } }
173
- ```
174
-
175
- 失败形态:
176
-
177
- ```jsonc
178
- { "id": "tc_01", "ok": false, "error": { "code": "rejected", "message": "预算不够" } }
179
- ```
180
-
181
- - `error.code`:`rejected`(人工拒绝)/ `timeout`(审批超时)/ `tool_error`(执行异常)/ app 自定义。
182
- - **`ok: false` 不代表对话结束**——agent 读到 result 后可换方案重试(HITL 核心语义)。
183
-
184
- ### 4.3 `wf:tool_progress`
185
-
186
- ```jsonc
187
- {
188
- "toolCallId": "tc_01",
189
- "step": 2,
190
- "total": 5,
191
- "message": "生成第 2 页",
192
- "status": "running" // running | error | done
193
- }
194
- ```
195
-
196
- - 长任务(PPT 生成、委派子 agent、深度搜索)执行期间的进度汇报,前端更新卡片进度条。
197
- - 秒级任务用此事件;分钟级任务应入队(`ctx.queue`)+ 独立进度通道,见 §9。
198
-
199
- ### 4.4 工具执行模型
200
-
201
- ```ts
202
- // 工具 = 普通对象;run 收到 emit(汇报进度/自定义事件)与 signal(取消)
203
- tools: [{
204
- name: 'generate_ppt',
205
- run: async (args, { emit, signal }) => {
206
- emit('wf:tool_progress', { step: 1, total: 5, message: '生成大纲' })
207
- emit('x:ppt_page_done', { page: 2 }) // 自定义事件
208
- return { fileId: 'ppt_01' } // → wf:tool_result
209
- }
210
- }]
211
- ```
212
-
213
- - `emit` 是工具的执行声道:可发 `wf:tool_progress` 与任意 `x:*` 事件。
214
- - `signal`:用户取消 → abort → 中断长任务(与 §1.3 生命周期闭环)。
215
- - 工具名/args 语义是 app 的,协议只定义"调用怎么流动"。
216
-
217
- ### 4.5 人工审批(HITL,agent 扩展 schema)
218
-
219
- **下行** `wf:approval_request`:
220
-
221
- ```jsonc
222
- {
223
- "id": "ap_01",
224
- "toolCallId": "tc_02",
225
- "name": "send_email",
226
- "args": { "to": "boss@x.com", "subject": "方案" },
227
- "reason": "发送前需要确认收件人",
228
- "expiresAt": 1735689600000 // 审批超时
229
- }
230
- ```
231
-
232
- 发出后**后端挂起该工具执行**,等待上行回传。
233
-
234
- **上行** `POST`(`wf:approval_response` 载荷):
235
-
236
- ```jsonc
237
- {
238
- "id": "ap_01",
239
- "decision": "approved", // approved | rejected | modified
240
- "modifiedArgs": { "to": "me@x.com" }, // 仅 modified
241
- "note": "不要群发,只发给我"
242
- }
243
- ```
244
-
245
- **决策语义**:
246
-
247
- | decision | 工具行为 | 后续事件 |
248
- |---|---|---|
249
- | `approved` | 按原 args 执行 | `wf:tool_result` |
250
- | `modified` | 按 `modifiedArgs` 执行 | `wf:tool_result` |
251
- | `rejected` | 不执行 | `wf:tool_result { ok:false, error:{ code:'rejected', message: note } }` → **agent 读 reason 换方案** |
252
-
253
- - 审批请求带独立 `id`:并发多卡片各自挂起/恢复;id 一次性(防重放)。
254
- - 回传端点挂 auth + rateLimit,审批人 `ctx.user` 进审计(app 责任,协议给 id 语义)。
255
- - 超时:`expiresAt` 到期 → 按 rejected 处理(`error.code: 'timeout'`)。
256
-
257
- ---
258
-
259
- ## 5. agent 扩展事件(已实现:src/ai/agent.ts)
260
-
261
- ### 5.1 `wf:step`
262
-
263
- ```jsonc
264
- { "type": "llm", "content": "正在查询订单…" }
265
- { "type": "tool", "toolCallId": "tc_01", "name": "query_orders" }
266
- ```
267
-
268
- - 供前端做步骤可视化(思考中/工具执行中/完成)。
269
- - 由 agent 引擎(`a.agent()`)在每个 LLM 轮次前与每个工具执行前发出。
270
-
271
- ### 5.2 agent 引擎(`a.agent({ systemPrompt, tools, humanInTheLoop })`)
272
-
273
- 工具循环:LLM 流式(emit `wf:token`)→ tool_calls → 执行工具 → 结果回喂 → 重复,直到无工具调用或 maxSteps 耗尽。
274
-
275
- - 事件序列:`message_start → (step:llm → token* → tool_call → step:tool → [approval_request → approve] → tool_result)* → usage → done`
276
- - **工具执行上下文**:`run(args, { emit, signal })`——emit `wf:tool_progress` / `x:*` 自定义事件;signal 接收用户取消
277
- - **HITL 审批**:`humanInTheLoop` 时每个工具执行前挂起等 `ctx.ai.approve()` 响应(见 §4.5)
278
- - **多轮消息纪律**(真实 DeepSeek 验证):带 tool_calls 的 assistant 消息必须入上下文;thinking 模式 `reasoning_content` 必须回传(陷阱清单 #4)
279
-
280
- ### 5.3 子 agent = 工具
281
-
282
- 多 agent 沟通不新增协议事件:子 agent 通过 `delegate` 工具承载(工具 run 内调 `a.chat()` 或另一个 agent 的循环),其最终输出 = 该工具的 `tool_result`。编排逻辑(委派给谁、何时、如何聚合)是 app 在工具 handler 里的业务。
283
-
284
- ---
285
-
286
- ## 6. 扩展机制(协议不是紧身衣)
287
-
288
- | 机制 | 规则 |
289
- |---|---|
290
- | `x:` 命名空间 | `event: x:any_name`,解码器**透传不解释、不校验、不转换**,前端经 `onEvent` 自处理 |
291
- | 未知字段透传 | `data` 中未知字段必须原样保留(解码器不得丢弃)——新旧客户端双向兼容 |
292
- | 未知事件 | 前端**不得抛错**:未订阅的事件跳过(老前端接新后端安全) |
293
- | 晋升路径 | 2+ app 收敛的 `x:` 事件 → 晋升为 `wf:` 事件 + 协议版本小升 + 前端原语跟进,单包原子发布 |
294
-
295
- **规则一句话:`wf:` 是框架的、版本化的、有前端原语覆盖的;其余全是 app 的。**
296
-
297
- ---
298
-
299
- ## 7. 追踪关联(trace)
300
-
301
- - 前端流式请求应携带 `X-Trace-Id` 请求头(ctx.api 同源生成,一行钩子)。
302
- - 后端以 `X-Trace-Id` 作为 `wf:message_start.id`(serve.ts 已内置 traceId 机制,接受 `x-trace-id` / `traceparent`,兜底 `randomUUID`,响应头回显)。
303
- - **工具内发起的后端请求继承同一 traceId** → 整个 agent run(对话 + provider 调用 + 工具内请求)挂在同一 id 下,日志一次搜完。
304
-
305
- ```
306
- 用户输入 → POST /api/chat (X-Trace-Id: 9f3a)
307
- → wf:message_start { id: "9f3a" }
308
- → provider 调用(日志带 9f3a)
309
- → wf:tool_call query_orders
310
- → GET /api/orders(继承 X-Trace-Id: 9f3a)
311
- → wf:done
312
- → 日志搜 "9f3a" = 整个会话全链路
313
- ```
314
-
315
- ---
316
-
317
- ## 8. 共享类型(规范型,`src/ai/types.ts` 据此实现)
318
-
319
- ```ts
320
- // ── 事件 ──────────────────────────────────────────────
321
-
322
- export interface WfMessageStart { id: string }
323
- export interface WfToken { text: string }
324
- export interface WfUsage { prompt_tokens: number; completion_tokens: number; total_tokens?: number }
325
- export interface WfDone { content: string; usage?: WfUsage; reasoning?: string }
326
-
327
- export type WfErrorCode =
328
- | 'auth_failed' | 'rate_limited' | 'context_length' | 'timeout'
329
- | 'provider_error' | 'invalid_request' | 'unsupported' | 'aborted'
330
-
331
- export interface WfError { code: WfErrorCode; message: string }
332
-
333
- export interface WfToolCall { id: string; name: string; args: Record<string, unknown> }
334
- export interface WfToolResult {
335
- id: string
336
- ok: boolean
337
- output?: unknown
338
- error?: { code: string; message: string } // rejected | timeout | tool_error | app 自定义
339
- }
340
- export interface WfToolProgress {
341
- toolCallId: string
342
- step: number
343
- total: number
344
- message?: string
345
- status: 'running' | 'error' | 'done'
346
- }
347
-
348
- export interface WfStep { type: 'llm' | 'tool'; content?: string; toolCallId?: string; name?: string }
349
- export interface WfApprovalRequest {
350
- id: string
351
- toolCallId: string
352
- name: string
353
- args: Record<string, unknown>
354
- reason?: string
355
- expiresAt?: number
356
- }
357
- export type WfApprovalDecision = 'approved' | 'rejected' | 'modified'
358
- export interface WfApprovalResponse {
359
- id: string
360
- decision: WfApprovalDecision
361
- modifiedArgs?: Record<string, unknown>
362
- note?: string
363
- }
364
-
365
- /** 所有框架事件的联合类型(前端 switch 收窄用) */
366
- export type WfStreamEvent =
367
- | { name: 'wf:message_start'; data: WfMessageStart }
368
- | { name: 'wf:token'; data: WfToken }
369
- | { name: 'wf:usage'; data: WfUsage }
370
- | { name: 'wf:done'; data: WfDone }
371
- | { name: 'wf:error'; data: WfError }
372
- | { name: 'wf:tool_call'; data: WfToolCall }
373
- | { name: 'wf:tool_result'; data: WfToolResult }
374
- | { name: 'wf:tool_progress'; data: WfToolProgress }
375
- | { name: 'wf:step'; data: WfStep }
376
- | { name: 'wf:approval_request'; data: WfApprovalRequest }
377
-
378
- // ── 对话消息(回传 provider 的形状,与 wf: 事件无关)────
379
-
380
- export type MessageRole = 'system' | 'user' | 'assistant' | 'tool'
381
- export interface ChatMessage {
382
- role: MessageRole
383
- content: string
384
- /** DeepSeek thinking mode:前一轮的 reasoning_content 必须回传 */
385
- reasoning_content?: string
386
- tool_call_id?: string
387
- tool_calls?: ToolCall[]
388
- name?: string
389
- }
390
- export interface ToolCall { id: string; type: 'function'; function: { name: string; arguments: string } }
391
- ```
392
-
393
- ---
394
-
395
- ## 9. 诚实裁剪与边界
396
-
397
- | 范围 | 状态 |
398
- |---|---|
399
- | chat / stream / tools / progress / error 模型 | ✅ v1 实现 |
400
- | `x:*` 透传、未知事件/字段兼容 | ✅ v1 规则生效 |
401
- | agent 引擎(`a.agent()`)、`wf:step`、审批事件 | ✅ 已实现 |
402
- | embeddings | ❌ 不做(DeepSeek 无此 API) |
403
- | Anthropic/OpenAI 原生协议 | ❌ 不做(OpenAI 兼容已覆盖,baseUrl 可换) |
404
- | 多 agent 编排 | ❌ 不承诺(子 agent = 工具已覆盖) |
405
- | 审批持久化 | ❌ 不做(连接断 = 会话亡;持久化等 agent 长任务化 + queue) |
406
- | 分钟级长任务 | ⚠️ 工具入队即返回(`ctx.queue`),进度走独立通道(WS/SSE 订阅)——app 编排 |
407
- | 前端通用 HTTP 追踪/时间线面板 | ⏸ 信号(浏览器 DevTools 已覆盖前端部分) |
408
-
409
- ## 10. 陷阱清单(来自真实实现)
410
-
411
- 1. **partial chunk / UTF-8 边界**:SSE 解析必须 buffer + `TextDecoder(stream: true)`,`\n` 切行,末段保留到下次。
412
- 2. **`[DONE]` 终止符**:读到即结束,不当作 JSON 解析。
413
- 3. **tool_calls id 只在首个 chunk**(DeepSeek):后端必须聚合完整 tool_call 再发 `wf:tool_call`;无 id 的后续 chunk 追加到最后一个。
414
- 4. **reasoning_content 必须回传**(thinking 模式):`ChatMessage.reasoning_content` 随消息往返,否则推理断档。
415
- 5. **SSE 注释行 `:` 与空行**:跳过。
416
- 6. **非 JSON 行**:忽略不抛错。
417
- 7. **usage 可能只在最后一 chunk**(DeepSeek):聚合后发 `wf:usage`,`wf:done` 再带最终值。
418
- 8. **审批回传 id 一次性**:防重放,用后即焚。