kimiflare 0.7.0 → 0.7.1

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/dist/index.js CHANGED
@@ -142,7 +142,33 @@ var init_errors = __esm({
142
142
  }
143
143
  });
144
144
 
145
+ // src/agent/messages.ts
146
+ function sanitizeString(str) {
147
+ return str.replace(/[\uD800-\uDFFF]/g, "\uFFFD");
148
+ }
149
+ function jsonReplacer(_key, value) {
150
+ if (typeof value === "string") {
151
+ return sanitizeString(value);
152
+ }
153
+ return value;
154
+ }
155
+ var init_messages = __esm({
156
+ "src/agent/messages.ts"() {
157
+ "use strict";
158
+ }
159
+ });
160
+
145
161
  // src/agent/client.ts
162
+ function cleanErrorMessage(msg) {
163
+ return msg.replace(/^(AiError:\s*)+/, "").trim();
164
+ }
165
+ function isRetryable(err, attempt) {
166
+ if (attempt >= MAX_ATTEMPTS - 1) return false;
167
+ if (err.code !== void 0 && RETRYABLE_CODES.has(err.code)) return true;
168
+ if (err.httpStatus !== void 0 && err.httpStatus >= 500 && err.httpStatus < 600) return true;
169
+ if (err.message.includes("Internal server error")) return true;
170
+ return false;
171
+ }
146
172
  async function* runKimi(opts2) {
147
173
  const url = `https://api.cloudflare.com/client/v4/accounts/${opts2.accountId}/ai/run/${opts2.model}`;
148
174
  const body = {
@@ -156,15 +182,26 @@ async function* runKimi(opts2) {
156
182
  body.reasoning_effort = opts2.reasoningEffort;
157
183
  }
158
184
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
159
- const res = await fetch(url, {
160
- method: "POST",
161
- headers: {
162
- Authorization: `Bearer ${opts2.apiToken}`,
163
- "Content-Type": "application/json"
164
- },
165
- body: JSON.stringify(body),
166
- signal: opts2.signal
167
- });
185
+ let res;
186
+ try {
187
+ res = await fetch(url, {
188
+ method: "POST",
189
+ headers: {
190
+ Authorization: `Bearer ${opts2.apiToken}`,
191
+ "Content-Type": "application/json"
192
+ },
193
+ body: JSON.stringify(body, jsonReplacer),
194
+ signal: opts2.signal
195
+ });
196
+ } catch (fetchErr) {
197
+ const msg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
198
+ if (attempt < MAX_ATTEMPTS - 1) {
199
+ const delay = 500 * 2 ** attempt + Math.random() * 250;
200
+ await sleep(delay, opts2.signal);
201
+ continue;
202
+ }
203
+ throw new KimiApiError(`kimiflare: network error: ${msg}`, void 0, void 0);
204
+ }
168
205
  const contentType = res.headers.get("content-type") ?? "";
169
206
  if (!contentType.includes("text/event-stream")) {
170
207
  const text = await res.text();
@@ -174,13 +211,15 @@ async function* runKimi(opts2) {
174
211
  } catch {
175
212
  }
176
213
  const err = extractCloudflareError(parsed);
177
- if (err?.code === RETRYABLE_CODE && attempt < MAX_ATTEMPTS - 1) {
214
+ const rawMsg = err?.message ?? `HTTP ${res.status}: ${text.slice(0, 300)}`;
215
+ const msg = cleanErrorMessage(rawMsg);
216
+ const apiErr = new KimiApiError(`kimiflare: ${msg}`, err?.code, res.status);
217
+ if (isRetryable(apiErr, attempt)) {
178
218
  const delay = 500 * 2 ** attempt + Math.random() * 250;
179
219
  await sleep(delay, opts2.signal);
180
220
  continue;
181
221
  }
182
- const msg = err?.message ?? `HTTP ${res.status}: ${text.slice(0, 300)}`;
183
- throw new KimiApiError(`kimiflare: ${msg}`, err?.code, res.status);
222
+ throw apiErr;
184
223
  }
185
224
  if (!res.body) throw new KimiApiError("kimiflare: empty response body", void 0, res.status);
186
225
  yield* parseStream(res.body, opts2.signal);
@@ -257,9 +296,14 @@ async function* parseStream(body, signal) {
257
296
  }
258
297
  function extractCloudflareError(parsed) {
259
298
  if (!parsed || typeof parsed !== "object") return null;
260
- const p = parsed;
261
- if (p.success === false && Array.isArray(p.errors) && p.errors.length > 0) {
262
- return { code: p.errors[0]?.code, message: p.errors[0]?.message };
299
+ const cf = parsed;
300
+ if (cf.success === false && Array.isArray(cf.errors) && cf.errors.length > 0) {
301
+ return { code: cf.errors[0]?.code, message: cf.errors[0]?.message };
302
+ }
303
+ const oai = parsed;
304
+ if (oai.object === "error" && typeof oai.message === "string") {
305
+ const codeNum = typeof oai.code === "number" ? oai.code : void 0;
306
+ return { code: codeNum, message: oai.message };
263
307
  }
264
308
  return null;
265
309
  }
@@ -277,13 +321,14 @@ function sleep(ms, signal) {
277
321
  signal?.addEventListener("abort", onAbort, { once: true });
278
322
  });
279
323
  }
280
- var RETRYABLE_CODE, MAX_ATTEMPTS;
324
+ var RETRYABLE_CODES, MAX_ATTEMPTS;
281
325
  var init_client = __esm({
282
326
  "src/agent/client.ts"() {
283
327
  "use strict";
284
328
  init_sse();
285
329
  init_errors();
286
- RETRYABLE_CODE = 3040;
330
+ init_messages();
331
+ RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
287
332
  MAX_ATTEMPTS = 5;
288
333
  }
289
334
  });
@@ -360,9 +405,17 @@ async function runAgentTurn(opts2) {
360
405
  }
361
406
  const assistantMsg = {
362
407
  role: "assistant",
363
- content: content || null,
364
- ...reasoning ? { reasoning_content: reasoning } : {},
365
- ...toolCalls.length ? { tool_calls: toolCalls } : {}
408
+ content: content ? sanitizeString(content) : null,
409
+ ...reasoning ? { reasoning_content: sanitizeString(reasoning) } : {},
410
+ ...toolCalls.length ? {
411
+ tool_calls: toolCalls.map((tc) => ({
412
+ ...tc,
413
+ function: {
414
+ name: tc.function.name,
415
+ arguments: sanitizeString(tc.function.arguments)
416
+ }
417
+ }))
418
+ } : {}
366
419
  };
367
420
  opts2.messages.push(assistantMsg);
368
421
  opts2.callbacks.onAssistantFinal?.(assistantMsg);
@@ -377,7 +430,7 @@ async function runAgentTurn(opts2) {
377
430
  opts2.messages.push({
378
431
  role: "tool",
379
432
  tool_call_id: result.tool_call_id,
380
- content: result.content,
433
+ content: sanitizeString(result.content),
381
434
  name: result.name
382
435
  });
383
436
  opts2.callbacks.onToolResult?.(result);
@@ -390,6 +443,7 @@ var init_loop = __esm({
390
443
  "use strict";
391
444
  init_client();
392
445
  init_registry();
446
+ init_messages();
393
447
  }
394
448
  });
395
449
 
@@ -3189,7 +3243,7 @@ function App({ initialCfg }) {
3189
3243
  "Do not call `tasks_set` for this. Just read what you need, then write the file."
3190
3244
  ].join("\n");
3191
3245
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: "/init" }]);
3192
- messagesRef.current.push({ role: "user", content: prompt });
3246
+ messagesRef.current.push({ role: "user", content: sanitizeString(prompt) });
3193
3247
  setBusy(true);
3194
3248
  setTurnStartedAt(Date.now());
3195
3249
  const controller = new AbortController();
@@ -3543,7 +3597,7 @@ use: /thinking low | medium | high`
3543
3597
  if (trimmed.startsWith("/") && handleSlash(trimmed)) return;
3544
3598
  const display = displayText?.trim() || trimmed;
3545
3599
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display }]);
3546
- messagesRef.current.push({ role: "user", content: trimmed });
3600
+ messagesRef.current.push({ role: "user", content: sanitizeString(trimmed) });
3547
3601
  setBusy(true);
3548
3602
  setTurnStartedAt(Date.now());
3549
3603
  const controller = new AbortController();
@@ -3827,6 +3881,7 @@ var init_app = __esm({
3827
3881
  init_system_prompt();
3828
3882
  init_compact();
3829
3883
  init_executor();
3884
+ init_messages();
3830
3885
  init_chat();
3831
3886
  init_status();
3832
3887
  init_permission();