@kweaver-ai/kweaver-sdk 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +25 -2
  2. package/README.zh.md +24 -1
  3. package/dist/api/agent-chat.d.ts +8 -2
  4. package/dist/api/agent-chat.js +150 -44
  5. package/dist/api/agent-list.d.ts +35 -0
  6. package/dist/api/agent-list.js +95 -21
  7. package/dist/api/bkn-backend.d.ts +60 -0
  8. package/dist/api/bkn-backend.js +103 -10
  9. package/dist/api/business-domains.js +9 -5
  10. package/dist/api/context-loader.js +4 -1
  11. package/dist/api/conversations.d.ts +6 -3
  12. package/dist/api/conversations.js +29 -35
  13. package/dist/api/dataflow.js +1 -10
  14. package/dist/api/dataflow2.d.ts +95 -0
  15. package/dist/api/dataflow2.js +80 -0
  16. package/dist/api/datasources.js +1 -10
  17. package/dist/api/dataviews.js +1 -10
  18. package/dist/api/headers.d.ts +11 -0
  19. package/dist/api/headers.js +30 -0
  20. package/dist/api/knowledge-networks.d.ts +41 -0
  21. package/dist/api/knowledge-networks.js +69 -22
  22. package/dist/api/ontology-query.d.ts +14 -1
  23. package/dist/api/ontology-query.js +63 -49
  24. package/dist/api/semantic-search.js +2 -12
  25. package/dist/api/skills.d.ts +141 -0
  26. package/dist/api/skills.js +208 -0
  27. package/dist/api/vega.d.ts +54 -7
  28. package/dist/api/vega.js +112 -25
  29. package/dist/auth/oauth.d.ts +5 -1
  30. package/dist/auth/oauth.js +351 -95
  31. package/dist/cli.js +49 -5
  32. package/dist/client.d.ts +12 -0
  33. package/dist/client.js +52 -8
  34. package/dist/commands/agent.d.ts +33 -1
  35. package/dist/commands/agent.js +721 -49
  36. package/dist/commands/auth.js +226 -55
  37. package/dist/commands/bkn-ops.d.ts +77 -0
  38. package/dist/commands/bkn-ops.js +1056 -0
  39. package/dist/commands/bkn-query.d.ts +14 -0
  40. package/dist/commands/bkn-query.js +370 -0
  41. package/dist/commands/bkn-schema.d.ts +135 -0
  42. package/dist/commands/bkn-schema.js +1483 -0
  43. package/dist/commands/bkn-utils.d.ts +36 -0
  44. package/dist/commands/bkn-utils.js +102 -0
  45. package/dist/commands/bkn.d.ts +7 -113
  46. package/dist/commands/bkn.js +175 -2429
  47. package/dist/commands/call.js +8 -5
  48. package/dist/commands/dataflow.d.ts +1 -0
  49. package/dist/commands/dataflow.js +251 -0
  50. package/dist/commands/dataview.d.ts +7 -0
  51. package/dist/commands/dataview.js +38 -2
  52. package/dist/commands/ds.d.ts +1 -0
  53. package/dist/commands/ds.js +8 -1
  54. package/dist/commands/explore-bkn.d.ts +79 -0
  55. package/dist/commands/explore-bkn.js +273 -0
  56. package/dist/commands/explore-chat.d.ts +3 -0
  57. package/dist/commands/explore-chat.js +193 -0
  58. package/dist/commands/explore-vega.d.ts +3 -0
  59. package/dist/commands/explore-vega.js +71 -0
  60. package/dist/commands/explore.d.ts +9 -0
  61. package/dist/commands/explore.js +258 -0
  62. package/dist/commands/import-csv.d.ts +2 -0
  63. package/dist/commands/import-csv.js +3 -2
  64. package/dist/commands/skill.d.ts +26 -0
  65. package/dist/commands/skill.js +524 -0
  66. package/dist/commands/vega.js +372 -117
  67. package/dist/config/jwt.d.ts +6 -0
  68. package/dist/config/jwt.js +21 -0
  69. package/dist/config/no-auth.d.ts +3 -0
  70. package/dist/config/no-auth.js +5 -0
  71. package/dist/config/store.d.ts +45 -5
  72. package/dist/config/store.js +385 -30
  73. package/dist/index.d.ts +6 -1
  74. package/dist/index.js +5 -1
  75. package/dist/kweaver.d.ts +5 -0
  76. package/dist/kweaver.js +32 -2
  77. package/dist/resources/bkn.d.ts +4 -0
  78. package/dist/resources/bkn.js +6 -3
  79. package/dist/resources/conversations.d.ts +5 -2
  80. package/dist/resources/conversations.js +17 -3
  81. package/dist/resources/knowledge-networks.js +3 -8
  82. package/dist/resources/skills.d.ts +47 -0
  83. package/dist/resources/skills.js +47 -0
  84. package/dist/resources/vega.d.ts +11 -6
  85. package/dist/resources/vega.js +37 -10
  86. package/dist/templates/explorer/app.js +136 -0
  87. package/dist/templates/explorer/bkn.js +747 -0
  88. package/dist/templates/explorer/chat.js +980 -0
  89. package/dist/templates/explorer/dashboard.js +82 -0
  90. package/dist/templates/explorer/index.html +35 -0
  91. package/dist/templates/explorer/style.css +2440 -0
  92. package/dist/templates/explorer/vega.js +291 -0
  93. package/dist/utils/http.d.ts +3 -0
  94. package/dist/utils/http.js +37 -1
  95. package/package.json +9 -5
