super-agent-sdk 1.0.3 → 1.0.5

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
@@ -15,7 +15,8 @@ Super Agent Web SDK —— 为 `super-agent-service` 提供的一站式浏览器
15
15
  9. [会话管理](#9-会话管理)
16
16
  10. [中断恢复](#10-中断恢复)
17
17
  11. [消息类型](#11-消息类型)
18
- 12. [高级用法](#12-高级用法)
18
+ 12. [Human-in-the-Loop](#12-human-in-the-loop)
19
+ 13. [高级用法](#13-高级用法)
19
20
 
20
21
  ---
21
22
 
@@ -61,7 +62,11 @@ pnpm add react react-dom
61
62
  import { createSuperAgent } from "super-agent-sdk";
62
63
  import { mount } from "super-agent-sdk/widget";
63
64
 
64
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
65
+ const sdk = createSuperAgent({
66
+ baseUrl: "/api/v1",
67
+ botId: 1,
68
+ appId: "your_app_id",
69
+ });
65
70
  // Token 自动获取,无需手动设置
66
71
  const widget = mount("#chat", { sdk });
67
72
  widget.open();
@@ -81,46 +86,46 @@ function createSuperAgent(config: SDKConfig): SuperAgentSDK;
81
86
  export interface SDKConfig {
82
87
  baseUrl: string; // e.g. "https://agent.example.com/api/v1"
83
88
  botId: number; // bot ID
89
+ appId: string; // 应用密钥,用于换取 token
84
90
  }
85
91
  ```
86
92
 
87
- 只需提供 `baseUrl` 与 `botId` 即可。SDK 不要求在创建时传入 `appId` / `token`——组件挂载后会自动调用 `sdk.getToken()` 获取凭证并内部保存,无需手动设置。
93
+ 只需提供 `baseUrl`、`botId` 与 `appId` 即可。SDK 不要求在创建时传入 `token`——组件挂载后会自动调用 `sdk.getToken()` 获取 token 并内部保存,无需手动设置。
88
94
 
89
95
  ```ts
90
96
  const sdk = createSuperAgent({
91
97
  baseUrl: "https://agent.example.com/api/v1",
92
98
  botId: 1,
99
+ appId: "your_app_id",
93
100
  });
94
101
  ```
95
102
 
96
103
  凭证获取流程:
97
104
 
98
105
  ```
99
- createSuperAgent({ baseUrl, botId })
106
+ createSuperAgent({ baseUrl, botId, appId })
100
107
 
101
108
  Widget 挂载时自动调用 sdk.getToken()
102
109
 
103
- POST /token { botId }
110
+ POST /api/token(无参数)
104
111
 
105
- 后端返回 { app_id, token }
112
+ 后端返回 { token }
106
113
 
107
- SDK 内部自动保存凭证
114
+ SDK 内部自动保存 token
108
115
 
109
116
  ready
110
117
  ```
111
118
 
112
- 出于安全考虑,`app_key` 应永远留在后端,浏览器端只持有短时有效的 Token
119
+ `appId` 用于换取访问 token,浏览器端后续请求只持有短时有效的 token
113
120
 
114
121
  ---
115
122
 
116
123
  ## 5. API 参考
117
124
 
118
- > **字段命名映射**:后端接口返回的是 snake_case 字段,SDK 内部统一转换为 camelCase 后对外暴露。
125
+ > **字段命名**:后端接口返回 camelCase 字段,SDK 直接使用,无需转换。
119
126
  >
120
- > - 后端返回 `thread_id`(snake_case)→ SDK 内部映射为 `sessionId`
121
- > - 后端返回 `bot_id` SDK 映射为 `botId`
122
- > - SDK 所有公开 API 均使用 camelCase(`sessionId`、`botId`)
123
- > - SDK 发起请求时同样使用 camelCase(如 `{ botId, sessionId, message }`)
127
+ > - 后端返回 `sessionId`、`botId`、`createTime`、`updateTime`
128
+ > - SDK 所有公开 API 均使用 camelCase
124
129
 
125
130
  ### 5.1 SDK 实例
126
131
 
@@ -134,10 +139,10 @@ export interface SuperAgentSDK {
134
139
  renameConversation(sessionId: string, title: string): Promise<void>;
135
140
  deleteConversation(sessionId: string): Promise<void>;
136
141
  getMessages(sessionId: string): Promise<UIMessage[]>;
137
- getToken(): Promise<{ appId: string; token: string }>;
138
- setToken(appId: string, token: string): void;
139
- feedback(messageId: string, type: "like" | "dislike"): void;
140
- onFeedback?: (messageId: string, type: "like" | "dislike") => void;
142
+ getToken(): Promise<string>;
143
+ setToken(token: string): void;
144
+ feedback(messageId: string, type: "like" | "dislike", options?: { reason?: number; remark?: string }): Promise<void>;
145
+ cancelFeedback(messageId: string): Promise<void>;
141
146
  }
142
147
  ```
143
148
 
@@ -146,8 +151,8 @@ export interface SuperAgentSDK {
146
151
  创建新会话,返回 `sessionId`。聊天前必须先创建会话。
147
152
 
148
153
  - SDK 调用 `POST /chat/sessions`,请求体为 `{ botId }`
149
- - 后端返回 `{ session_id: "..." }`
150
- - SDK 解析后返回 `sessionId` 字符串
154
+ - 后端返回 `{ sessionId: "..." }`
155
+ - SDK 直接返回 `sessionId` 字符串
151
156
 
152
157
  ```ts
153
158
  const sessionId = await sdk.createSession();
@@ -158,7 +163,7 @@ const sessionId = await sdk.createSession();
158
163
  流式聊天。**`sessionId` 为必填**,必须先调用 `createSession()` 获取。返回 `AbortController`,可随时中断。
159
164
 
160
165
  - SDK 发送 `POST /chat`,请求体为 `{ botId, sessionId, message, stream }`
161
- - 后端 SSE 在 `done` 事件中返回 `session_id` → SDK 暴露为 `sessionId`
166
+ - 后端 SSE 在 `done` 事件中返回 `sessionId`
162
167
 
163
168
  ```ts
164
169
  export interface ChatOptions {
@@ -245,8 +250,7 @@ const { items, total } = await sdk.listConversations({ page: 1, size: 20 });
245
250
  ```
246
251
 
247
252
  - SDK 调用 `GET /chat/conversations?botId=1&page=1&size=20`
248
- - 后端返回的每一项使用 `thread_id`、`bot_id`、`create_time`、`update_time` 字段
249
- - SDK 将其映射为 `Conversation { sessionId, botId, title, createTime, updateTime }`
253
+ - 后端返回的每一项为 `{ sessionId, botId, title, createTime, updateTime }`
250
254
 
251
255
  ### 5.5 getMessages()
252
256
 
@@ -266,67 +270,69 @@ await sdk.deleteConversation(sessionId);
266
270
  ### 5.7 getToken()
267
271
 
268
272
  ```typescript
269
- sdk.getToken(): Promise<{ appId: string; token: string }>
273
+ sdk.getToken(): Promise<string>
270
274
  ```
271
275
 
272
- 自动获取并保存访问凭证:
276
+ 自动获取并保存访问 token:
273
277
 
274
- - SDK 调用 `POST /token`,请求体为 `{ botId }`(无需鉴权头)
275
- - 后端返回 `{ app_id, token }`
276
- - SDK 自动将凭证保存在内部
278
+ - SDK 调用 `POST /api/token`(固定路径,不拼接 `baseUrl`),无需请求参数
279
+ - 后端返回 `{ token }`
280
+ - SDK 自动将 token 保存在内部
277
281
  - Widget 在挂载时自动调用此方法 —— 通常无需手动调用
278
282
 
279
283
  ```ts
280
- const { appId, token } = await sdk.getToken();
284
+ const token = await sdk.getToken();
281
285
  ```
282
286
 
283
287
  ### 5.8 setToken()
284
288
 
285
289
  ```typescript
286
- sdk.setToken(appId: string, token: string): void
290
+ sdk.setToken(token: string): void
287
291
  ```
288
292
 
289
- 手动设置凭证(用于凭证由外部获取、或测试等场景)。正常情况下无需调用,Widget 挂载时会自动通过 `getToken()` 获取凭证。
293
+ 手动设置 token(用于 token 由外部获取、或测试等场景)。正常情况下无需调用,Widget 挂载时会自动通过 `getToken()` 获取 token。
290
294
 
291
295
  ```ts
292
- sdk.setToken("<APP_ID>", "<TOKEN>");
296
+ sdk.setToken("<TOKEN>");
293
297
  ```
294
298
 
295
- ### 5.9 feedback() / onFeedback
299
+ ### 5.9 feedback() / cancelFeedback()
296
300
 
297
- 点赞 / 踩:调用 `sdk.feedback()`,SDK 会回调你注册的 `sdk.onFeedback`。
301
+ 消息反馈(赞/踩)。调用 `sdk.feedback()` 发送 `POST /chat/messages/{messageId}/feedback`;调用 `sdk.cancelFeedback()` 发送 `DELETE` 取消反馈。
298
302
 
299
303
  ```ts
300
- sdk.onFeedback = (messageId, type) => {
301
- // type: 'like' | 'dislike'
302
- console.log(`Feedback: ${type} on message ${messageId}`);
303
- // 上报到自己的业务后端
304
- };
304
+ // 点赞
305
+ await sdk.feedback("msg_123", "like");
305
306
 
306
- sdk.feedback("msg_123", "like");
307
+ // 踩(可附原因 + 备注)
308
+ await sdk.feedback("msg_123", "dislike", {
309
+ reason: 1, // 1=事实错误 2=逻辑问题 3=不相关 4=信息过时 5=冗长啰嗦 6=难以理解
310
+ remark: "时间描述有误",
311
+ });
312
+
313
+ // 取消反馈
314
+ await sdk.cancelFeedback("msg_123");
307
315
  ```
308
316
 
309
- 组件内置的点赞 / 踩按钮也会经过 `sdk.feedback()`,因此只需注册一次 `onFeedback` 即可统一接收所有反馈。
317
+ 组件内置的点赞/踩按钮已自动调用 `feedback()`/`cancelFeedback()`:
318
+ - 点击已选中的按钮 → 取消(DELETE)
319
+ - 点击另一个按钮 → 切换(POST)
320
+ - 点踩时弹出原因选择面板(可选填原因 + 备注后提交)
310
321
 
311
322
  ### 5.10 后端接口对照
312
323
 
313
324
  SDK 方法 → 后端接口的完整映射:
314
325
 
315
- | SDK 方法 | HTTP 请求 | 请求体(camelCase) | 后端返回(snake_case) | SDK 返回 |
316
- | --------------------- | ------------------------- | --------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------ |
317
- | `getToken()` | `POST /token` | `{ botId }` | `{ app_id, token }` | `{ appId, token }` |
318
- | `createSession()` | `POST /chat/sessions` | `{ botId }` | `{ session_id }` | `sessionId` 字符串 |
319
- | `chat()` | `POST /chat` | `{ botId, sessionId, message, stream }` | SSE 流,`done` 事件含 `session_id` | `onDone` 中暴露 `sessionId` |
320
- | `listConversations()` | `GET /chat/conversations` | 查询参数 `botId`、`page`、`size` | `{ thread_id, bot_id, create_time, update_time }[]` | `Conversation { sessionId, botId, title, createTime, updateTime }` |
321
-
322
- 字段命名转换规则:
326
+ > `getToken()` 外,其余接口请求均携带 `X-App-Id` `X-Token` 请求头进行鉴权。
323
327
 
324
- | 后端(snake_case) | SDK(camelCase) |
325
- | ------------------ | ---------------- |
326
- | `thread_id` | `sessionId` |
327
- | `bot_id` | `botId` |
328
- | `create_time` | `createTime` |
329
- | `update_time` | `updateTime` |
328
+ | SDK 方法 | HTTP 请求 | 请求体 | 后端返回 |
329
+ | --------------------- | ------------------------- | --------------------------------------- | ----------------------------------------------- |
330
+ | `getToken()` | `POST {gateway}/v1/token` | `{ botId }` | `{ token, appId }` |
331
+ | `createSession()` | `POST /chat/sessions` | `{ botId }` | `{ sessionId }` |
332
+ | `chat()` | `POST /chat` | `{ botId, sessionId, message, stream }` | SSE 流,`done` 事件含 `sessionId` |
333
+ | `listConversations()` | `GET /chat/conversations` | 查询参数 `botId`、`page`、`size` | `{ items: [{ sessionId, botId, ... }], total }` |
334
+ | `feedback()` | `POST /chat/messages/{messageId}/feedback` | `{ type, reason?, remark? }` | `null` |
335
+ | `cancelFeedback()` | `DELETE /chat/messages/{messageId}/feedback` | 无 body | `null` |
330
336
 
331
337
  ---
332
338
 
@@ -366,7 +372,11 @@ export interface WidgetInstance {
366
372
  import { createSuperAgent } from "super-agent-sdk";
367
373
  import { mount } from "super-agent-sdk/widget";
368
374
 
369
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
375
+ const sdk = createSuperAgent({
376
+ baseUrl: "/api/v1",
377
+ botId: 1,
378
+ appId: "your_app_id",
379
+ });
370
380
 
371
381
  const widget = mount("#chat-root", {
372
382
  sdk,
@@ -874,8 +884,12 @@ import type {
874
884
  WelcomeScreenProps,
875
885
  } from "super-agent-sdk/widget";
876
886
 
877
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
878
- sdk.setToken("demo_app", "demo_token");
887
+ const sdk = createSuperAgent({
888
+ baseUrl: "/api/v1",
889
+ botId: 1,
890
+ appId: "your_app_id",
891
+ });
892
+ sdk.setToken("demo_token");
879
893
 
880
894
  function CustomHeader({ title, onClose, onToggleThreadList }: HeaderProps) {
881
895
  return (
@@ -1192,17 +1206,118 @@ export interface ChatEvent {
1192
1206
 
1193
1207
  ---
1194
1208
 
1195
- ## 12. 高级用法
1209
+ ## 12. Human-in-the-Loop
1210
+
1211
+ Agent 执行中需要人类介入时(确认敏感操作、选择方案、补充信息、审核内容等),SDK 提供统一的中断交互机制。
1212
+
1213
+ ### 12.1 核心流程
1214
+
1215
+ ```
1216
+ Agent 执行中
1217
+ → 后端发送 SSE interrupt 事件
1218
+ → SDK 渲染中断交互卡片(按 interruptType 分发)
1219
+ → 用户完成操作
1220
+ → SDK 调用 respondInterrupt() 回传响应
1221
+ → Agent 基于响应继续执行
1222
+ ```
1223
+
1224
+ ### 12.2 interrupt 事件
1225
+
1226
+ ```json
1227
+ {
1228
+ "type": "interrupt",
1229
+ "interruptId": "int_001",
1230
+ "interruptType": "confirm",
1231
+ "content": "即将执行转账 ¥500,确认执行?",
1232
+ "options": ["确认", "取消"]
1233
+ }
1234
+ ```
1235
+
1236
+ ### 12.3 响应中断
1237
+
1238
+ ```ts
1239
+ await sdk.respondInterrupt(interruptId, {
1240
+ action: "confirm",
1241
+ });
1242
+ ```
1243
+
1244
+ ### 12.4 Widget 自动处理
1245
+
1246
+ Widget 内置中断卡片渲染,收到 `interrupt` 事件后自动展示对应交互卡片,用户操作后自动回传,无需业务方处理:
1247
+
1248
+ ```ts
1249
+ mount("#chat", { sdk }).open();
1250
+ // 遇到 interrupt 时:自动渲染卡片 → 用户点击 → 自动 respondInterrupt → Agent 继续
1251
+ ```
1252
+
1253
+ 也可通过插槽自定义中断卡片:
1254
+
1255
+ ```ts
1256
+ mount("#chat", {
1257
+ sdk,
1258
+ slots: {
1259
+ InterruptCard: MyInterruptCard, // 自定义全部类型
1260
+ },
1261
+ });
1262
+ ```
1263
+
1264
+ ### 12.5 纯 API 模式手动处理
1265
+
1266
+ ```ts
1267
+ sdk.chat({
1268
+ sessionId,
1269
+ message: "帮我转账 ¥500",
1270
+ onMessage: (e) => {
1271
+ if (e.type === "interrupt" && e.interrupt) {
1272
+ // 自定义 UI 处理
1273
+ const ok = window.confirm(e.interrupt.content);
1274
+ sdk.respondInterrupt(e.interrupt.interruptId, {
1275
+ action: ok ? "confirm" : "cancel",
1276
+ });
1277
+ }
1278
+ },
1279
+ });
1280
+ ```
1281
+
1282
+ ### 12.6 interruptType 一览
1283
+
1284
+ | interruptType | 交互 | action | value 示例 |
1285
+ |---------------|------|--------|-----------|
1286
+ | `confirm` | 二次确认 | `confirm` / `cancel` | — |
1287
+ | `select` | 单选 | `select` | `"flight_a"` |
1288
+ | `multiSelect` | 多选 | `submit` | `["name", "phone"]` |
1289
+ | `input` | 文本输入 | `submit` | `"123456"` |
1290
+ | `form` | 表单 | `submit` | `{ name: "张三" }` |
1291
+ | `review` | 内容审阅 | `approve` / `reject` | 修改后内容 |
1292
+ | `approve` | 审批流 | `approve` / `reject` | 驳回原因 |
1293
+ | `upload` | 文件上传 | `submit` | `{ fileId, fileName }` |
1294
+ | `image` | 图片选择 | `select` / `reject` | 图片索引 |
1295
+ | `auth` | 授权请求 | `authorized` / `skip` | — |
1296
+ | `captcha` | 人机验证 | `verified` | `"captcha_token"` |
1297
+ | `decision` | 流程分支 | `decide` | `"retry"` 等 |
1298
+ | `rating` | 评分 | `rate` | `4` |
1299
+ | `date` | 日期选择 | `submit` | `"2026-08-21T14:00:00"` |
1300
+ | `location` | 位置选择 | `submit` | `{ address, lat, lng }` |
1301
+
1302
+ > 每个 `interruptType` 的完整字段定义见 [API 文档](./API.md) 与 [HITL 设计文档](../docs/superpowers/specs/2026-08-18-human-in-the-loop-design.md)。
1303
+
1304
+ ---
1305
+
1306
+ ## 13. 高级用法
1196
1307
 
1197
- ### 12.1 纯 API 模式(不用 Widget)
1308
+ ### 13.1 纯 API 模式(不用 Widget)
1198
1309
 
1199
1310
  不挂载组件,仅使用 API 层构建自己的 UI:
1200
1311
 
1201
1312
  ```ts
1202
1313
  import { createSuperAgent } from "super-agent-sdk";
1203
1314
 
1204
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
1205
- sdk.setToken("<APP_ID>", "<TOKEN>");
1315
+ const sdk = createSuperAgent({
1316
+ baseUrl: "/api/v1",
1317
+ botId: 1,
1318
+ appId: "your_app_id",
1319
+ });
1320
+ sdk.setToken("<TOKEN>");
1206
1321
 
1207
1322
  const sessionId = await sdk.createSession();
1208
1323
 
@@ -1218,7 +1333,7 @@ sdk.chat({
1218
1333
  });
1219
1334
  ```
1220
1335
 
1221
- ### 12.2 使用 Hooks
1336
+ ### 13.2 使用 Hooks
1222
1337
 
1223
1338
  从 `super-agent-sdk/widget` 导出 `useChat`、`useConversations`,可在自己的 React 应用中复用:
1224
1339
 
@@ -1290,11 +1405,11 @@ function MyChat({ sdk }: { sdk: SuperAgentSDK }) {
1290
1405
 
1291
1406
  > `useChat` 在 `sessionId` 为空时会自动调用 `sdk.createSession()` 创建新会话,无需手动处理。
1292
1407
 
1293
- ### 12.3 Token 续期
1408
+ ### 13.3 Token 续期
1294
1409
 
1295
- 请求返回 `401` 时,SDK 会自动重新调用 `POST /token` 获取新凭证并重试(并发请求合并为一次刷新),无需手动处理。
1410
+ 请求返回 `401` 时,SDK 自动重新调用 `POST /api/token` 获取新 token。
1296
1411
 
1297
- ### 12.4 取消请求
1412
+ ### 13.4 取消请求
1298
1413
 
1299
1414
  `chat()` 返回 `AbortController`;或传入外部 `AbortSignal`(SDK 会合并内部信号,任一触发即中断):
1300
1415
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h(s){if(Array.isArray(s))return s.map(e=>h(e));if(s&&typeof s=="object"&&s.constructor===Object){const e={};for(const[r,t]of Object.entries(s)){const o=r.replace(/_([a-z])/g,(n,a)=>a.toUpperCase());e[o]=h(t)}return e}return s}class g{constructor(e){this.refreshing=null,this.baseUrl=e.baseUrl.replace(/\/$/,""),this.appId=e.appId??"",this.token=e.token??"",this.botId=e.botId}getBotId(){return this.botId}setToken(e,r){this.appId=e,this.token=r}async postPublic(e,r){const t=`${this.baseUrl}${e}`,o=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:r?JSON.stringify(r):void 0});if(!o.ok)throw new Error(`HTTP ${o.status}: ${o.statusText}`);const n=await o.json();if(n.code!=="200")throw new Error(n.message||`API error: ${n.code}`);return h(n.data)}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()=>{const e=await this.postPublic("/token",{botId:this.botId});this.appId=e.appId,this.token=e.token})(),await this.refreshing,!0}catch{return!1}finally{this.refreshing=null}}async request(e,r,t){if((!this.appId||!this.token)&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");let o=`${this.baseUrl}${r}`;if(t!=null&&t.params){const c=new URLSearchParams;for(const[l,d]of Object.entries(t.params))d!=null&&c.set(l,String(d));const i=c.toString();i&&(o+=`?${i}`)}const n=await fetch(o,{method:e,headers:this.headers(),body:t!=null&&t.body?JSON.stringify(t.body):void 0});if(n.status===401&&!(t!=null&&t.retry)&&await this.handleTokenRefresh())return this.request(e,r,{...t,retry:!0});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);const a=await n.json();if(a.code!=="200")throw new Error(a.message||`API error: ${a.code}`);return h(a.data)}get(e,r){return this.request("GET",e,{params:r})}post(e,r){return this.request("POST",e,{body:r})}patch(e,r){return this.request("PATCH",e,{body:r})}del(e){return this.request("DELETE",e)}async streamPost(e,r,t){if((!this.appId||!this.token)&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");const o=`${this.baseUrl}${e}`,n=await fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(r),signal:t});return n.status===401&&await this.handleTokenRefresh()?fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(r),signal:t}):n}}function f(s){const e=s.split(`
2
- `).map(t=>t.trim()).filter(t=>t.startsWith("data:")).map(t=>t.slice(5).trim());if(!e.length)return null;let r;try{r=JSON.parse(e.join(`
3
- `))}catch{return null}switch(r.type){case"user":return{type:"user",content:r.content??""};case"thinking":return{type:"thinking",content:r.content??""};case"ai":return{type:"ai",content:r.content??""};case"tool_call":return{type:"tool_call",content:r.name??"",toolName:r.name,toolCallId:r.id,args:typeof r.args=="string"?r.args:JSON.stringify(r.args??{})};case"tool_result":return{type:"tool_result",content:r.output??"",toolName:r.name,toolCallId:r.id};case"done":return{type:"done",content:r.content??"",sessionId:r.sessionId??r.session_id??""};case"error":return{type:"error",content:r.message??"Unknown error"};default:return null}}async function y(s,e,r){if(!s.body){r(new Error("Response body is null"));return}const t=s.body.getReader(),o=new TextDecoder;let n="";try{for(;;){const{value:a,done:c}=await t.read();if(c)break;n+=o.decode(a,{stream:!0});let i;for(;(i=n.indexOf(`
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(`
4
4
 
5
- `))!==-1;){const l=n.slice(0,i);n=n.slice(i+2);const d=f(l);d&&e(d)}}if(n.trim()){const a=f(n);a&&e(a)}}catch(a){(a==null?void 0:a.name)!=="AbortError"&&r(a instanceof Error?a:new Error(String(a)))}finally{try{t.releaseLock()}catch{}}}function m(s,e){const r=new AbortController,t=e.signal?I(e.signal,r.signal):r.signal,o={botId:s.getBotId(),message:e.message,sessionId:e.sessionId,stream:e.stream!==!1};return o.stream?s.streamPost("/chat",o,t).then(n=>{var a;if(!n.ok){(a=e.onError)==null||a.call(e,new Error(`HTTP ${n.status}`));return}return y(n,c=>{var i,l,d,u;c.type==="done"&&c.sessionId?(i=e.onDone)==null||i.call(e,{sessionId:c.sessionId,content:c.content}):c.type==="done"?(l=e.onDone)==null||l.call(e,{sessionId:"",content:c.content}):c.type==="error"?(d=e.onError)==null||d.call(e,new Error(c.content)):(u=e.onMessage)==null||u.call(e,c)},c=>{var i;return(i=e.onError)==null?void 0:i.call(e,c)})}).catch(n=>{var a;(n==null?void 0:n.name)!=="AbortError"&&((a=e.onError)==null||a.call(e,n instanceof Error?n:new Error(String(n))))}):s.post("/chat",o).then(n=>{var a;(a=e.onDone)==null||a.call(e,{sessionId:n.sessionId,content:n.content??""})}).catch(n=>{var a;(a=e.onError)==null||a.call(e,n instanceof Error?n:new Error(String(n)))}),r}function I(s,e){const r=new AbortController,t=()=>r.abort();return s.addEventListener("abort",t,{once:!0}),e.addEventListener("abort",t,{once:!0}),(s.aborted||e.aborted)&&r.abort(),r.signal}async function b(s){return(await s.post("/chat/sessions",{botId:s.getBotId()})).sessionId}function w(s){return{sessionId:s.threadId??s.sessionId,botId:s.botId,title:s.title,createTime:s.createTime,updateTime:s.updateTime}}async function k(s,e){const r=await s.get("/chat/conversations",{botId:s.getBotId(),page:(e==null?void 0:e.page)??1,size:(e==null?void 0:e.size)??20});return{...r,items:r.items.map(w)}}async function T(s,e,r){await s.patch(`/chat/conversations/${e}`,{title:r})}async function S(s,e){await s.del(`/chat/conversations/${e}`)}async function E(s,e){const r=await s.get(`/chat/conversations/${e}/messages`);return C(r)}function C(s){const e=[],r=[...s].sort((t,o)=>(t.seq??0)-(o.seq??0));for(const t of r){const o=t.role;if(o==="system")continue;const n=t.messageId??`msg_${t.id}`,a=new Date(t.createTime??"").getTime()||Date.now();if(o==="human")e.push({id:n,role:"user",parts:[{type:"text",content:t.content??""}],timestamp:a});else if(o==="ai"){const c=[];t.reasoning&&c.push({type:"thinking",content:t.reasoning}),t.content&&c.push({type:"text",content:t.content});const i=t.toolCalls;if(i&&Array.isArray(i))for(const l of i){let d;try{d=typeof l.args=="string"?l.args:JSON.stringify(l.args??{})}catch{d=String(l.args??"{}")}c.push({type:"tool_call",toolName:l.name??"",toolCallId:l.id??"",args:d})}e.push({id:n,role:"assistant",parts:c,timestamp:a})}else o==="tool"&&e.push({id:n,role:"assistant",parts:[{type:"tool_result",toolName:t.toolName??"",toolCallId:t.toolCallId??"",content:t.content??""}],timestamp:a})}return e}function P(s){const e=new g(s),r={getToken:async()=>{const t=await e.postPublic("/token",{botId:e.getBotId()});return e.setToken(t.appId,t.token),t},createSession:()=>b(e),chat:t=>m(e,t),listConversations:t=>k(e,t),renameConversation:(t,o)=>T(e,t,o),deleteConversation:t=>S(e,t),getMessages:t=>E(e,t),setToken:(t,o)=>e.setToken(t,o),feedback:(t,o)=>{var n;(n=r.onFeedback)==null||n.call(r,t,o)}};return r}exports.createSuperAgent=P;
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??""})}).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;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,31 @@
1
+ /** 通用产物(INTERFACE 约定:type + data,适配 table/chart/file/text/image/html/custom) */
2
+ export declare interface Artifact {
3
+ type: string;
4
+ data: Record<string, any>;
5
+ }
6
+
7
+ export declare interface ArtifactFile {
8
+ id: string;
9
+ type: "html" | "pdf" | "docx" | string;
10
+ filename: string;
11
+ url: string;
12
+ mime?: string;
13
+ size?: number;
14
+ summary?: string;
15
+ }
16
+
1
17
  export declare interface ChatEvent {
2
- type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "error";
18
+ type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "error" | "interrupt";
3
19
  content: string;
4
20
  toolName?: string;
5
21
  toolCallId?: string;
6
22
  args?: string;
7
23
  sessionId?: string;
24
+ messageId?: string;
25
+ interrupt?: InterruptEvent;
26
+ artifacts?: Artifact[];
27
+ renderMode?: string;
28
+ html?: string;
8
29
  }
