super-agent-sdk 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -25
- package/dist/index.cjs +4 -4
- package/dist/index.d.ts +19 -3
- package/dist/index.mjs +227 -178
- package/dist/widget.cjs +239 -35
- package/dist/widget.d.ts +51 -12
- package/dist/widget.mjs +1864 -1106
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -131,6 +131,7 @@ ready
|
|
|
131
131
|
|
|
132
132
|
```ts
|
|
133
133
|
export interface SuperAgentSDK {
|
|
134
|
+
readonly botId: number; // 当前 Bot ID
|
|
134
135
|
createSession(): Promise<string>;
|
|
135
136
|
chat(options: ChatOptions): AbortController;
|
|
136
137
|
listConversations(
|
|
@@ -143,7 +144,9 @@ export interface SuperAgentSDK {
|
|
|
143
144
|
setToken(token: string): void;
|
|
144
145
|
feedback(messageId: string, type: "like" | "dislike", options?: { reason?: number; remark?: string }): Promise<void>;
|
|
145
146
|
cancelFeedback(messageId: string): Promise<void>;
|
|
146
|
-
respondInterrupt(interruptId: string, sessionId: string, response: InterruptResponse): Promise<void>;
|
|
147
|
+
respondInterrupt(interruptId: string, sessionId: string, response: InterruptResponse, onMessage?: (event: ChatEvent) => void, onError?: (error: Error) => void): Promise<void>;
|
|
148
|
+
listSkills(): Promise<SkillInfo[]>;
|
|
149
|
+
stopGeneration(sessionId: string): Promise<void>;
|
|
147
150
|
}
|
|
148
151
|
```
|
|
149
152
|
|
|
@@ -173,7 +176,7 @@ export interface ChatOptions {
|
|
|
173
176
|
stream?: boolean; // 默认 true
|
|
174
177
|
onMessage?: (event: ChatEvent) => void;
|
|
175
178
|
onError?: (error: Error) => void;
|
|
176
|
-
onDone?: (result: { sessionId: string; content: string }) => void;
|
|
179
|
+
onDone?: (result: { sessionId: string; content: string; messageId?: string }) => void; // messageId:后端在 done 事件返回的消息标识(存在时 SDK Widget 用其启用消息反馈,若返回可据此判断该消息可 feedback)
|
|
177
180
|
signal?: AbortSignal;
|
|
178
181
|
}
|
|
179
182
|
```
|
|
@@ -301,26 +304,57 @@ sdk.setToken("<TOKEN>");
|
|
|
301
304
|
|
|
302
305
|
消息反馈(赞/踩)。调用 `sdk.feedback()` 发送 `POST /chat/messages/{messageId}/feedback`;调用 `sdk.cancelFeedback()` 发送 `DELETE` 取消反馈。
|
|
303
306
|
|
|
307
|
+
> **messageId 说明**:即消息历史返回的表主键 `id`(数字),不是 `messageId`(UUID)。纯 API 模式从 `getMessages()` 结果取 `UIMessage.id`。
|
|
308
|
+
|
|
304
309
|
```ts
|
|
310
|
+
const messages = await sdk.getMessages(sessionId);
|
|
311
|
+
const aiMsg = messages.findLast((m) => m.role === "assistant");
|
|
312
|
+
|
|
305
313
|
// 点赞
|
|
306
|
-
await sdk.feedback(
|
|
314
|
+
await sdk.feedback(aiMsg.id, "like");
|
|
307
315
|
|
|
308
316
|
// 踩(可附原因 + 备注)
|
|
309
|
-
await sdk.feedback(
|
|
317
|
+
await sdk.feedback(aiMsg.id, "dislike", {
|
|
310
318
|
reason: 1, // 1=事实错误 2=逻辑问题 3=不相关 4=信息过时 5=冗长啰嗦 6=难以理解
|
|
311
319
|
remark: "时间描述有误",
|
|
312
320
|
});
|
|
313
321
|
|
|
314
322
|
// 取消反馈
|
|
315
|
-
await sdk.cancelFeedback(
|
|
323
|
+
await sdk.cancelFeedback(aiMsg.id);
|
|
316
324
|
```
|
|
317
325
|
|
|
318
326
|
组件内置的点赞/踩按钮已自动调用 `feedback()`/`cancelFeedback()`:
|
|
319
327
|
- 点击已选中的按钮 → 取消(DELETE)
|
|
320
328
|
- 点击另一个按钮 → 切换(POST)
|
|
321
329
|
- 点踩时弹出原因选择面板(可选填原因 + 备注后提交)
|
|
330
|
+
- 流式渲染期间消息尚无后端 ID(临时占位),此时点击反馈 SDK 会自动拉取一次历史消息解析真实 ID 再发送,业务方无感知
|
|
331
|
+
|
|
332
|
+
### 5.10 respondInterrupt()
|
|
333
|
+
|
|
334
|
+
响应中断事件(Human-in-the-Loop),后端继续推送后续 SSE 事件:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
sdk.respondInterrupt(
|
|
338
|
+
interruptId: string,
|
|
339
|
+
sessionId: string,
|
|
340
|
+
response: InterruptResponse,
|
|
341
|
+
onMessage?: (event: ChatEvent) => void,
|
|
342
|
+
onError?: (error: Error) => void
|
|
343
|
+
): Promise<void>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Widget 内置中断卡片会自动调用此方法,纯 API 模式需手动处理(详见 [12. Human-in-the-Loop](#12-human-in-the-loop))。
|
|
347
|
+
|
|
348
|
+
### 5.11 listSkills()
|
|
322
349
|
|
|
323
|
-
|
|
350
|
+
获取当前 Bot 绑定的技能列表(用于 `/` 斜杠命令选择技能):
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
const skills: SkillInfo[] = await sdk.listSkills();
|
|
354
|
+
// [{ code: "web-searcher", name: "web-searcher", displayName: "网页搜索", description: "..." }]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 5.12 后端接口对照
|
|
324
358
|
|
|
325
359
|
SDK 方法 → 后端接口的完整映射:
|
|
326
360
|
|
|
@@ -330,10 +364,13 @@ SDK 方法 → 后端接口的完整映射:
|
|
|
330
364
|
| --------------------- | ------------------------- | --------------------------------------- | ----------------------------------------------- |
|
|
331
365
|
| `getToken()` | `POST {gateway}/v1/token` | `{ botId }` | `{ token, appId }` |
|
|
332
366
|
| `createSession()` | `POST /chat/sessions` | `{ botId }` | `{ sessionId }` |
|
|
333
|
-
| `chat()` | `POST /chat` | `{ botId, sessionId, message, stream }` | SSE 流,`done` 事件含 `sessionId`
|
|
367
|
+
| `chat()` | `POST /chat` | `{ botId, sessionId, message, stream }` | SSE 流,`done`/`stop` 事件含 `sessionId` |
|
|
334
368
|
| `listConversations()` | `GET /chat/conversations` | 查询参数 `botId`、`page`、`size` | `{ items: [{ sessionId, botId, ... }], total }` |
|
|
335
369
|
| `feedback()` | `POST /chat/messages/{messageId}/feedback` | `{ type, reason?, remark? }` | `null` |
|
|
336
370
|
| `cancelFeedback()` | `DELETE /chat/messages/{messageId}/feedback` | 无 body | `null` |
|
|
371
|
+
| `respondInterrupt()` | `POST /chat/interrupt/{interruptId}/respond` | `{ sessionId, action, value? }` | SSE 流(后续事件) |
|
|
372
|
+
| `stopGeneration()` | `POST /chat/stop` | `{ sessionId }` | `{ stopped: "pending", sessionId }` |
|
|
373
|
+
| `listSkills()` | `GET /chat/bots/{botId}/skills` | — | `[{ code, name, displayName, description }]` |
|
|
337
374
|
|
|
338
375
|
---
|
|
339
376
|
|
|
@@ -360,6 +397,7 @@ export interface MountOptions {
|
|
|
360
397
|
title?: string; // 标题,默认 "AI Assistant"
|
|
361
398
|
sidebarDefaultOpen?: boolean; // fullpage 模式:侧边栏初始展开(默认 true)
|
|
362
399
|
avatar?: AvatarConfig; // 自定义头像
|
|
400
|
+
artifactPreview?: boolean; // 产物预览总开关(html/pdf/docx 预览 + 下载 + 全屏),默认 false
|
|
363
401
|
}
|
|
364
402
|
|
|
365
403
|
export interface WidgetInstance {
|
|
@@ -500,6 +538,7 @@ export interface Slots {
|
|
|
500
538
|
ToolCallPart?: ComponentType<ToolCallPartProps>;
|
|
501
539
|
ToolResultPart?: ComponentType<ToolResultPartProps>;
|
|
502
540
|
ErrorPart?: ComponentType<ErrorPartProps>;
|
|
541
|
+
InterruptCard?: ComponentType<InterruptCardProps>;
|
|
503
542
|
WelcomeScreen?: ComponentType<WelcomeScreenProps>;
|
|
504
543
|
}
|
|
505
544
|
```
|
|
@@ -514,8 +553,8 @@ export interface Slots {
|
|
|
514
553
|
| `ThreadList` | `ThreadListProps` | 会话列表(浮窗模式覆盖层 / 全屏模式侧边栏) |
|
|
515
554
|
| `ThinkingPart` | `ThinkingPartProps` | 思考过程块 |
|
|
516
555
|
| `TextPart` | `TextPartProps` | 文本块 |
|
|
517
|
-
| `ToolCallPart` | `ToolCallPartProps` |
|
|
518
|
-
| `ToolResultPart` | `ToolResultPartProps` |
|
|
556
|
+
| `ToolCallPart` | `ToolCallPartProps` | 工具调用卡片(可折叠 + 展开后复制参数) |
|
|
557
|
+
| `ToolResultPart` | `ToolResultPartProps` | 工具返回卡片(pre 滚动 + 复制;`renderMode==="html"` 时 DOMPurify 清洗后直接渲染,最高 60vh 滚动) |
|
|
519
558
|
| `ErrorPart` | `ErrorPartProps` | 错误提示块 |
|
|
520
559
|
| `WelcomeScreen` | `WelcomeScreenProps` | 空会话欢迎页 |
|
|
521
560
|
|
|
@@ -693,8 +732,8 @@ function MyComposer({ status, onSend, onStop }: ComposerProps) {
|
|
|
693
732
|
export interface ActionBarProps {
|
|
694
733
|
message: UIMessage;
|
|
695
734
|
onCopy: () => void;
|
|
696
|
-
onRegenerate
|
|
697
|
-
onFeedback?: (messageId: string, feedback: "like" | "dislike") => void;
|
|
735
|
+
onRegenerate?: () => void;
|
|
736
|
+
onFeedback?: (messageId: string, feedback: "like" | "dislike", options?: { reason?: number; remark?: string }) => void;
|
|
698
737
|
}
|
|
699
738
|
```
|
|
700
739
|
|
|
@@ -728,6 +767,8 @@ export interface ThreadListProps {
|
|
|
728
767
|
onNew: () => void;
|
|
729
768
|
onDelete: (sessionId: string) => void;
|
|
730
769
|
onRename: (sessionId: string, title: string) => void;
|
|
770
|
+
hasMore?: boolean;
|
|
771
|
+
onLoadMore?: () => void;
|
|
731
772
|
}
|
|
732
773
|
```
|
|
733
774
|
|
|
@@ -791,6 +832,10 @@ export interface ToolResultPartProps {
|
|
|
791
832
|
toolName: string;
|
|
792
833
|
toolCallId: string;
|
|
793
834
|
content: string;
|
|
835
|
+
artifacts?: Artifact[];
|
|
836
|
+
renderMode?: string;
|
|
837
|
+
html?: string;
|
|
838
|
+
artifactPreview?: boolean;
|
|
794
839
|
}
|
|
795
840
|
export interface ErrorPartProps {
|
|
796
841
|
content: string;
|
|
@@ -1101,18 +1146,59 @@ await sdk.deleteConversation(items[0].sessionId);
|
|
|
1101
1146
|
|
|
1102
1147
|
### 9.2 组件内置能力
|
|
1103
1148
|
|
|
1104
|
-
- **自动加载**:挂载后自动调用 `listConversations`
|
|
1149
|
+
- **自动加载**:挂载后自动调用 `listConversations` 拉取会话列表(每页 20 条)。
|
|
1150
|
+
- **滚动加载**:会话列表滚动到底部时自动加载下一页,支持任意数量的会话。
|
|
1105
1151
|
- **切换**:点击会话项加载该会话历史消息并展示。
|
|
1106
1152
|
- **新建**:点击「新会话」清空当前消息,下次发送时自动调用 `createSession()` 创建新会话。
|
|
1107
1153
|
- **重命名**:`fullpage` 侧边栏或自定义 `ThreadList` 中调用 `renameConversation`。
|
|
1108
1154
|
- **删除**:悬停会话项后点击删除按钮。
|
|
1109
|
-
- **本地持久化**:最近一次活跃会话写入 `localStorage`,键为 `
|
|
1155
|
+
- **本地持久化**:最近一次活跃会话写入 `localStorage`,键为 `sa_active_conversation_{botId}`(按 Bot 隔离,切换 Bot 后互不串扰)。
|
|
1110
1156
|
|
|
1111
1157
|
---
|
|
1112
1158
|
|
|
1113
1159
|
## 10. 中断恢复
|
|
1114
1160
|
|
|
1115
|
-
### 10.1
|
|
1161
|
+
### 10.1 停止生成
|
|
1162
|
+
|
|
1163
|
+
SDK 提供双信号停止机制,确保多实例部署下也能可靠停止生成:
|
|
1164
|
+
|
|
1165
|
+
1. **`abort()`**:断开 SSE 连接,前端即时停止接收
|
|
1166
|
+
2. **`POST /chat/stop`**:通过 Redis Pub/Sub 广播取消信号到实际执行的后端实例
|
|
1167
|
+
|
|
1168
|
+
Widget 内置的停止按钮已自动执行双信号(`abort()` + `stopGeneration()`),无需手动处理。
|
|
1169
|
+
|
|
1170
|
+
#### 纯 API 模式
|
|
1171
|
+
|
|
1172
|
+
```ts
|
|
1173
|
+
const controller = sdk.chat({
|
|
1174
|
+
sessionId,
|
|
1175
|
+
message: "...",
|
|
1176
|
+
stream: true,
|
|
1177
|
+
onMessage: (event) => {
|
|
1178
|
+
if (event.type === 'stop') {
|
|
1179
|
+
// 用户主动停止,event.content 为已生成的半截回复全文
|
|
1180
|
+
console.log("已停止,半截回复:", event.content);
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
onDone,
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
// 停止生成:双信号并发
|
|
1187
|
+
controller.abort();
|
|
1188
|
+
await sdk.stopGeneration(sessionId);
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
#### stopGeneration()
|
|
1192
|
+
|
|
1193
|
+
```ts
|
|
1194
|
+
sdk.stopGeneration(sessionId: string): Promise<void>
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
调用 `POST /chat/stop`,广播取消信号。返回 `{ stopped: "pending" }` 表示已受理,实际停止异步发生。SDK 不应阻塞等待——以 SSE 流的 `stop`/`done` 事件或本地 `abort` 作为停止完成的判定。
|
|
1198
|
+
|
|
1199
|
+
半截回复(含思考与正文)会保留:既写入数据库(刷新可回显),也写入 checkpoint(影响下一轮对话上下文)。
|
|
1200
|
+
|
|
1201
|
+
### 10.2 中断请求
|
|
1116
1202
|
|
|
1117
1203
|
`chat()` 返回 `AbortController`,可随时中断:
|
|
1118
1204
|
|
|
@@ -1134,11 +1220,11 @@ sdk.chat({ sessionId, message: "...", signal: ac.signal });
|
|
|
1134
1220
|
ac.abort();
|
|
1135
1221
|
```
|
|
1136
1222
|
|
|
1137
|
-
### 10.
|
|
1223
|
+
### 10.3 会话恢复
|
|
1138
1224
|
|
|
1139
1225
|
页面刷新或组件重新挂载时,会话状态自动恢复:
|
|
1140
1226
|
|
|
1141
|
-
1. 挂载时从 `localStorage` 读取上次活跃会话 ID(键 `
|
|
1227
|
+
1. 挂载时从 `localStorage` 读取上次活跃会话 ID(键 `sa_active_conversation_{botId}`)。
|
|
1142
1228
|
2. 若该会话仍在列表中,自动调用 `getMessages` 拉取历史消息并渲染。
|
|
1143
1229
|
3. 若存储的会话已不存在(被删除),则自动清空并回退到「新会话」。
|
|
1144
1230
|
|
|
@@ -1171,8 +1257,17 @@ export type MessagePart =
|
|
|
1171
1257
|
toolName: string;
|
|
1172
1258
|
toolCallId: string;
|
|
1173
1259
|
content: string;
|
|
1260
|
+
artifacts?: Artifact[];
|
|
1261
|
+
renderMode?: string;
|
|
1262
|
+
html?: string;
|
|
1174
1263
|
}
|
|
1175
|
-
| { type: "error"; content: string }
|
|
1264
|
+
| { type: "error"; content: string }
|
|
1265
|
+
| {
|
|
1266
|
+
type: "interrupt";
|
|
1267
|
+
interrupt: InterruptEvent;
|
|
1268
|
+
resolved?: boolean;
|
|
1269
|
+
response?: InterruptResponse;
|
|
1270
|
+
};
|
|
1176
1271
|
```
|
|
1177
1272
|
|
|
1178
1273
|
| type | 字段 | 说明 |
|
|
@@ -1180,8 +1275,9 @@ export type MessagePart =
|
|
|
1180
1275
|
| `text` | `content` | 文本 / Markdown 内容 |
|
|
1181
1276
|
| `thinking` | `content` | 思考过程(reasoning) |
|
|
1182
1277
|
| `tool_call` | `toolName`, `toolCallId`, `args` | 工具调用,`args` 为 JSON 字符串 |
|
|
1183
|
-
| `tool_result` | `toolName`, `toolCallId`, `content` | 工具返回结果
|
|
1278
|
+
| `tool_result` | `toolName`, `toolCallId`, `content`, `artifacts?`, `renderMode?`, `html?` | 工具返回结果 |
|
|
1184
1279
|
| `error` | `content` | 错误信息 |
|
|
1280
|
+
| `interrupt` | `interrupt`, `resolved?`, `response?` | 中断交互卡片(HITL) |
|
|
1185
1281
|
|
|
1186
1282
|
### 11.3 ChatEvent(流式事件)
|
|
1187
1283
|
|
|
@@ -1194,12 +1290,19 @@ export interface ChatEvent {
|
|
|
1194
1290
|
| "tool_call"
|
|
1195
1291
|
| "tool_result"
|
|
1196
1292
|
| "done"
|
|
1197
|
-
| "
|
|
1293
|
+
| "stop"
|
|
1294
|
+
| "error"
|
|
1295
|
+
| "interrupt";
|
|
1198
1296
|
content: string;
|
|
1199
1297
|
toolName?: string;
|
|
1200
1298
|
toolCallId?: string;
|
|
1201
1299
|
args?: string; // JSON.stringify 后的字符串
|
|
1202
1300
|
sessionId?: string;
|
|
1301
|
+
messageId?: string; // done 事件携带后端消息标识(后端返回时该消息可反馈/feedback)
|
|
1302
|
+
interrupt?: InterruptEvent; // interrupt 事件携带中断详情
|
|
1303
|
+
artifacts?: Artifact[]; // tool_result 事件携带的结构化产物
|
|
1304
|
+
renderMode?: string; // "html" 时工具结果为富文本
|
|
1305
|
+
html?: string; // renderMode==="html" 时的 HTML 内容
|
|
1203
1306
|
}
|
|
1204
1307
|
```
|
|
1205
1308
|
|
|
@@ -1235,7 +1338,7 @@ Agent 执行中
|
|
|
1235
1338
|
### 12.3 响应中断
|
|
1236
1339
|
|
|
1237
1340
|
```ts
|
|
1238
|
-
await sdk.respondInterrupt(interruptId, {
|
|
1341
|
+
await sdk.respondInterrupt(interruptId, sessionId, {
|
|
1239
1342
|
action: "confirm",
|
|
1240
1343
|
});
|
|
1241
1344
|
```
|
|
@@ -1270,7 +1373,7 @@ sdk.chat({
|
|
|
1270
1373
|
if (e.type === "interrupt" && e.interrupt) {
|
|
1271
1374
|
// 自定义 UI 处理
|
|
1272
1375
|
const ok = window.confirm(e.interrupt.content);
|
|
1273
|
-
sdk.respondInterrupt(e.interrupt.interruptId, {
|
|
1376
|
+
sdk.respondInterrupt(e.interrupt.interruptId, sessionId, {
|
|
1274
1377
|
action: ok ? "confirm" : "cancel",
|
|
1275
1378
|
});
|
|
1276
1379
|
}
|
|
@@ -1298,6 +1401,8 @@ sdk.chat({
|
|
|
1298
1401
|
| `date` | 日期选择 | `submit` | `"2026-08-21T14:00:00"` |
|
|
1299
1402
|
| `location` | 位置选择 | `submit` | `{ address, lat, lng }` |
|
|
1300
1403
|
|
|
1404
|
+
> `form` 的字段定义见 `FormField`(`types.ts`):`type` 支持 `text` / `number` / `password` / `textarea` / `select` / `multiSelect` / `date`;`options` 支持字符串数组或 `{ value, label }` 数组。已回答的历史 interrupt 以禁用态回显用户答案。
|
|
1405
|
+
|
|
1301
1406
|
> 每个 `interruptType` 的完整字段定义见 [API 文档](./API.md) 与 [HITL 设计文档](../docs/superpowers/specs/2026-08-18-human-in-the-loop-design.md)。
|
|
1302
1407
|
|
|
1303
1408
|
---
|
|
@@ -1356,6 +1461,7 @@ interface UseChatReturn {
|
|
|
1356
1461
|
stop: () => void;
|
|
1357
1462
|
regenerate: () => void;
|
|
1358
1463
|
setMessages: (messages: UIMessage[]) => void;
|
|
1464
|
+
respondToInterrupt: (interruptId: string, response: InterruptResponse) => void;
|
|
1359
1465
|
}
|
|
1360
1466
|
```
|
|
1361
1467
|
|
|
@@ -1367,15 +1473,18 @@ export function useConversations(
|
|
|
1367
1473
|
interface UseConversationsOptions {
|
|
1368
1474
|
sdk: SuperAgentSDK;
|
|
1369
1475
|
onConversationChange?: (sessionId: string) => void;
|
|
1476
|
+
enabled?: boolean; // 默认 true,false 时不自动加载
|
|
1370
1477
|
}
|
|
1371
1478
|
|
|
1372
1479
|
interface UseConversationsReturn {
|
|
1373
1480
|
conversations: Conversation[];
|
|
1374
1481
|
activeConversationId: string | null;
|
|
1375
1482
|
loading: boolean;
|
|
1483
|
+
hasMore: boolean;
|
|
1376
1484
|
loadConversations: () => Promise<void>;
|
|
1485
|
+
loadMore: () => Promise<void>;
|
|
1377
1486
|
switchConversation: (sessionId: string) => Promise<UIMessage[]>;
|
|
1378
|
-
newConversation: () => void
|
|
1487
|
+
newConversation: () => Promise<void>;
|
|
1379
1488
|
deleteConversation: (sessionId: string) => Promise<void>;
|
|
1380
1489
|
renameConversation: (sessionId: string, title: string) => Promise<void>;
|
|
1381
1490
|
setActiveConversationId: (id: string | null) => void;
|
|
@@ -1407,9 +1516,11 @@ function MyChat({ sdk }: { sdk: SuperAgentSDK }) {
|
|
|
1407
1516
|
|
|
1408
1517
|
请求返回 `401` 时,SDK 自动重新调用 `POST {tokenGateway}/v1/token` 获取新 token。
|
|
1409
1518
|
|
|
1410
|
-
### 13.4
|
|
1519
|
+
### 13.4 停止与取消请求
|
|
1520
|
+
|
|
1521
|
+
`chat()` 返回 `AbortController`;或传入外部 `AbortSignal`(SDK 会合并内部信号,任一触发即中断)。
|
|
1411
1522
|
|
|
1412
|
-
|
|
1523
|
+
推荐使用双信号机制停止生成(Widget 内置停止按钮已自动处理):
|
|
1413
1524
|
|
|
1414
1525
|
```ts
|
|
1415
1526
|
const controller = sdk.chat({
|
|
@@ -1418,11 +1529,16 @@ const controller = sdk.chat({
|
|
|
1418
1529
|
stream: true,
|
|
1419
1530
|
onDone,
|
|
1420
1531
|
});
|
|
1421
|
-
//
|
|
1532
|
+
// 双信号停止:abort 断 SSE + POST /chat/stop 广播后端取消
|
|
1422
1533
|
controller.abort();
|
|
1534
|
+
await sdk.stopGeneration(sessionId);
|
|
1423
1535
|
```
|
|
1424
1536
|
|
|
1537
|
+
纯客户端取消(不通知后端):
|
|
1538
|
+
|
|
1425
1539
|
```ts
|
|
1540
|
+
controller.abort();
|
|
1541
|
+
// 或使用外部 signal
|
|
1426
1542
|
const ac = new AbortController();
|
|
1427
1543
|
sdk.chat({ sessionId, message: "...", signal: ac.signal });
|
|
1428
1544
|
ac.abort();
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function
|
|
2
|
-
`).map(
|
|
3
|
-
`))}catch{return null}switch(t.type){case"user":return{type:"user",content:t.content??""};case"thinking":return{type:"thinking",content:t.content??""};case"ai":return{type:"ai",content:t.content??""};case"tool_call":return{type:"tool_call",content:t.name??"",toolName:t.name,toolCallId:t.id,args:typeof t.args=="string"?t.args:JSON.stringify(t.args??{})};case"tool_result":return{type:"tool_result",content:t.output??"",toolName:t.name,toolCallId:t.id,artifacts:Array.isArray(t.artifacts)?t.artifacts:void 0,renderMode:t.render_mode??t.renderMode,html:t.html};case"done":return{type:"done",content:t.content??"",sessionId:t.sessionId??t.session_id??"",messageId:t.messageId??t.message_id};case"error":return{type:"error",content:t.message??"Unknown error"};case"interrupt":return{type:"interrupt",content:t.content??"",interrupt:{interruptId:t.interruptId??t.interrupt_id??"",interruptType:t.interruptType??t.interrupt_type??"confirm",content:t.content??"",options:t.options,placeholder:t.placeholder,inputType:t.inputType??t.input_type,maxLength:t.maxLength??t.max_length,required:t.required,fields:t.fields,metadata:t.metadata}};default:return null}}async function m(
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function u(r){if(Array.isArray(r))return r.map(e=>u(e));if(r&&typeof r=="object"&&r.constructor===Object){const e={};for(const[t,s]of Object.entries(r)){const o=t.replace(/_([a-z])/g,(a,n)=>n.toUpperCase());e[o]=u(s)}return e}return r}const I="/api/agent/runtime/v1";class w{constructor(e){this.refreshing=null;const t=e.baseUrl??I;this.baseUrl=t.replace(/\/$/,""),this.tokenGateway=e.tokenGateway.replace(/\/$/,""),this.appId=e.appId??"",this.token=e.token??"",this.botId=e.botId,this.isAbsoluteUrl=/^https?:\/\//.test(this.baseUrl),this.isAbsoluteGateway=/^https?:\/\//.test(this.tokenGateway)}getBotId(){return this.botId}getAppKey(){return this.appId}setToken(e){this.token=e}getOrigin(){return typeof window<"u"?window.location.origin:""}async fetchToken(){const t=`${this.isAbsoluteGateway?this.tokenGateway:`${this.getOrigin()}${this.tokenGateway}`}/v1/token`,s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({botId:this.botId})});if(!s.ok)throw new Error(`HTTP ${s.status}: ${s.statusText}`);const o=await s.json();if(o.code!=="200")throw new Error(o.message||`API error: ${o.code}`);const a=u(o.data);return this.token=a.token,a.appId&&!this.appId&&(this.appId=a.appId),a.token}headers(e){return{"Content-Type":"application/json","X-App-Id":this.appId,"X-Token":this.token,...e}}async handleTokenRefresh(){if(this.refreshing)return await this.refreshing,!0;try{return this.refreshing=(async()=>{await this.fetchToken()})(),await this.refreshing,!0}catch{return!1}finally{this.refreshing=null}}async request(e,t,s){if(!this.token&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");let o=this.isAbsoluteUrl?`${this.baseUrl}${t}`:`${this.getOrigin()}${this.baseUrl}${t}`;if(s!=null&&s.params){const i=new URLSearchParams;for(const[d,l]of Object.entries(s.params))l!=null&&i.set(d,String(l));const c=i.toString();c&&(o+=`?${c}`)}const a=await fetch(o,{method:e,headers:this.headers(),body:s!=null&&s.body?JSON.stringify(s.body):void 0});if(a.status===401&&!(s!=null&&s.retry)&&await this.handleTokenRefresh())return this.request(e,t,{...s,retry:!0});if(!a.ok)throw new Error(`HTTP ${a.status}: ${a.statusText}`);const n=await a.json();if(n.code!=="200")throw new Error(n.message||`API error: ${n.code}`);return u(n.data)}get(e,t){return this.request("GET",e,{params:t})}post(e,t){return this.request("POST",e,{body:t})}patch(e,t){return this.request("PATCH",e,{body:t})}del(e){return this.request("DELETE",e)}async streamPost(e,t,s){if(!this.token&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");const o=this.isAbsoluteUrl?`${this.baseUrl}${e}`:`${this.getOrigin()}${this.baseUrl}${e}`,a=await fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:s});return a.status===401&&await this.handleTokenRefresh()?fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:s}):a}}function y(r){const e=r.split(`
|
|
2
|
+
`).map(s=>s.trim()).filter(s=>s.startsWith("data:")).map(s=>s.slice(5).trim());if(!e.length)return null;let t;try{t=JSON.parse(e.join(`
|
|
3
|
+
`))}catch{return null}switch(t.type){case"user":return{type:"user",content:t.content??""};case"thinking":return{type:"thinking",content:t.content??""};case"ai":return{type:"ai",content:t.content??""};case"tool_call":return{type:"tool_call",content:t.name??"",toolName:t.name,toolCallId:t.id,args:typeof t.args=="string"?t.args:JSON.stringify(t.args??{})};case"tool_result":return{type:"tool_result",content:t.output??"",toolName:t.name,toolCallId:t.id,artifacts:Array.isArray(t.artifacts)?t.artifacts:void 0,renderMode:t.render_mode??t.renderMode,html:t.html};case"done":return{type:"done",content:t.content??"",sessionId:t.sessionId??t.session_id??"",messageId:t.messageId??t.message_id};case"stop":return{type:"stop",content:t.content??"",sessionId:t.sessionId??t.session_id??"",messageId:t.messageId??t.message_id};case"error":return{type:"error",content:t.message??"Unknown error"};case"interrupt":return{type:"interrupt",content:t.content??"",interrupt:{interruptId:t.interruptId??t.interrupt_id??"",interruptType:t.interruptType??t.interrupt_type??"confirm",content:t.content??"",options:t.options,placeholder:t.placeholder,inputType:t.inputType??t.input_type,maxLength:t.maxLength??t.max_length,required:t.required,fields:t.fields,metadata:t.metadata}};default:return null}}async function m(r,e,t){if(!r.body){t(new Error("Response body is null"));return}const s=r.body.getReader(),o=new TextDecoder;let a="";try{for(;;){const{value:n,done:i}=await s.read();if(i)break;a+=o.decode(n,{stream:!0});let c;for(;(c=a.indexOf(`
|
|
4
4
|
|
|
5
|
-
`))!==-1;){const
|
|
5
|
+
`))!==-1;){const d=a.slice(0,c);a=a.slice(c+2);const l=y(d);l&&e(l)}}if(a.trim()){const n=y(a);n&&e(n)}}catch(n){(n==null?void 0:n.name)!=="AbortError"&&t(n instanceof Error?n:new Error(String(n)))}finally{try{s.releaseLock()}catch{}}}function b(r,e){const t=new AbortController,s=e.signal?p(e.signal,t.signal):t.signal,o={botId:r.getBotId(),message:e.message,sessionId:e.sessionId,stream:e.stream!==!1};return o.stream?r.streamPost("/chat",o,s).then(a=>{var n;if(!a.ok){(n=e.onError)==null||n.call(e,new Error(`HTTP ${a.status}`));return}return m(a,i=>{var c,d,l,h,f,g;i.type==="done"&&i.sessionId?(c=e.onDone)==null||c.call(e,{sessionId:i.sessionId,content:i.content,messageId:i.messageId}):i.type==="done"?(d=e.onDone)==null||d.call(e,{sessionId:"",content:i.content,messageId:i.messageId}):i.type==="stop"?((l=e.onMessage)==null||l.call(e,i),(h=e.onDone)==null||h.call(e,{sessionId:i.sessionId??"",content:i.content,messageId:i.messageId})):i.type==="error"?(f=e.onError)==null||f.call(e,new Error(i.content)):(g=e.onMessage)==null||g.call(e,i)},i=>{var c;return(c=e.onError)==null?void 0:c.call(e,i)})}).catch(a=>{var n;(a==null?void 0:a.name)!=="AbortError"&&((n=e.onError)==null||n.call(e,a instanceof Error?a:new Error(String(a))))}):r.post("/chat",o).then(a=>{var n;(n=e.onDone)==null||n.call(e,{sessionId:a.sessionId,content:a.content??"",messageId:a.messageId})}).catch(a=>{var n;(n=e.onError)==null||n.call(e,a instanceof Error?a:new Error(String(a)))}),t}function p(r,e){const t=new AbortController,s=()=>t.abort();return r.addEventListener("abort",s,{once:!0}),e.addEventListener("abort",s,{once:!0}),(r.aborted||e.aborted)&&t.abort(),t.signal}async function T(r){return(await r.post("/chat/sessions",{botId:r.getBotId()})).sessionId}function k(r){return{sessionId:r.threadId??r.sessionId,botId:r.botId,title:r.title,createTime:r.createTime,updateTime:r.updateTime}}async function S(r,e){const t=await r.get("/chat/conversations",{botId:r.getBotId(),page:(e==null?void 0:e.page)??1,size:(e==null?void 0:e.size)??20});return{...t,items:t.items.map(k)}}async function $(r,e,t){await r.patch(`/chat/conversations/${e}`,{title:t})}async function A(r,e){await r.del(`/chat/conversations/${e}`)}async function C(r,e){const t=await r.get(`/chat/conversations/${e}/messages`);return O(t)}function O(r){const e=[...r].sort((n,i)=>{const c=new Date(n.createTime??0).getTime()||0,d=new Date(i.createTime??0).getTime()||0;return c!==d?c-d:(n.seq??0)-(i.seq??0)}),t=new Map;for(const n of e)n.role==="tool"&&n.toolCallId&&t.set(n.toolCallId,n.content||"");const s=new Set,o=[],a=(n,i,c)=>{const d=o[o.length-1];(d==null?void 0:d.role)==="assistant"?(d.parts.push(...n),d.id=i):o.push({id:i,role:"assistant",parts:[...n],timestamp:c})};for(const n of e){if(n.role==="system")continue;const i=String(n.id??n.messageId??`msg_${Date.now()}`),c=new Date(n.createTime??"").getTime()||Date.now();if(n.role==="human"){o.push({id:i,role:"user",parts:[{type:"text",content:n.content??""}],timestamp:c});continue}if(n.role==="ai"){const d=P(n,t,s);d.length>0&&a(d,i,c);continue}if(n.role==="tool"){if(n.toolCallId&&s.has(n.toolCallId))continue;a([v(n)],i,c)}}return o}function P(r,e,t){const s=[];r.reasoning&&s.push({type:"thinking",content:r.reasoning}),r.content&&s.push({type:"text",content:r.content});for(const o of r.toolCalls??[])o.is_interrupt?(t.add(o.id),s.push(_(o,e))):s.push(E(o));return s}function _(r,e){const t=e.has(r.id),s=typeof r.args=="object"?r.args:{};return{type:"interrupt",interrupt:{interruptId:r.id??"",interruptType:s.interruptType??"input",content:s.content??"",options:s.options,placeholder:s.placeholder,inputType:s.inputType,maxLength:s.maxLength,required:s.required,fields:s.fields,metadata:s.metadata},resolved:t,response:t?{action:"submit",value:U(e.get(r.id))}:void 0}}function E(r){let e;try{e=typeof r.args=="string"?r.args:JSON.stringify(r.args??{})}catch{e=String(r.args??"{}")}return{type:"tool_call",toolName:r.name??"",toolCallId:r.id??"",args:e}}function v(r){var t,s,o;const e=Array.isArray(r.artifacts)?r.artifacts:void 0;return{type:"tool_result",toolName:r.toolName??"",toolCallId:r.toolCallId??"",content:r.content??"",artifacts:e,renderMode:((t=r.metadata)==null?void 0:t.renderMode)??((s=r.metadata)==null?void 0:s.render_mode),html:(o=r.metadata)==null?void 0:o.html}}function U(r){if(r!=null)try{return JSON.parse(r)}catch{return r}}async function x(r,e,t,s,o,a){const n=await r.streamPost(`/chat/interrupt/${e}/respond`,{sessionId:t,action:s.action,value:s.value});if(!n.ok){const i=new Error(`HTTP ${n.status}`);throw a==null||a(i),i}o&&await m(n,i=>{i.type==="error"?a==null||a(new Error(i.content)):o(i)},i=>a==null?void 0:a(i))}async function D(r){return r.get(`/chat/bots/${r.getBotId()}/skills`)}async function q(r,e){await r.post("/chat/stop",{sessionId:e})}function N(r){const e=new w(r);return{botId:e.getBotId(),getToken:()=>e.fetchToken(),createSession:()=>T(e),chat:s=>b(e,s),listConversations:s=>S(e,s),renameConversation:(s,o)=>$(e,s,o),deleteConversation:s=>A(e,s),getMessages:s=>C(e,s),respondInterrupt:(s,o,a,n,i)=>x(e,s,o,a,n,i),setToken:s=>e.setToken(s),feedback:async(s,o,a)=>{const n={type:o};o==="dislike"&&((a==null?void 0:a.reason)!=null&&(n.reason=a.reason),a!=null&&a.remark&&(n.remark=a.remark)),await e.post(`/chat/messages/${s}/feedback`,n)},cancelFeedback:async s=>{await e.del(`/chat/messages/${s}/feedback`)},listSkills:()=>D(e),stopGeneration:s=>q(e,s)}}exports.createSuperAgent=N;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare interface ArtifactFile {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export declare interface ChatEvent {
|
|
18
|
-
type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "error" | "interrupt";
|
|
18
|
+
type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "stop" | "error" | "interrupt";
|
|
19
19
|
content: string;
|
|
20
20
|
toolName?: string;
|
|
21
21
|
toolCallId?: string;
|
|
@@ -55,11 +55,14 @@ export declare function createSuperAgent(config: SDKConfig): SuperAgentSDK;
|
|
|
55
55
|
export declare interface FormField {
|
|
56
56
|
name: string;
|
|
57
57
|
label: string;
|
|
58
|
-
type: 'text' | 'number' | 'password' | 'textarea' | 'select' | 'date';
|
|
58
|
+
type: 'text' | 'number' | 'password' | 'textarea' | 'select' | 'multiSelect' | 'date';
|
|
59
59
|
required?: boolean;
|
|
60
60
|
placeholder?: string;
|
|
61
61
|
defaultValue?: any;
|
|
62
|
-
options?: string
|
|
62
|
+
options?: (string | {
|
|
63
|
+
value: string;
|
|
64
|
+
label: string;
|
|
65
|
+
})[];
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export declare interface InterruptEvent {
|
|
@@ -137,7 +140,16 @@ export declare interface SDKConfig {
|
|
|
137
140
|
token?: string;
|
|
138
141
|
}
|
|
139
142
|
|
|
143
|
+
declare interface SkillInfo {
|
|
144
|
+
code: string;
|
|
145
|
+
name: string;
|
|
146
|
+
displayName: string | null;
|
|
147
|
+
description: string | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
140
150
|
export declare interface SuperAgentSDK {
|
|
151
|
+
/** 当前 Bot ID(只读) */
|
|
152
|
+
readonly botId: number;
|
|
141
153
|
/** 获取 token(调用 POST {tokenGateway}/v1/token),自动设置内部凭证 */
|
|
142
154
|
getToken(): Promise<string>;
|
|
143
155
|
/** 创建新会话,返回 sessionId */
|
|
@@ -157,6 +169,10 @@ export declare interface SuperAgentSDK {
|
|
|
157
169
|
}): Promise<void>;
|
|
158
170
|
/** 取消消息反馈,DELETE /chat/messages/{messageId}/feedback */
|
|
159
171
|
cancelFeedback(messageId: string): Promise<void>;
|
|
172
|
+
/** 获取当前 bot 绑定的 skill 列表,GET /chat/bots/{botId}/skills */
|
|
173
|
+
listSkills(): Promise<SkillInfo[]>;
|
|
174
|
+
/** 停止生成(双信号:POST /chat/stop 广播 + abort SSE),半截回复保留 */
|
|
175
|
+
stopGeneration(sessionId: string): Promise<void>;
|
|
160
176
|
}
|
|
161
177
|
|
|
162
178
|
export declare interface UIMessage {
|