kimiflare 0.13.0 → 0.13.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
@@ -230,57 +230,87 @@ async function* parseStream(body, signal) {
230
230
  const toolCalls = /* @__PURE__ */ new Map();
231
231
  let lastUsage = null;
232
232
  let finishReason = null;
233
- for await (const dataStr of readSSE(body, signal)) {
234
- if (dataStr === "[DONE]") break;
235
- let chunk = null;
236
- try {
237
- chunk = JSON.parse(dataStr);
238
- } catch {
239
- continue;
240
- }
241
- if (!chunk) continue;
242
- if (chunk.usage) {
243
- lastUsage = chunk.usage;
244
- yield { type: "usage", usage: chunk.usage };
245
- }
246
- const choice = chunk.choices?.[0];
247
- if (!choice) continue;
248
- const d = choice.delta;
249
- if (d) {
250
- if (typeof d.reasoning_content === "string" && d.reasoning_content.length) {
251
- yield { type: "reasoning", delta: d.reasoning_content };
233
+ let timeoutId = null;
234
+ let timedOut = false;
235
+ const resetTimeout = () => {
236
+ if (timeoutId) clearTimeout(timeoutId);
237
+ timeoutId = setTimeout(() => {
238
+ timedOut = true;
239
+ }, STREAM_TIMEOUT_MS);
240
+ };
241
+ try {
242
+ for await (const dataStr of readSSE(body, signal)) {
243
+ if (timedOut) {
244
+ throw new KimiApiError(
245
+ `kimiflare: stream timed out (no data for ${STREAM_TIMEOUT_MS / 1e3}s)`,
246
+ void 0,
247
+ void 0
248
+ );
252
249
  }
253
- if (typeof d.content === "string" && d.content.length) {
254
- yield { type: "text", delta: d.content };
250
+ resetTimeout();
251
+ if (dataStr === "[DONE]") break;
252
+ let chunk = null;
253
+ try {
254
+ chunk = JSON.parse(dataStr);
255
+ } catch {
256
+ continue;
255
257
  }
256
- if (Array.isArray(d.tool_calls)) {
257
- for (const tc of d.tool_calls) {
258
- const idx = typeof tc.index === "number" ? tc.index : 0;
259
- let buf = toolCalls.get(idx);
260
- const incomingName = tc.function?.name ?? null;
261
- const incomingId = tc.id ?? null;
262
- if (!buf) {
263
- buf = { id: incomingId ?? `tc_${idx}`, name: incomingName ?? "", args: "" };
264
- toolCalls.set(idx, buf);
265
- if (buf.name) {
266
- yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
258
+ if (!chunk) continue;
259
+ if (chunk.usage) {
260
+ lastUsage = chunk.usage;
261
+ yield { type: "usage", usage: chunk.usage };
262
+ }
263
+ const choice = chunk.choices?.[0];
264
+ if (!choice) continue;
265
+ const d = choice.delta;
266
+ if (d) {
267
+ if (typeof d.reasoning_content === "string" && d.reasoning_content.length) {
268
+ yield { type: "reasoning", delta: d.reasoning_content };
269
+ }
270
+ if (typeof d.content === "string" && d.content.length) {
271
+ yield { type: "text", delta: d.content };
272
+ }
273
+ if (Array.isArray(d.tool_calls)) {
274
+ for (const tc of d.tool_calls) {
275
+ const idx = typeof tc.index === "number" ? tc.index : 0;
276
+ let buf = toolCalls.get(idx);
277
+ const incomingName = tc.function?.name ?? null;
278
+ const incomingId = tc.id ?? null;
279
+ if (!buf) {
280
+ buf = { id: incomingId ?? `tc_${idx}`, name: incomingName ?? "", args: "" };
281
+ toolCalls.set(idx, buf);
282
+ if (buf.name) {
283
+ yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
284
+ }
285
+ } else {
286
+ if (!buf.name && incomingName) {
287
+ buf.name = incomingName;
288
+ yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
289
+ }
290
+ if (buf.id.startsWith("tc_") && incomingId) buf.id = incomingId;
267
291
  }
268
- } else {
269
- if (!buf.name && incomingName) {
270
- buf.name = incomingName;
271
- yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
292
+ const argDelta = tc.function?.arguments;
293
+ if (typeof argDelta === "string" && argDelta.length) {
294
+ buf.args += argDelta;
295
+ yield { type: "tool_call_args", index: idx, argsDelta: argDelta };
272
296
  }
273
- if (buf.id.startsWith("tc_") && incomingId) buf.id = incomingId;
274
- }
275
- const argDelta = tc.function?.arguments;
276
- if (typeof argDelta === "string" && argDelta.length) {
277
- buf.args += argDelta;
278
- yield { type: "tool_call_args", index: idx, argsDelta: argDelta };
279
297
  }
280
298
  }
281
299
  }
300
+ if (choice.finish_reason) finishReason = choice.finish_reason;
282
301
  }
283
- if (choice.finish_reason) finishReason = choice.finish_reason;
302
+ for (const [idx, buf] of [...toolCalls.entries()].sort((a, b) => a[0] - b[0])) {
303
+ if (!buf.name) continue;
304
+ yield {
305
+ type: "tool_call_complete",
306
+ index: idx,
307
+ id: buf.id,
308
+ name: buf.name,
309
+ arguments: buf.args
310
+ };
311
+ }
312
+ } finally {
313
+ if (timeoutId) clearTimeout(timeoutId);
284
314
  }
285
315
  for (const [idx, buf] of [...toolCalls.entries()].sort((a, b) => a[0] - b[0])) {
286
316
  if (!buf.name) continue;
@@ -354,7 +384,7 @@ function sleep(ms, signal) {
354
384
  signal?.addEventListener("abort", onAbort, { once: true });
355
385
  });
356
386
  }
357
- var RETRYABLE_CODES, MAX_ATTEMPTS;
387
+ var RETRYABLE_CODES, MAX_ATTEMPTS, STREAM_TIMEOUT_MS;
358
388
  var init_client = __esm({
359
389
  "src/agent/client.ts"() {
360
390
  "use strict";
@@ -363,6 +393,7 @@ var init_client = __esm({
363
393
  init_messages();
364
394
  RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
365
395
  MAX_ATTEMPTS = 5;
396
+ STREAM_TIMEOUT_MS = 6e4;
366
397
  }
367
398
  });
368
399
 
@@ -3575,6 +3606,60 @@ function findImagePaths(text) {
3575
3606
  }
3576
3607
  return [...new Set(paths)];
3577
3608
  }
3609
+ function stripImagesFromHistory(messages) {
3610
+ for (const m of messages) {
3611
+ if (!Array.isArray(m.content)) continue;
3612
+ let changed = false;
3613
+ const next = [];
3614
+ for (const part of m.content) {
3615
+ if (part.type === "image_url") {
3616
+ changed = true;
3617
+ next.push({ type: "text", text: "[image]" });
3618
+ } else {
3619
+ next.push(part);
3620
+ }
3621
+ }
3622
+ if (changed) {
3623
+ m.content = next;
3624
+ }
3625
+ }
3626
+ }
3627
+ function truncateOldToolResults(messages, keepRecent) {
3628
+ const toolIndices = [];
3629
+ for (let i = 0; i < messages.length; i++) {
3630
+ if (messages[i].role === "tool") toolIndices.push(i);
3631
+ }
3632
+ const cutoff = toolIndices.length - keepRecent;
3633
+ for (let i = 0; i < cutoff; i++) {
3634
+ const idx = toolIndices[i];
3635
+ const m = messages[idx];
3636
+ const text = typeof m.content === "string" ? m.content : "";
3637
+ if (text.length > 500) {
3638
+ m.content = text.slice(0, 500) + "\n\u2026 [truncated]";
3639
+ }
3640
+ }
3641
+ }
3642
+ async function maybeAutoCompact(messages, cfg, onInfo) {
3643
+ const heapUsed = process.memoryUsage().heapUsed;
3644
+ if (heapUsed < HEAP_AUTO_COMPACT_THRESHOLD || messages.length <= MSG_AUTO_COMPACT_THRESHOLD) {
3645
+ return false;
3646
+ }
3647
+ onInfo("auto-compacting to reduce memory usage");
3648
+ const result = await compactMessages({
3649
+ accountId: cfg.accountId,
3650
+ apiToken: cfg.apiToken,
3651
+ model: cfg.model,
3652
+ messages,
3653
+ keepLastTurns: 2
3654
+ });
3655
+ if (result.replacedCount > 0) {
3656
+ messages.length = 0;
3657
+ messages.push(...result.newMessages);
3658
+ onInfo(`compacted ${result.replacedCount} messages into a summary`);
3659
+ return true;
3660
+ }
3661
+ return false;
3662
+ }
3578
3663
  function App({ initialCfg, initialUpdateResult }) {
3579
3664
  const { exit } = useApp();
3580
3665
  const [cfg, setCfg] = useState6(initialCfg);
@@ -4002,6 +4087,13 @@ function App({ initialCfg, initialUpdateResult }) {
4002
4087
  })
4003
4088
  }
4004
4089
  });
4090
+ stripImagesFromHistory(messagesRef.current);
4091
+ truncateOldToolResults(messagesRef.current, 8);
4092
+ await maybeAutoCompact(
4093
+ messagesRef.current,
4094
+ cfg,
4095
+ (text) => setEvents((es) => [...es, { kind: "info", key: mkKey(), text }])
4096
+ );
4005
4097
  if (existsSync(join5(cwd, "KIMI.md"))) {
4006
4098
  messagesRef.current[0] = {
4007
4099
  role: "system",
@@ -4456,7 +4548,14 @@ use: /thinking low | medium | high`
4456
4548
  })
4457
4549
  }
4458
4550
  });
4459
- await saveSessionSafe();
4551
+ stripImagesFromHistory(messagesRef.current);
4552
+ truncateOldToolResults(messagesRef.current, 8);
4553
+ await maybeAutoCompact(
4554
+ messagesRef.current,
4555
+ cfg,
4556
+ (text2) => setEvents((es) => [...es, { kind: "info", key: mkKey(), text: text2 }])
4557
+ );
4558
+ void saveSessionSafe();
4460
4559
  } catch (e) {
4461
4560
  if (e.name === "AbortError") {
4462
4561
  setEvents((es) => [...es, { kind: "info", key: mkKey(), text: "(interrupted)" }]);
@@ -4652,7 +4751,7 @@ async function renderApp(cfg, updateResult) {
4652
4751
  const instance = render(/* @__PURE__ */ jsx13(App, { initialCfg: cfg, initialUpdateResult: updateResult }));
4653
4752
  await instance.waitUntilExit();
4654
4753
  }
4655
- var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
4754
+ var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS, HEAP_AUTO_COMPACT_THRESHOLD, MSG_AUTO_COMPACT_THRESHOLD;
4656
4755
  var init_app = __esm({
4657
4756
  "src/app.tsx"() {
4658
4757
  "use strict";
@@ -4689,6 +4788,8 @@ var init_app = __esm({
4689
4788
  medium: "medium \u2014 balanced (default). Solid quality on most edits, fast on trivial prompts.",
4690
4789
  high: "high \u2014 deepest reasoning; slowest. Best for complex debugging, architecture, multi-file refactors."
4691
4790
  };
4791
+ HEAP_AUTO_COMPACT_THRESHOLD = 2e9;
4792
+ MSG_AUTO_COMPACT_THRESHOLD = 50;
4692
4793
  }
4693
4794
  });
4694
4795