package/README.md CHANGED
@@ -133,19 +133,24 @@ const health = await client.vega.health();
133
133
  // Context Loader (semantic search over a BKN via MCP)
134
134
  const cl = client.contextLoader(mcpUrl, "bkn-id");
135
135
  const results = await cl.search({ query: "hypertension treatment" });
136
+
137
+ // Skills (registry + market + progressive read)
138
+ const skills = await client.skills.market({ name: "kweaver" });
139
+ const skillMd = await client.skills.fetchContent("skill-id");
136
140
  ```
137
141
 
138
142
  ## CLI Reference
139
143
 
140
144
  ```
141
- kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
145
+ kweaver auth login <url> [--alias name] [--no-auth] [-u user] [-p pass] [--playwright] [--insecure|-k]
142
146
  kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (headless login)
143
147
  kweaver auth export [url|alias] [--json] (export command to run on a headless host)
144
148
  kweaver auth status/list/use/delete/logout
145
149
  kweaver config show / list-bd / set-bd <value> # platform business domain — after login
146
150
  kweaver token
147
151
  kweaver ds list/get/delete/tables/connect
148
- kweaver ds import-csv <ds_id> --files <glob> [--table-prefix <p>] [--batch-size 500]
152
+ kweaver ds import-csv <ds_id> --files <glob> [--table-prefix <p>] [--batch-size 500] [--recreate]
153
+ kweaver dataflow list/run/runs/logs
149
154
  kweaver dataview list/find/get/query/delete
150
155
  kweaver bkn list/get/stats/export/create/update/delete
151
156
  kweaver bkn create-from-ds <ds_id> --name <name> [--tables t1,t2] [--build]
@@ -158,12 +163,29 @@ kweaver bkn subgraph / search
158
163
  kweaver bkn action-execution get
159
164
  kweaver bkn action-log list/get/cancel
160
165
  kweaver agent list/get/create/update/delete/chat/sessions/history/publish/unpublish
166
+ kweaver skill list/market/get/register/status/delete/content/read-file/download/install
161
167
  kweaver vega health/stats/inspect/catalog/resource/connector-type
162
168
  kweaver context-loader config set/use/list/show
163
169
  kweaver context-loader kn-search/query-object-instance/...
164
170
  kweaver call <path> [-X METHOD] [-d BODY] [-H header]
