super-agent-sdk 1.0.2 → 1.0.4

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,8 +139,8 @@ 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;
142
+ getToken(): Promise<string>;
143
+ setToken(token: string): void;
139
144
  feedback(messageId: string, type: "like" | "dislike"): void;
140
145
  onFeedback?: (messageId: string, type: "like" | "dislike") => void;
141
146
  }
@@ -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,30 +270,30 @@ 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
299
  ### 5.9 feedback() / onFeedback
@@ -312,21 +316,14 @@ sdk.feedback("msg_123", "like");
312
316
 
313
317
  SDK 方法 → 后端接口的完整映射:
314
318
 
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
- 字段命名转换规则:
319
+ > `getToken()` 外,其余接口请求均携带 `X-App-Id` `X-Token` 请求头进行鉴权。
323
320
 
324
- | 后端(snake_case) | SDK(camelCase) |
325
- | ------------------ | ---------------- |
326
- | `thread_id` | `sessionId` |
327
- | `bot_id` | `botId` |
328
- | `create_time` | `createTime` |
329
- | `update_time` | `updateTime` |
321
+ | SDK 方法 | HTTP 请求 | 请求体 | 后端返回 |
322
+ | --------------------- | ------------------------- | --------------------------------------- | ----------------------------------------------- |
323
+ | `getToken()` | `POST /api/token` | 无参数 | `{ token }` |
324
+ | `createSession()` | `POST /chat/sessions` | `{ botId }` | `{ sessionId }` |
325
+ | `chat()` | `POST /chat` | `{ botId, sessionId, message, stream }` | SSE 流,`done` 事件含 `sessionId` |
326
+ | `listConversations()` | `GET /chat/conversations` | 查询参数 `botId`、`page`、`size` | `{ items: [{ sessionId, botId, ... }], total }` |
330
327
 
331
328
  ---
332
329
 