9
30
 
10
31
  export declare interface ChatOptions {
@@ -16,6 +37,7 @@ export declare interface ChatOptions {
16
37
  onDone?: (result: {
17
38
  sessionId: string;
18
39
  content: string;
40
+ messageId?: string;
19
41
  }) => void;
20
42
  signal?: AbortSignal;
21
43
  }
@@ -30,6 +52,44 @@ export declare interface Conversation {
30
52
 
31
53
  export declare function createSuperAgent(config: SDKConfig): SuperAgentSDK;
32
54
 
55
+ export declare interface FormField {
56
+ name: string;
57
+ label: string;
58
+ type: 'text' | 'number' | 'password' | 'textarea' | 'select' | 'date';
59
+ required?: boolean;
60
+ placeholder?: string;
61
+ defaultValue?: any;
62
+ options?: string[];
63
+ }
64
+
65
+ export declare interface InterruptEvent {
66
+ interruptId: string;
67
+ interruptType: InterruptType;
68
+ content: string;
69
+ options?: (string | InterruptOption)[];
70
+ placeholder?: string;
71
+ inputType?: 'text' | 'number' | 'password' | 'textarea';
72
+ maxLength?: number;
73
+ required?: boolean;
74
+ fields?: FormField[];
75
+ metadata?: Record<string, any>;
76
+ }
77
+
78
+ export declare interface InterruptOption {
79
+ value: string;
80
+ label: string;
81
+ description?: string;
82
+ icon?: string;
83
+ danger?: boolean;
84
+ }
85
+
86
+ export declare interface InterruptResponse {
87
+ action: string;
88
+ value?: any;
89
+ }
90
+
91
+ export declare type InterruptType = 'confirm' | 'select' | 'multiSelect' | 'input' | 'form' | 'approve';
92
+
33
93
  export declare interface ListConversationsParams {
34
94
  page?: number;
35
95
  size?: number;
@@ -56,24 +116,30 @@ export declare type MessagePart = {
56
116
  toolName: string;
57
117
  toolCallId: string;
58
118
  content: string;
119
+ artifacts?: Artifact[];
120
+ renderMode?: string;
121
+ html?: string;
59
122
  } | {
60
123
  type: "error";
61
124
  content: string;
125
+ } | {
126
+ type: "interrupt";
127
+ interrupt: InterruptEvent;
128
+ resolved?: boolean;
129
+ response?: InterruptResponse;
62
130
  };
63
131
 
64
132
  export declare interface SDKConfig {
65
- baseUrl: string;
66
133
  botId: number;
134
+ tokenGateway: string;
135
+ baseUrl?: string;
67
136
  appId?: string;
68
137
  token?: string;
69
138
  }
70
139
 
71
140
  export declare interface SuperAgentSDK {
72
- /** 获取 token(调用 POST /token),自动设置内部凭证 */
73
- getToken(): Promise<{
74
- appId: string;
75
- token: string;
76
- }>;
141
+ /** 获取 token(调用 POST {tokenGateway}/v1/token),自动设置内部凭证 */
142
+ getToken(): Promise<string>;
77
143
  /** 创建新会话,返回 sessionId */
78
144
  createSession(): Promise<string>;
79
145
  chat(options: ChatOptions): AbortController;
@@ -81,9 +147,16 @@ export declare interface SuperAgentSDK {
81
147
  renameConversation(sessionId: string, title: string): Promise<void>;
82
148
  deleteConversation(sessionId: string): Promise<void>;
83
149
  getMessages(sessionId: string): Promise<UIMessage[]>;
84
- setToken(appId: string, token: string): void;
85
- feedback(sessionId: string, type: "like" | "dislike"): void;
86
- onFeedback?: (sessionId: string, type: "like" | "dislike") => void;
150
+ /** 响应中断事件(返回 SSE 流,后端继续推送后续事件) */
151
+ respondInterrupt(interruptId: string, sessionId: string, response: InterruptResponse, onMessage?: (event: ChatEvent) => void, onError?: (error: Error) => void): Promise<void>;
152
+ setToken(token: string): void;
153
+ /** 消息反馈(赞/踩),POST /chat/messages/{messageId}/feedback */
154
+ feedback(messageId: string, type: "like" | "dislike", options?: {
155
+ reason?: number;
156
+ remark?: string;
157
+ }): Promise<void>;
158
+ /** 取消消息反馈,DELETE /chat/messages/{messageId}/feedback */
159
+ cancelFeedback(messageId: string): Promise<void>;
87
160
  }
88
161
 
89
162
  export declare interface UIMessage {