165
171
  ```
166
172
 
173
+ ### Dataflow CLI examples
174
+
175
+ ```bash
176
+ kweaver dataflow list
177
+ kweaver dataflow run <dagId> --file ./demo.pdf
178
+ kweaver dataflow run <dagId> --url https://example.com/demo.pdf --name demo.pdf
179
+ kweaver dataflow runs <dagId>
180
+ kweaver dataflow runs <dagId> --since 2026-04-01
181
+ kweaver dataflow logs <dagId> <instanceId>
182
+ kweaver dataflow logs <dagId> <instanceId> --detail
183
+ ```
184
+
185
+ `kweaver dataflow runs --since` filters one local natural day. If the value cannot be parsed by `new Date(...)`, the CLI falls back to the most recent 20 runs. `kweaver dataflow logs` defaults to summary output; add `--detail` to print indented `input` and `output` payloads.
186
+
187
+ **No-auth platforms:** If OAuth is not enabled, use `kweaver auth <url> --no-auth` (or run a normal `auth login`; a **404** on `POST /oauth2/clients` switches to no-auth automatically). Credentials are still saved under `~/.kweaver/` and work with `auth use` / `auth list`. Optional: `KWEAVER_NO_AUTH=1` with `KWEAVER_BASE_URL` when no token env is set. SDK: `new KWeaverClient({ baseUrl, auth: false })` or `kweaver.configure({ baseUrl, auth: false })`.
188
+
167
189
  ## Environment Variables
168
190
 
169
191
  | Variable | Description |
@@ -171,6 +193,7 @@ kweaver call <path> [-X METHOD] [-d BODY] [-H header]
171
193
  | `KWEAVER_BASE_URL` | KWeaver instance URL |
172
194
  | `KWEAVER_BUSINESS_DOMAIN` | Business domain identifier |
173
195
  | `KWEAVER_TOKEN` | Access token |
196
+ | `KWEAVER_NO_AUTH` | Set to `1`/`true`/`yes` to use no-auth sentinel when `KWEAVER_TOKEN` is unset (with `KWEAVER_BASE_URL` or active platform) |
174
197
  | `KWEAVER_TLS_INSECURE` | Set to `1` or `true` to skip TLS certificate verification for all HTTPS in the process (dev only; prefer `kweaver auth … --insecure` which saves per platform) |
175
198
  | `NODE_TLS_REJECT_UNAUTHORIZED` | Node.js built-in TLS switch: set to `0` to skip certificate verification for HTTPS in this process. The `kweaver` CLI sets this when `KWEAVER_TLS_INSECURE` is set or the saved token has insecure TLS (same scope as above; dev only). |
176
199
 
package/README.zh.md CHANGED
@@ -122,18 +122,23 @@ const queryRows = await client.dataviews.query(viewId, {
122
122
  // Context Loader(通过 MCP 对 BKN 做语义搜索)
123
123
  const cl = client.contextLoader(mcpUrl, "bkn-id");
124
124
  const results = await cl.search({ query: "高血压 治疗" });
125
+
126
+ // Skill(注册表/市场/渐进式读取)
127
+ const skills = await client.skills.market({ name: "kweaver" });
128
+ const skillMd = await client.skills.fetchContent("skill-id");
125
129
  ```
126
130
 
127
131
  ## 命令速查
128
132
 
129
133
  ```
130
- kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
134
+ kweaver auth login <url> [--alias name] [--no-auth] [-u user] [-p pass] [--playwright] [--insecure|-k]
131
135
  kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (无浏览器登录)
132
136
  kweaver auth export [url|alias] [--json] (导出在无浏览器机器上运行的命令)
133
137
  kweaver auth status/list/use/delete/logout
134
138
  kweaver config show / list-bd / set-bd <value> # 平台业务域,登录后优先
135
139
  kweaver token
136
140
  kweaver ds list/get/delete/tables/connect
141
+ kweaver dataflow list/run/runs/logs
137
142
  kweaver dataview list/find/get/query/delete
138
143
  kweaver bkn list/get/stats/export/create/update/delete
139
144
  kweaver bkn object-type list/get/create/update/delete/query/properties
@@ -143,11 +148,28 @@ kweaver bkn subgraph
143
148
  kweaver bkn action-execution get
144
149
  kweaver bkn action-log list/get/cancel
145
150
  kweaver agent list/get/chat/sessions/history
151
+ kweaver skill list/market/get/register/status/delete/content/read-file/download/install
146
152
  kweaver context-loader config set/use/list/show
147
153
  kweaver context-loader kn-search/query-object-instance/...
148
154
  kweaver call <path> [-X METHOD] [-d BODY] [-H header]
149
155
  ```
150
156
 
157
+ ### Dataflow CLI 示例
158
+
159
+ ```bash
160
+ kweaver dataflow list
161
+ kweaver dataflow run <dagId> --file ./demo.pdf
162
+ kweaver dataflow run <dagId> --url https://example.com/demo.pdf --name demo.pdf
163
+ kweaver dataflow runs <dagId>
164
+ kweaver dataflow runs <dagId> --since 2026-04-01
165
+ kweaver dataflow logs <dagId> <instanceId>
166
+ kweaver dataflow logs <dagId> <instanceId> --detail
167
+ ```
168
+
169
+ `kweaver dataflow runs --since` 会按本地自然日过滤;如果参数无法被 `new Date(...)` 解析,CLI 会回退到最近 20 条运行记录。`kweaver dataflow logs` 默认输出摘要;加上 `--detail` 会打印带缩进的 `input` 和 `output` 载荷。
170
+
171
+ **无 OAuth 的平台:** 使用 `kweaver auth <url> --no-auth`,或照常 `auth login`;若 `POST /oauth2/clients` 返回 **404**,CLI 会提示并自动保存为 no-auth。凭据仍在 `~/.kweaver/`,可用 `auth use` / `auth list` 切换。可选环境变量 `KWEAVER_NO_AUTH=1`(未设置 `KWEAVER_TOKEN` 时)配合 `KWEAVER_BASE_URL`。SDK:`new KWeaverClient({ baseUrl, auth: false })` 或 `kweaver.configure({ baseUrl, auth: false })`。
172
+
151
173
  ## 环境变量
