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 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("msg_123", "like");
314
+ await sdk.feedback(aiMsg.id, "like");
307
315
 
308
316
  // 踩(可附原因 + 备注)
309
- await sdk.feedback("msg_123", "dislike", {
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("msg_123");
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
- ### 5.10 后端接口对照
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: () => void;
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`,键为 `sa_active_conversation`。
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.2 会话恢复
1223
+ ### 10.3 会话恢复
1138
1224
 
1139
1225
  页面刷新或组件重新挂载时,会话状态自动恢复:
1140
1226
 
1141
- 1. 挂载时从 `localStorage` 读取上次活跃会话 ID(键 `sa_active_conversation`)。
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
- | "error";
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
- `chat()` 返回 `AbortController`;或传入外部 `AbortSignal`(SDK 会合并内部信号,任一触发即中断):
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 f(a){if(Array.isArray(a))return a.map(e=>f(e));if(a&&typeof a=="object"&&a.constructor===Object){const e={};for(const[t,n]of Object.entries(a)){const o=t.replace(/_([a-z])/g,(r,s)=>s.toUpperCase());e[o]=f(n)}return e}return a}const w="/api/agent/runtime/v1";class I{constructor(e){this.refreshing=null;const t=e.baseUrl??w;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`,n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({botId:this.botId})});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);const o=await n.json();if(o.code!=="200")throw new Error(o.message||`API error: ${o.code}`);const r=f(o.data);return this.token=r.token,r.appId&&!this.appId&&(this.appId=r.appId),r.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,n){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(n!=null&&n.params){const i=new URLSearchParams;for(const[l,d]of Object.entries(n.params))d!=null&&i.set(l,String(d));const c=i.toString();c&&(o+=`?${c}`)}const r=await fetch(o,{method:e,headers:this.headers(),body:n!=null&&n.body?JSON.stringify(n.body):void 0});if(r.status===401&&!(n!=null&&n.retry)&&await this.handleTokenRefresh())return this.request(e,t,{...n,retry:!0});if(!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);const s=await r.json();if(s.code!=="200")throw new Error(s.message||`API error: ${s.code}`);return f(s.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,n){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}`,r=await fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:n});return r.status===401&&await this.handleTokenRefresh()?fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:n}):r}}function y(a){const e=a.split(`
2
- `).map(n=>n.trim()).filter(n=>n.startsWith("data:")).map(n=>n.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"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(a,e,t){if(!a.body){t(new Error("Response body is null"));return}const n=a.body.getReader(),o=new TextDecoder;let r="";try{for(;;){const{value:s,done:i}=await n.read();if(i)break;r+=o.decode(s,{stream:!0});let c;for(;(c=r.indexOf(`
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 l=r.slice(0,c);r=r.slice(c+2);const d=y(l);d&&e(d)}}if(r.trim()){const s=y(r);s&&e(s)}}catch(s){(s==null?void 0:s.name)!=="AbortError"&&t(s instanceof Error?s:new Error(String(s)))}finally{try{n.releaseLock()}catch{}}}function k(a,e){const t=new AbortController,n=e.signal?b(e.signal,t.signal):t.signal,o={botId:a.getBotId(),message:e.message,sessionId:e.sessionId,stream:e.stream!==!1};return o.stream?a.streamPost("/chat",o,n).then(r=>{var s;if(!r.ok){(s=e.onError)==null||s.call(e,new Error(`HTTP ${r.status}`));return}return m(r,i=>{var c,l,d,h;i.type==="done"&&i.sessionId?(c=e.onDone)==null||c.call(e,{sessionId:i.sessionId,content:i.content,messageId:i.messageId}):i.type==="done"?(l=e.onDone)==null||l.call(e,{sessionId:"",content:i.content,messageId:i.messageId}):i.type==="error"?(d=e.onError)==null||d.call(e,new Error(i.content)):(h=e.onMessage)==null||h.call(e,i)},i=>{var c;return(c=e.onError)==null?void 0:c.call(e,i)})}).catch(r=>{var s;(r==null?void 0:r.name)!=="AbortError"&&((s=e.onError)==null||s.call(e,r instanceof Error?r:new Error(String(r))))}):a.post("/chat",o).then(r=>{var s;(s=e.onDone)==null||s.call(e,{sessionId:r.sessionId,content:r.content??"",messageId:r.messageId})}).catch(r=>{var s;(s=e.onError)==null||s.call(e,r instanceof Error?r:new Error(String(r)))}),t}function b(a,e){const t=new AbortController,n=()=>t.abort();return a.addEventListener("abort",n,{once:!0}),e.addEventListener("abort",n,{once:!0}),(a.aborted||e.aborted)&&t.abort(),t.signal}async function T(a){return(await a.post("/chat/sessions",{botId:a.getBotId()})).sessionId}function S(a){return{sessionId:a.threadId??a.sessionId,botId:a.botId,title:a.title,createTime:a.createTime,updateTime:a.updateTime}}async function p(a,e){const t=await a.get("/chat/conversations",{botId:a.getBotId(),page:(e==null?void 0:e.page)??1,size:(e==null?void 0:e.size)??20});return{...t,items:t.items.map(S)}}async function $(a,e,t){await a.patch(`/chat/conversations/${e}`,{title:t})}async function A(a,e){await a.del(`/chat/conversations/${e}`)}async function C(a,e){const t=await a.get(`/chat/conversations/${e}/messages`);return O(t)}function O(a){var n,o,r;const e=[],t=[...a].sort((s,i)=>(s.seq??0)-(i.seq??0));for(const s of t){const i=s.role;if(i==="system")continue;const c=s.messageId??`msg_${s.id}`,l=new Date(s.createTime??"").getTime()||Date.now();if(i==="human")e.push({id:c,role:"user",parts:[{type:"text",content:s.content??""}],timestamp:l});else if(i==="ai"){const d=[];s.reasoning&&d.push({type:"thinking",content:s.reasoning}),s.content&&d.push({type:"text",content:s.content});const h=s.toolCalls;if(h&&Array.isArray(h))for(const u of h){let g;try{g=typeof u.args=="string"?u.args:JSON.stringify(u.args??{})}catch{g=String(u.args??"{}")}d.push({type:"tool_call",toolName:u.name??"",toolCallId:u.id??"",args:g})}e.push({id:c,role:"assistant",parts:d,timestamp:l})}else if(i==="tool"){const d=Array.isArray(s.artifacts)?s.artifacts:void 0;e.push({id:c,role:"assistant",parts:[{type:"tool_result",toolName:s.toolName??"",toolCallId:s.toolCallId??"",content:s.content??"",artifacts:d,renderMode:((n=s.metadata)==null?void 0:n.renderMode)??((o=s.metadata)==null?void 0:o.render_mode),html:(r=s.metadata)==null?void 0:r.html}],timestamp:l})}}return e}async function _(a,e,t,n,o,r){const s=await a.streamPost(`/chat/interrupt/${e}/respond`,{sessionId:t,action:n.action,value:n.value});if(!s.ok){const i=new Error(`HTTP ${s.status}`);throw r==null||r(i),i}o&&await m(s,i=>{i.type==="error"?r==null||r(new Error(i.content)):o(i)},i=>r==null?void 0:r(i))}function v(a){const e=new I(a);return{getToken:()=>e.fetchToken(),createSession:()=>T(e),chat:n=>k(e,n),listConversations:n=>p(e,n),renameConversation:(n,o)=>$(e,n,o),deleteConversation:n=>A(e,n),getMessages:n=>C(e,n),respondInterrupt:(n,o,r,s,i)=>_(e,n,o,r,s,i),setToken:n=>e.setToken(n),feedback:async(n,o,r)=>{const s={type:o};o==="dislike"&&((r==null?void 0:r.reason)!=null&&(s.reason=r.reason),r!=null&&r.remark&&(s.remark=r.remark)),await e.post(`/chat/messages/${n}/feedback`,s)},cancelFeedback:async n=>{await e.del(`/chat/messages/${n}/feedback`)}}}exports.createSuperAgent=v;
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 {