@@ -366,7 +363,11 @@ export interface WidgetInstance {
366
363
  import { createSuperAgent } from "super-agent-sdk";
367
364
  import { mount } from "super-agent-sdk/widget";
368
365
 
369
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
366
+ const sdk = createSuperAgent({
367
+ baseUrl: "/api/v1",
368
+ botId: 1,
369
+ appId: "your_app_id",
370
+ });
370
371
 
371
372
  const widget = mount("#chat-root", {
372
373
  sdk,
@@ -874,8 +875,12 @@ import type {
874
875
  WelcomeScreenProps,
875
876
  } from "super-agent-sdk/widget";
876
877
 
877
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
878
- sdk.setToken("demo_app", "demo_token");
878
+ const sdk = createSuperAgent({
879
+ baseUrl: "/api/v1",
880
+ botId: 1,
881
+ appId: "your_app_id",
882
+ });
883
+ sdk.setToken("demo_token");
879
884
 
880
885
  function CustomHeader({ title, onClose, onToggleThreadList }: HeaderProps) {
881
886
  return (
@@ -1192,17 +1197,118 @@ export interface ChatEvent {
1192
1197
 
1193
1198
  ---
1194
1199
 
1195
- ## 12. 高级用法
1200
+ ## 12. Human-in-the-Loop
1201
+
1202
+ Agent 执行中需要人类介入时(确认敏感操作、选择方案、补充信息、审核内容等),SDK 提供统一的中断交互机制。
1203
+
1204
+ ### 12.1 核心流程
1205
+
1206
+ ```
1207
+ Agent 执行中
1208
+ → 后端发送 SSE interrupt 事件
1209
+ → SDK 渲染中断交互卡片(按 interruptType 分发)
1210
+ → 用户完成操作
1211
+ → SDK 调用 respondInterrupt() 回传响应
1212
+ → Agent 基于响应继续执行
1213
+ ```
1214
+
1215
+ ### 12.2 interrupt 事件
1216
+
1217
+ ```json
1218
+ {
1219
+ "type": "interrupt",
1220
+ "interruptId": "int_001",
1221
+ "interruptType": "confirm",
1222
+ "content": "即将执行转账 ¥500,确认执行?",
1223
+ "options": ["确认", "取消"]
1224
+ }
1225
+ ```
1226
+
1227
+ ### 12.3 响应中断
1228
+
1229
+ ```ts
1230
+ await sdk.respondInterrupt(interruptId, {
1231
+ action: "confirm",
1232
+ });
1233
+ ```
1234
+
1235
+ ### 12.4 Widget 自动处理
1236
+
1237
+ Widget 内置中断卡片渲染,收到 `interrupt` 事件后自动展示对应交互卡片,用户操作后自动回传,无需业务方处理:
1238
+
1239
+ ```ts
1240
+ mount("#chat", { sdk }).open();
1241
+ // 遇到 interrupt 时:自动渲染卡片 → 用户点击 → 自动 respondInterrupt → Agent 继续
1242
+ ```
1243
+
1244
+ 也可通过插槽自定义中断卡片:
1245
+
1246
+ ```ts
1247
+ mount("#chat", {
1248
+ sdk,
1249
+ slots: {
1250
+ InterruptCard: MyInterruptCard, // 自定义全部类型
1251
+ },
1252
+ });
1253
+ ```
1254
+
1255
+ ### 12.5 纯 API 模式手动处理
1256
+
1257
+ ```ts
1258
+ sdk.chat({
1259
+ sessionId,
1260
+ message: "帮我转账 ¥500",
1261
+ onMessage: (e) => {
1262
+ if (e.type === "interrupt" && e.interrupt) {
1263
+ // 自定义 UI 处理
1264
+ const ok = window.confirm(e.interrupt.content);
1265
+ sdk.respondInterrupt(e.interrupt.interruptId, {
1266
+ action: ok ? "confirm" : "cancel",
1267
+ });
1268
+ }
1269
+ },
1270
+ });
1271
+ ```
1272
+
1273
+ ### 12.6 interruptType 一览
1274
+
1275
+ | interruptType | 交互 | action | value 示例 |
1276
+ |---------------|------|--------|-----------|
1277
+ | `confirm` | 二次确认 | `confirm` / `cancel` | — |
1278
+ | `select` | 单选 | `select` | `"flight_a"` |
1279
+ | `multiSelect` | 多选 | `submit` | `["name", "phone"]` |
1280
+ | `input` | 文本输入 | `submit` | `"123456"` |
1281
+ | `form` | 表单 | `submit` | `{ name: "张三" }` |
1282
+ | `review` | 内容审阅 | `approve` / `reject` | 修改后内容 |
1283
+ | `approve` | 审批流 | `approve` / `reject` | 驳回原因 |
1284
+ | `upload` | 文件上传 | `submit` | `{ fileId, fileName }` |
1285
+ | `image` | 图片选择 | `select` / `reject` | 图片索引 |
1286
+ | `auth` | 授权请求 | `authorized` / `skip` | — |
1287
+ | `captcha` | 人机验证 | `verified` | `"captcha_token"` |
1288
+ | `decision` | 流程分支 | `decide` | `"retry"` 等 |
1289
+ | `rating` | 评分 | `rate` | `4` |
1290
+ | `date` | 日期选择 | `submit` | `"2026-08-21T14:00:00"` |
1291
+ | `location` | 位置选择 | `submit` | `{ address, lat, lng }` |
1292
+
1293
+ > 每个 `interruptType` 的完整字段定义见 [API 文档](./API.md) 与 [HITL 设计文档](../docs/superpowers/specs/2026-08-18-human-in-the-loop-design.md)。
1294
+
1295
+ ---
1296
+
1297
+ ## 13. 高级用法
1196
1298
 
1197
- ### 12.1 纯 API 模式(不用 Widget)
1299
+ ### 13.1 纯 API 模式(不用 Widget)
1198
1300
 
1199
1301
  不挂载组件,仅使用 API 层构建自己的 UI:
1200
1302
 
1201
1303
  ```ts
1202
1304
  import { createSuperAgent } from "super-agent-sdk";
1203
1305
 
1204
- const sdk = createSuperAgent({ baseUrl: "/api/v1", botId: 1 });
1205
- sdk.setToken("<APP_ID>", "<TOKEN>");
1306
+ const sdk = createSuperAgent({
1307
+ baseUrl: "/api/v1",
1308
+ botId: 1,
1309
+ appId: "your_app_id",
1310
+ });
1311
+ sdk.setToken("<TOKEN>");
1206
1312
 
1207
1313
  const sessionId = await sdk.createSession();
1208
1314
 
@@ -1218,7 +1324,7 @@ sdk.chat({
1218
1324
  });
1219
1325
  ```
1220
1326
 
1221
- ### 12.2 使用 Hooks
1327
+ ### 13.2 使用 Hooks
1222
1328
 
1223
1329
  从 `super-agent-sdk/widget` 导出 `useChat`、`useConversations`,可在自己的 React 应用中复用:
1224
1330
 
@@ -1290,11 +1396,11 @@ function MyChat({ sdk }: { sdk: SuperAgentSDK }) {
1290
1396
 
1291
1397
  > `useChat` 在 `sessionId` 为空时会自动调用 `sdk.createSession()` 创建新会话,无需手动处理。
1292
1398
 
1293
- ### 12.3 Token 续期
1399
+ ### 13.3 Token 续期
1294
1400
 
1295
- 请求返回 `401` 时,SDK 会自动重新调用 `POST /token` 获取新凭证并重试(并发请求合并为一次刷新),无需手动处理。
1401
+ 请求返回 `401` 时,SDK 自动重新调用 `POST /api/token` 获取新 token。
1296
1402
 
1297
- ### 12.4 取消请求
1403
+ ### 13.4 取消请求
1298
1404
 
1299
1405
  `chat()` 返回 `AbortController`;或传入外部 `AbortSignal`(SDK 会合并内部信号,任一触发即中断):
1300
1406
 
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 u(n){if(Array.isArray(n))return n.map(e=>u(e));if(n&&typeof n=="object"&&n.constructor===Object){const e={};for(const[t,r]of Object.entries(n)){const o=t.replace(/_([a-z])/g,(s,a)=>a.toUpperCase());e[o]=u(r)}return e}return n}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}getAppKey(){return this.appId}setToken(e){this.token=e}getOrigin(){try{return new URL(this.baseUrl).origin}catch{return""}}async fetchToken(){const e=`${this.getOrigin()}/api/token`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.appId,botId:this.botId})});if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);const r=await t.json();if(r.code!=="200")throw new Error(r.message||`API error: ${r.code}`);const o=u(r.data);return this.token=o.token,o.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,r){if(!this.token&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");let o=`${this.baseUrl}${t}`;if(r!=null&&r.params){const i=new URLSearchParams;for(const[l,d]of Object.entries(r.params))d!=null&&i.set(l,String(d));const c=i.toString();c&&(o+=`?${c}`)}const s=await fetch(o,{method:e,headers:this.headers(),body:r!=null&&r.body?JSON.stringify(r.body):void 0});if(s.status===401&&!(r!=null&&r.retry)&&await this.handleTokenRefresh())return this.request(e,t,{...r,retry:!0});if(!s.ok)throw new Error(`HTTP ${s.status}: ${s.statusText}`);const a=await s.json();if(a.code!=="200")throw new Error(a.message||`API error: ${a.code}`);return u(a.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,r){if(!this.token&&!await this.handleTokenRefresh())throw new Error("SDK token not available: POST /token failed");const o=`${this.baseUrl}${e}`,s=await fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:r});return s.status===401&&await this.handleTokenRefresh()?fetch(o,{method:"POST",headers:this.headers(),body:JSON.stringify(t),signal:r}):s}}function f(n){const e=n.split(`
2
+ `).map(r=>r.trim()).filter(r=>r.startsWith("data:")).map(r=>r.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};case"done":return{type:"done",content:t.content??"",sessionId:t.sessionId??t.session_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 y(n,e,t){if(!n.body){t(new Error("Response body is null"));return}const r=n.body.getReader(),o=new TextDecoder;let s="";try{for(;;){const{value:a,done:i}=await r.read();if(i)break;s+=o.decode(a,{stream:!0});let c;for(;(c=s.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=s.slice(0,c);s=s.slice(c+2);const d=f(l);d&&e(d)}}if(s.trim()){const a=f(s);a&&e(a)}}catch(a){(a==null?void 0:a.name)!=="AbortError"&&t(a instanceof Error?a:new Error(String(a)))}finally{try{r.releaseLock()}catch{}}}function m(n,e){const t=new AbortController,r=e.signal?I(e.signal,t.signal):t.signal,o={botId:n.getBotId(),message:e.message,sessionId:e.sessionId,stream:e.stream!==!1};return o.stream?n.streamPost("/chat",o,r).then(s=>{var a;if(!s.ok){(a=e.onError)==null||a.call(e,new Error(`HTTP ${s.status}`));return}return y(s,i=>{var c,l,d,h;i.type==="done"&&i.sessionId?(c=e.onDone)==null||c.call(e,{sessionId:i.sessionId,content:i.content}):i.type==="done"?(l=e.onDone)==null||l.call(e,{sessionId:"",content:i.content}):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(s=>{var a;(s==null?void 0:s.name)!=="AbortError"&&((a=e.onError)==null||a.call(e,s instanceof Error?s:new Error(String(s))))}):n.post("/chat",o).then(s=>{var a;(a=e.onDone)==null||a.call(e,{sessionId:s.sessionId,content:s.content??""})}).catch(s=>{var a;(a=e.onError)==null||a.call(e,s instanceof Error?s:new Error(String(s)))}),t}function I(n,e){const t=new AbortController,r=()=>t.abort();return n.addEventListener("abort",r,{once:!0}),e.addEventListener("abort",r,{once:!0}),(n.aborted||e.aborted)&&t.abort(),t.signal}async function w(n){return(await n.post("/chat/sessions",{botId:n.getBotId()})).sessionId}function T(n){return{sessionId:n.threadId??n.sessionId,botId:n.botId,title:n.title,createTime:n.createTime,updateTime:n.updateTime}}async function b(n,e){const t=await n.get("/chat/conversations",{botId:n.getBotId(),page:(e==null?void 0:e.page)??1,size:(e==null?void 0:e.size)??20});return{...t,items:t.items.map(T)}}async function k(n,e,t){await n.patch(`/chat/conversations/${e}`,{title:t})}async function S(n,e){await n.del(`/chat/conversations/${e}`)}async function p(n,e){const t=await n.get(`/chat/conversations/${e}/messages`);return E(t)}function E(n){const e=[],t=[...n].sort((r,o)=>(r.seq??0)-(o.seq??0));for(const r of t){const o=r.role;if(o==="system")continue;const s=r.messageId??`msg_${r.id}`,a=new Date(r.createTime??"").getTime()||Date.now();if(o==="human")e.push({id:s,role:"user",parts:[{type:"text",content:r.content??""}],timestamp:a});else if(o==="ai"){const i=[];r.reasoning&&i.push({type:"thinking",content:r.reasoning}),r.content&&i.push({type:"text",content:r.content});const c=r.toolCalls;if(c&&Array.isArray(c))for(const l of c){let d;try{d=typeof l.args=="string"?l.args:JSON.stringify(l.args??{})}catch{d=String(l.args??"{}")}i.push({type:"tool_call",toolName:l.name??"",toolCallId:l.id??"",args:d})}e.push({id:s,role:"assistant",parts:i,timestamp:a})}else o==="tool"&&e.push({id:s,role:"assistant",parts:[{type:"tool_result",toolName:r.toolName??"",toolCallId:r.toolCallId??"",content:r.content??""}],timestamp:a})}return e}async function C(n,e,t,r){await n.post(`/chat/interrupt/${e}/respond`,{sessionId:t,action:r.action,value:r.value})}function O(n){const e=new g(n),t={getToken:()=>e.fetchToken(),createSession:()=>w(e),chat:r=>m(e,r),listConversations:r=>b(e,r),renameConversation:(r,o)=>k(e,r,o),deleteConversation:r=>S(e,r),getMessages:r=>p(e,r),respondInterrupt:(r,o,s)=>C(e,r,o,s),setToken:r=>e.setToken(r),feedback:(r,o)=>{var s;(s=t.onFeedback)==null||s.call(t,r,o)}};return t}exports.createSuperAgent=O;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export declare interface ChatEvent {
2
- type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "error";
2
+ type: "user" | "thinking" | "ai" | "tool_call" | "tool_result" | "done" | "error" | "interrupt";
3
3
  content: string;
4
4
  toolName?: string;
5
5
  toolCallId?: string;
6
6
  args?: string;
7
7
  sessionId?: string;
8
+ interrupt?: InterruptEvent;
8
9
  }
9
10
 
10
11
  export declare interface ChatOptions {
@@ -30,6 +31,44 @@ export declare interface Conversation {
30
31
 
31
32
  export declare function createSuperAgent(config: SDKConfig): SuperAgentSDK;
32
33
 
34
+ export declare interface FormField {
35
+ name: string;
36
+ label: string;
37
+ type: 'text' | 'number' | 'password' | 'textarea' | 'select' | 'date';
38
+ required?: boolean;
39
+ placeholder?: string;
40
+ defaultValue?: any;
41
+ options?: string[];
42
+ }
43
+
44
+ export declare interface InterruptEvent {
45
+ interruptId: string;
46
+ interruptType: InterruptType;
47
+ content: string;
48
+ options?: (string | InterruptOption)[];
49
+ placeholder?: string;
50
+ inputType?: 'text' | 'number' | 'password' | 'textarea';
51
+ maxLength?: number;
52
+ required?: boolean;
53
+ fields?: FormField[];
54
+ metadata?: Record<string, any>;
55
+ }
56
+
57
+ export declare interface InterruptOption {
58
+ value: string;
59
+ label: string;
60
+ description?: string;
61
+ icon?: string;
62
+ danger?: boolean;
63
+ }
64
+
65
+ export declare interface InterruptResponse {
66
+ action: string;
67
+ value?: any;
68
+ }
69
+
70
+ export declare type InterruptType = 'confirm' | 'select' | 'multiSelect' | 'input' | 'form';
71
+
33
72
  export declare interface ListConversationsParams {
34
73
  page?: number;
35
74
  size?: number;
@@ -59,21 +98,23 @@ export declare type MessagePart = {
59
98
  } | {
60
99
  type: "error";
61
100
  content: string;
101
+ } | {
102
+ type: "interrupt";
103
+ interrupt: InterruptEvent;
104
+ resolved?: boolean;
105
+ response?: InterruptResponse;
62
106
  };
63
107
 
64
108
  export declare interface SDKConfig {
65
109
  baseUrl: string;
66
110
  botId: number;
67
- appId?: string;
111
+ appId: string;
68
112
  token?: string;
69
113
  }
70
114
 
71
115
  export declare interface SuperAgentSDK {
72
- /** 获取 token(调用 POST /token),自动设置内部凭证 */
73
- getToken(): Promise<{
74
- appId: string;
75
- token: string;
76
- }>;
116
+ /** 获取 token(调用 POST /api/token),自动设置内部凭证 */
117
+ getToken(): Promise<string>;
77
118
  /** 创建新会话,返回 sessionId */
78
119
  createSession(): Promise<string>;
79
120
  chat(options: ChatOptions): AbortController;
@@ -81,9 +122,11 @@ export declare interface SuperAgentSDK {
81
122
  renameConversation(sessionId: string, title: string): Promise<void>;
82
123
  deleteConversation(sessionId: string): Promise<void>;
83
124
  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;
125
+ /** 响应中断事件 */
126
+ respondInterrupt(interruptId: string, sessionId: string, response: InterruptResponse): Promise<void>;
127
+ setToken(token: string): void;
128
+ feedback(messageId: string, type: "like" | "dislike"): void;
129
+ onFeedback?: (messageId: string, type: "like" | "dislike") => void;
87
130
  }
88
131
 
89
132
  export declare interface UIMessage {