152
174
 
153
175
  | 变量 | 说明 |
@@ -155,6 +177,7 @@ kweaver call <path> [-X METHOD] [-d BODY] [-H header]
155
177
  | `KWEAVER_BASE_URL` | KWeaver 实例地址 |
156
178
  | `KWEAVER_BUSINESS_DOMAIN` | 业务域标识 |
157
179
  | `KWEAVER_TOKEN` | 访问令牌 |
180
+ | `KWEAVER_NO_AUTH` | 设为 `1`/`true`/`yes` 且未设置 `KWEAVER_TOKEN` 时使用 no-auth 占位(需 `KWEAVER_BASE_URL` 或已选平台) |
158
181
  | `KWEAVER_TLS_INSECURE` | 设为 `1` 或 `true` 时跳过 TLS 证书校验(仅开发;更推荐 `kweaver auth … --insecure` 以按平台持久化) |
159
182
  | `NODE_TLS_REJECT_UNAUTHORIZED` | Node.js 内置 TLS 开关:设为 `0` 时在本进程内跳过 HTTPS 证书校验。`kweaver` 在 `KWEAVER_TLS_INSECURE` 生效或已保存 token 为不安全 TLS 时会设置此项(范围同上;仅开发)。 |
160
183
 
@@ -41,7 +41,7 @@ export interface AgentInfo {
41
41
  key: string;
42
42
  version: string;
43
43
  }
44
- export declare function buildChatUrl(baseUrl: string, agentId: string): string;
44
+ export declare function buildChatUrl(baseUrl: string, agentKey: string): string;
45
45
  export declare function buildAgentInfoUrl(baseUrl: string, agentId: string, version: string): string;
46
46
  export declare function fetchAgentInfo(options: {
47
47
  baseUrl: string;
@@ -58,9 +58,15 @@ export declare function processIncrementalUpdate(data: {
58
58
  }, result: Record<string, unknown>): void;
59
59
  export declare function sendChatRequest(options: SendChatRequestOptions): Promise<ChatResult>;
60
60
  export interface SendChatRequestStreamCallbacks {
61
- onTextDelta: (fullText: string) => void;
61
+ onTextDelta: (fullText: string, currentSegmentText: string) => void;
62
62
  /** Optional: called when message.content.middle_answer.progress updates (tool/skill steps). */
63
63
  onProgress?: (progress: ProgressItem[]) => void;
64
+ /** Optional: called when a text segment is completed and a new phase starts. */
65
+ onSegmentComplete?: (segmentText: string, segmentIndex: number) => void;
66
+ /** Optional: called when answer_type_other changes (step metadata like status, tool info). */
67
+ onStepMeta?: (meta: Record<string, unknown>) => void;
68
+ /** Optional: called as soon as conversationId is discovered in the stream. */
69
+ onConversationId?: (conversationId: string) => void;
64
70
  }
65
71
  /**
66
72
  * Stream-only entry point for TUI: same as sendChatRequest with stream=true,
@@ -1,10 +1,11 @@
1
- import { fetchTextOrThrow, HttpError } from "../utils/http.js";
1
+ import { isNoAuth } from "../config/no-auth.js";
2
+ import { fetchTextOrThrow, fetchWithRetry, HttpError } from "../utils/http.js";
2
3
  import { normalizeDisplayText } from "../utils/display-text.js";
3
4
  const CHAT_PATH = "/api/agent-factory/v1/app";
4
5
  const AGENT_INFO_PATH = "/api/agent-factory/v3/agent-market/agent";
5
- export function buildChatUrl(baseUrl, agentId) {
6
+ export function buildChatUrl(baseUrl, agentKey) {
6
7
  const base = baseUrl.replace(/\/+$/, "");
7
- return `${base}${CHAT_PATH}/${agentId}/chat/completion`;
8
+ return `${base}${CHAT_PATH}/${agentKey}/chat/completion`;
8
9
  }
9
10
  export function buildAgentInfoUrl(baseUrl, agentId, version) {
10
11
  const base = baseUrl.replace(/\/+$/, "");
@@ -13,16 +14,19 @@ export function buildAgentInfoUrl(baseUrl, agentId, version) {
13
14
  export async function fetchAgentInfo(options) {
14
15
  const { baseUrl, accessToken, agentId, version, businessDomain = "bd_public" } = options;
15
16
  const url = buildAgentInfoUrl(baseUrl, agentId, version);
17
+ const agentHeaders = {
18
+ accept: "application/json, text/plain, */*",
19
+ "x-business-domain": businessDomain,
20
+ "x-language": "zh-CN",
21
+ "x-requested-with": "XMLHttpRequest",
22
+ };
23
+ if (!isNoAuth(accessToken)) {
24
+ agentHeaders.Authorization = `Bearer ${accessToken}`;
25
+ agentHeaders.token = accessToken;
26
+ }
16
27
  const { body } = await fetchTextOrThrow(url, {
17
28
  method: "GET",
18
- headers: {
19
- accept: "application/json, text/plain, */*",
20
- Authorization: `Bearer ${accessToken}`,
21
- token: accessToken,
22
- "x-business-domain": businessDomain,
23
- "x-language": "zh-CN",
24
- "x-requested-with": "XMLHttpRequest",
25
- },
29
+ headers: agentHeaders,
26
30
  });
27
31
  const data = JSON.parse(body);
28
32
  if (!data.id || !data.key) {
@@ -67,6 +71,19 @@ function stringFromAnswerObject(obj) {
67
71
  const parts = [d, c ? `code: ${c}` : "", s ? `solution: ${s}` : ""].filter(Boolean);
68
72
  return parts.join("\n");
69
73
  }
74
+ /** Format answer_type_other which may be an array of strings or an object. */
75
+ function stringFromAnswerTypeOther(other) {
76
+ if (Array.isArray(other)) {
77
+ const strings = other.filter((s) => typeof s === "string" && s);
78
+ if (strings.length > 0)
79
+ return JSON.stringify(strings);
80
+ return "";
81
+ }
82
+ if (other && typeof other === "object") {
83
+ return stringFromAnswerObject(other);
84
+ }
85
+ return "";
86
+ }
70
87
  export function extractText(data) {
71
88
  if (!data || typeof data !== "object")
72
89
  return "";
@@ -74,44 +91,44 @@ export function extractText(data) {
74
91
  const fa = obj.final_answer;
75
92
  if (fa?.answer && typeof fa.answer === "object") {
76
93
  const ans = fa.answer;
77
- if (typeof ans.text === "string")
94
+ if (typeof ans.text === "string" && ans.text)
78
95
  return ans.text;
79
96
  }
80
- if (typeof fa?.text === "string") {
97
+ if (typeof fa?.text === "string" && fa.text) {
81
98
  return fa.text;
82
99
  }
100
+ // Check answer_type_other at final_answer level (for content_type "other")
101
+ if (fa?.answer_type_other) {
102
+ const desc = stringFromAnswerTypeOther(fa.answer_type_other);
103
+ if (desc)
104
+ return desc;
105
+ }
83
106
  const msg = obj.message;
84
- if (typeof msg?.text === "string") {
107
+ if (typeof msg?.text === "string" && msg.text) {
85
108
  return msg.text;
86
109
  }
87
110
  if (msg?.content && typeof msg.content === "object") {
88
111
  const content = msg.content;
89
- if (typeof content.text === "string")
112
+ if (typeof content.text === "string" && content.text)
90
113
  return content.text;
91
114
  const contentFinalAnswer = content.final_answer;
92
115
  if (contentFinalAnswer?.answer && typeof contentFinalAnswer.answer === "object") {
93
116
  const answer = contentFinalAnswer.answer;
94
- if (typeof answer.text === "string")
117
+ if (typeof answer.text === "string" && answer.text)
95
118
  return answer.text;
96
119
  }
97
- if (typeof contentFinalAnswer?.text === "string") {
120
+ if (typeof contentFinalAnswer?.text === "string" && contentFinalAnswer.text) {
98
121
  return contentFinalAnswer.text;
99
122
  }
100
- const other = contentFinalAnswer?.answer_type_other;
101
- if (other && typeof other === "object") {
102
- const desc = stringFromAnswerObject(other);
123
+ // Check answer_type_other at content.final_answer level
124
+ if (contentFinalAnswer?.answer_type_other) {
125
+ const desc = stringFromAnswerTypeOther(contentFinalAnswer.answer_type_other);
103
126
  if (desc)
104
127
  return desc;
105
128
  }
106
129
  }
107
- const topOther = fa?.answer_type_other;
108
- if (topOther && typeof topOther === "object") {
109
- const desc = stringFromAnswerObject(topOther);
110
- if (desc)
111
- return desc;
112
- }
113
130
  const answer = obj.answer;
114
- if (typeof answer?.text === "string") {
131
+ if (typeof answer?.text === "string" && answer.text) {
115
132
  return answer.text;
116
133
  }
117
134
  return "";
@@ -194,7 +211,7 @@ function getProgressFromResult(result) {
194
211
  };
195
212
  });
196
213
  }
197
- function applySseDataLine(line, state, verbose, onTextDelta, onProgress) {
214
+ function applySseDataLine(line, state, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId) {
198
215
  if (!line.startsWith("data: ")) {
199
216
  return;
200
217
  }
@@ -208,21 +225,45 @@ function applySseDataLine(line, state, verbose, onTextDelta, onProgress) {
208
225
  if (data.key?.length === 1 && data.key[0] === "conversation_id" && data.action === "upsert") {
209
226
  state.conversationId =
210
227
  typeof data.content === "string" ? data.content : String(data.content ?? "");
228
+ if (state.conversationId && onConversationId) {
229
+ onConversationId(state.conversationId);
230
+ }
231
+ }
232
+ // Detect answer_type_other changes (step metadata: status, end_time, etc.)
233
+ if (data.key && data.key.join(".").includes("answer_type_other") && data.action === "upsert") {
234
+ const ato = getByPath(state.result, ["message", "content", "final_answer", "answer_type_other"]);
235
+ console.error(`[STEP_META] ${JSON.stringify(ato).slice(0, 500)}`);
236
+ if (ato && typeof ato === "object" && onStepMeta) {
237
+ onStepMeta(ato);
238
+ }
211
239
  }
212
240
  const progress = getProgressFromResult(state.result);
213
241
  if (progress.length > 0 && onProgress) {
214
242
  onProgress(progress);
215
243
  }
216
- const text = normalizeDisplayText(extractText(state.result));
217
- if (text && text !== state.lastText) {
244
+ const rawText = normalizeDisplayText(extractText(state.result));
245
+ // Detect when the upstream clears text between steps: previous had content, now empty or
246
+ // significantly shorter (new segment starting). Save the completed segment.
247
+ if (state.prevRawText && (!rawText || rawText.length < state.prevRawText.length * 0.5)) {
248
+ state.completedSegments.push(state.prevRawText);
249
+ if (onSegmentComplete) {
250
+ onSegmentComplete(state.prevRawText, state.completedSegments.length - 1);
251
+ }
252
+ }
253
+ state.prevRawText = rawText;
254
+ // Build full text: completed segments + current segment
255
+ const fullText = state.completedSegments.length > 0
256
+ ? state.completedSegments.join("\n\n") + (rawText ? "\n\n" + rawText : "")
257
+ : rawText;
258
+ if (fullText && fullText !== state.lastText) {
218
259
  if (onTextDelta) {
219
- onTextDelta(text);
260
+ onTextDelta(fullText, rawText);
220
261
  }
221
262
  else {
222
- const delta = text.slice(state.lastText.length);
263
+ const delta = fullText.slice(state.lastText.length);
223
264
  process.stdout.write(delta);
224
265
  }
225
- state.lastText = text;
266
+ state.lastText = fullText;
226
267
  }
227
268
  }
228
269
  catch {
@@ -233,7 +274,7 @@ function applySseDataLine(line, state, verbose, onTextDelta, onProgress) {
233
274
  }
234
275
  export async function sendChatRequest(options) {
235
276
  const { baseUrl, accessToken, agentId, agentKey, agentVersion, query, conversationId, stream, verbose, businessDomain = "bd_public", } = options;
236
- const url = buildChatUrl(baseUrl, agentId);
277
+ const url = buildChatUrl(baseUrl, agentKey);
237
278
  const body = {
238
279
  agent_id: agentId,
239
280
  agent_key: agentKey,
@@ -247,11 +288,13 @@ export async function sendChatRequest(options) {
247
288
  const headers = {
248
289
  "Content-Type": "application/json",
249
290
  accept: stream ? "text/event-stream" : "application/json",
250
- Authorization: `Bearer ${accessToken}`,
251
291
  "Accept-Language": "zh-CN",
252
292
  "x-Language": "zh-CN",
253
293
  "x-business-domain": businessDomain,
254
294
  };
295
+ if (!isNoAuth(accessToken)) {
296
+ headers.Authorization = `Bearer ${accessToken}`;
297
+ }
255
298
  if (verbose) {
256
299
  console.error(`POST ${url}`);
257
300
  const safeHeaders = Object.fromEntries(Object.entries(headers).map(([k, v]) => k.toLowerCase() === "authorization" ? [k, "Bearer ***"] : [k, v]));
@@ -260,7 +303,7 @@ export async function sendChatRequest(options) {
260
303
  }
261
304
  let response;
262
305
  try {
263
- response = await fetch(url, {
306
+ response = await fetchWithRetry(url, {
264
307
  method: "POST",
265
308
  headers,
266
309
  body: JSON.stringify(body),
@@ -292,7 +335,7 @@ export async function sendChatRequest(options) {
292
335
  export async function sendChatRequestStream(options, callbacks) {
293
336
  const opts = { ...options, stream: true };
294
337
  const { baseUrl, accessToken, agentId, agentKey, agentVersion, query, conversationId, verbose, businessDomain = "bd_public", } = opts;
295
- const url = buildChatUrl(baseUrl, agentId);
338
+ const url = buildChatUrl(baseUrl, agentKey);
296
339
  const body = {
297
340
  agent_id: agentId,
298
341
  agent_key: agentKey,
@@ -306,11 +349,13 @@ export async function sendChatRequestStream(options, callbacks) {
306
349
  const headers = {
307
350
  "Content-Type": "application/json",
308
351
  accept: "text/event-stream",
309
- Authorization: `Bearer ${accessToken}`,
310
352
  "Accept-Language": "zh-CN",
311
353
  "x-Language": "zh-CN",
312
354
  "x-business-domain": businessDomain,
313
355
  };
356
+ if (!isNoAuth(accessToken)) {
357
+ headers.Authorization = `Bearer ${accessToken}`;
358
+ }
314
359
  if (verbose) {
315
360
  console.error(`POST ${url}`);
316
361
  const safeHeaders = Object.fromEntries(Object.entries(headers).map(([k, v]) => k.toLowerCase() === "authorization" ? [k, "Bearer ***"] : [k, v]));
@@ -319,7 +364,7 @@ export async function sendChatRequestStream(options, callbacks) {
319
364
  }
320
365
  let response;
321
366
  try {
322
- response = await fetch(url, {
367
+ response = await fetchWithRetry(url, {
323
368
  method: "POST",
324
369
  headers,
325
370
  body: JSON.stringify(body),
@@ -335,29 +380,75 @@ export async function sendChatRequestStream(options, callbacks) {
335
380
  throw new HttpError(response.status, response.statusText, text);
336
381
  }
337
382
  if (contentType.includes("text/event-stream")) {
338
- return handleStreamResponse(response, verbose, callbacks.onTextDelta, callbacks.onProgress);
383
+ return handleStreamResponse(response, verbose, callbacks.onTextDelta, callbacks.onProgress, callbacks.onSegmentComplete, callbacks.onStepMeta, callbacks.onConversationId);
339
384
  }
340
385
  const text = await response.text();
341
386
  const json = parseJsonResponse(text);
342
387
  const resultText = normalizeDisplayText(extractText(json));
343
388
  const convId = json.conversation_id;
344
- callbacks.onTextDelta(resultText);
389
+ callbacks.onTextDelta(resultText, resultText);
345
390
  return { text: resultText, conversationId: convId, progress: getProgressFromResult(json) };
346
391
  }
347
- async function handleStreamResponse(response, verbose, onTextDelta, onProgress) {
392
+ async function handleStreamResponse(response, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId) {
348
393
  const reader = response.body?.getReader();
349
394
  if (!reader) {
350
395
  throw new Error("No response body for stream");
351
396
  }
352
397
  const decoder = new TextDecoder();
353
398
  let buffer = "";
399
+ let pendingEventType = "";
354
400
  const state = {
355
401
  result: {},
356
402
  conversationId: undefined,
357
403
  lastText: "",
404
+ completedSegments: [],
405
+ prevRawText: "",
358
406
  };
359
407
  const applyLine = (line) => {
360
- applySseDataLine(line, state, verbose, onTextDelta, onProgress);
408
+ // Track SSE event type (e.g., "event:error")
409
+ if (line.startsWith("event:")) {
410
+ pendingEventType = line.slice(6).trim();
411
+ return;
412
+ }
413
+ // If we have an error event type, handle the data line as an error
414
+ if (pendingEventType === "error" && line.startsWith("data: ")) {
415
+ pendingEventType = "";
416
+ const errStr = line.slice(6).trim();
417
+ // Emit as error text rather than processing as incremental update
418
+ if (onTextDelta) {
419
+ let errMsg = errStr;
420
+ let errDetail = "";
421
+ try {
422
+ const errObj = JSON.parse(errStr);
423
+ errMsg = errObj.description || errObj.details || errStr;
424
+ if (errObj.solution && errObj.solution !== "无")
425
+ errMsg += "\n💡 " + errObj.solution;
426
+ // Collect all remaining fields as detail context
427
+ const detailFields = {};
428
+ for (const [k, v] of Object.entries(errObj)) {
429
+ if (k !== "description" && k !== "solution" && v != null && v !== "") {
430
+ detailFields[k] = v;
431
+ }
432
+ }
433
+ if (Object.keys(detailFields).length > 0) {
434
+ errDetail = "\n\n<details><summary>详细错误信息</summary>\n\n```json\n" + JSON.stringify(detailFields, null, 2) + "\n```\n</details>";
435
+ }
436
+ }
437
+ catch {
438
+ if (verbose)
439
+ console.error("Failed to parse SSE error JSON:", errStr);
440
+ }
441
+ const errText = "⚠️ " + errMsg + errDetail;
442
+ const fullText = state.completedSegments.length > 0
443
+ ? state.completedSegments.join("\n\n") + "\n\n" + errText
444
+ : errText;
445
+ onTextDelta(fullText, errText);
446
+ state.lastText = fullText;
447
+ }
448
+ return;
449
+ }
450
+ pendingEventType = "";
451
+ applySseDataLine(line, state, verbose, onTextDelta, onProgress, onSegmentComplete, onStepMeta, onConversationId);
361
452
  };
362
453
  while (true) {
363
454
  const { done, value } = await reader.read();
@@ -376,6 +467,21 @@ async function handleStreamResponse(response, verbose, onTextDelta, onProgress)
376
467
  if (!onTextDelta && state.lastText && !state.lastText.endsWith("\n")) {
377
468
  process.stdout.write("\n");
378
469
  }
379
- const finalText = normalizeDisplayText(extractText(state.result) || state.lastText);
470
+ // Fallback: try to extract conversationId from accumulated result if not found in stream
471
+ if (!state.conversationId) {
472
+ const r = state.result;
473
+ const candidate = r.conversation_id ??
474
+ r.message?.conversation_id ??
475
+ r.conversationId;
476
+ if (typeof candidate === "string" && candidate) {
477
+ state.conversationId = candidate;
478
+ if (onConversationId)
479
+ onConversationId(candidate);
480
+ }
481
+ }
482
+ const rawFinal = normalizeDisplayText(extractText(state.result));
483
+ const finalText = state.completedSegments.length > 0
484
+ ? state.completedSegments.join("\n\n") + (rawFinal ? "\n\n" + rawFinal : "")
485
+ : rawFinal || state.lastText;
380
486
  return { text: finalText, conversationId: state.conversationId };
381
487
  }
@@ -51,6 +51,7 @@ export interface PublishAgentOptions {
51
51
  accessToken: string;
52
52
  agentId: string;
53
53
  body?: string;
54
+ categoryId?: string;
54
55
  businessDomain?: string;
55
56
  }
56
57
  export declare function publishAgent(options: PublishAgentOptions): Promise<string>;
@@ -61,3 +62,37 @@ export interface UnpublishAgentOptions {
61
62
  businessDomain?: string;
62
63
  }
63
64
  export declare function unpublishAgent(options: UnpublishAgentOptions): Promise<void>;
65
+ export interface ListPersonalAgentsOptions {
66
+ baseUrl: string;
67
+ accessToken: string;
68
+ businessDomain?: string;
69
+ name?: string;
70
+ pagination_marker_str?: string;
71
+ publish_status?: string;
72
+ publish_to_be?: string;
73
+ size?: number;
74
+ }
75
+ export declare function listPersonalAgents(options: ListPersonalAgentsOptions): Promise<string>;
76
+ export interface ListPublishedAgentTemplatesOptions {
77
+ baseUrl: string;
78
+ accessToken: string;
79
+ businessDomain?: string;
80
+ category_id?: string;
81
+ name?: string;
82
+ pagination_marker_str?: string;
83
+ size?: number;
84
+ }
85
+ export declare function listPublishedAgentTemplates(options: ListPublishedAgentTemplatesOptions): Promise<string>;
86
+ export interface GetPublishedAgentTemplateOptions {
87
+ baseUrl: string;
88
+ accessToken: string;
89
+ templateId: string;
90
+ businessDomain?: string;
91
+ }
92
+ export declare function getPublishedAgentTemplate(options: GetPublishedAgentTemplateOptions): Promise<string>;
93
+ export interface ListAgentCategoriesOptions {
94
+ baseUrl: string;
95
+ accessToken: string;
96
+ businessDomain?: string;
97
+ }
98
+ export declare function listAgentCategories(options: ListAgentCategoriesOptions): Promise<string>;