zelari-code 1.48.0 → 1.48.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.
@@ -29131,6 +29131,7 @@ __export(openai_compatible_exports, {
29131
29131
  parseCachedPromptTokens: () => parseCachedPromptTokens,
29132
29132
  providerConfigFor: () => providerConfigFor,
29133
29133
  providerFromEnv: () => providerFromEnv,
29134
+ readChunkWithTimeout: () => readChunkWithTimeout,
29134
29135
  resolveActiveProvider: () => resolveActiveProvider,
29135
29136
  resolveBaseUrl: () => resolveBaseUrl
29136
29137
  });
@@ -29156,13 +29157,20 @@ async function readChunkWithTimeout(reader, opts) {
29156
29157
  if (opts.signal?.aborted) {
29157
29158
  throw new Error("aborted");
29158
29159
  }
29159
- const remaining = opts.deadlineMs - Date.now();
29160
+ const now = Date.now();
29161
+ const remaining = opts.deadlineMs - now;
29160
29162
  if (remaining <= 0) {
29161
29163
  throw new Error(
29162
29164
  `Provider stream exceeded max duration (${Math.round(PROVIDER_STREAM_MAX_MS / 1e3)}s). Raise ZELARI_PROVIDER_STREAM_MAX_MS if needed.`
29163
29165
  );
29164
29166
  }
29165
- const waitMs = Math.min(opts.idleMs, remaining);
29167
+ const idleElapsed = now - opts.lastUsefulAt();
29168
+ if (idleElapsed >= opts.idleMs) {
29169
+ throw new Error(
29170
+ `Provider stream idle for ${Math.round(idleElapsed / 1e3)}s (no content tokens \u2014 keep-alive frames don't count). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
29171
+ );
29172
+ }
29173
+ const waitMs = Math.min(opts.idleMs - idleElapsed, remaining);
29166
29174
  let idleTimer;
29167
29175
  let onAbort;
29168
29176
  try {
@@ -29449,6 +29457,7 @@ function openaiCompatibleProvider(config2) {
29449
29457
  if (args === null) continue;
29450
29458
  toolCallAccumulator.delete(idx);
29451
29459
  emittedToolCall = true;
29460
+ markUseful();
29452
29461
  yield {
29453
29462
  kind: "tool_call",
29454
29463
  toolCallId: existing.id || `tc-${idx}`,
@@ -29459,6 +29468,10 @@ function openaiCompatibleProvider(config2) {
29459
29468
  toolCallAccumulator.clear();
29460
29469
  };
29461
29470
  const streamDeadline = Date.now() + PROVIDER_STREAM_MAX_MS;
29471
+ let lastUsefulAt = Date.now();
29472
+ const markUseful = () => {
29473
+ lastUsefulAt = Date.now();
29474
+ };
29462
29475
  try {
29463
29476
  while (true) {
29464
29477
  let chunk;
@@ -29466,7 +29479,8 @@ function openaiCompatibleProvider(config2) {
29466
29479
  chunk = await readChunkWithTimeout(reader, {
29467
29480
  idleMs: PROVIDER_STREAM_IDLE_MS,
29468
29481
  deadlineMs: streamDeadline,
29469
- signal: params.signal
29482
+ signal: params.signal,
29483
+ lastUsefulAt: () => lastUsefulAt
29470
29484
  });
29471
29485
  } catch (err) {
29472
29486
  const msg = err instanceof Error ? err.message : String(err);
@@ -29507,6 +29521,7 @@ function openaiCompatibleProvider(config2) {
29507
29521
  const completionTokens = typeof parsed.usage.completion_tokens === "number" ? parsed.usage.completion_tokens : 0;
29508
29522
  const totalTokens = typeof parsed.usage.total_tokens === "number" ? parsed.usage.total_tokens : promptTokens + completionTokens;
29509
29523
  const cachedPromptTokens = parseCachedPromptTokens(parsed.usage);
29524
+ markUseful();
29510
29525
  yield {
29511
29526
  kind: "usage",
29512
29527
  usage: {
@@ -29518,10 +29533,12 @@ function openaiCompatibleProvider(config2) {
29518
29533
  };
29519
29534
  }
29520
29535
  if (typeof delta?.content === "string" && delta.content.length > 0) {
29536
+ markUseful();
29521
29537
  yield { kind: "text", delta: delta.content };
29522
29538
  }
29523
29539
  const reasoning = delta?.reasoning_content ?? delta?.reasoning;
29524
29540
  if (typeof reasoning === "string" && reasoning.length > 0) {
29541
+ markUseful();
29525
29542
  yield { kind: "thinking", delta: reasoning };
29526
29543
  }
29527
29544
  const details = delta?.reasoning_details;
@@ -29533,9 +29550,13 @@ function openaiCompatibleProvider(config2) {
29533
29550
  if (t.startsWith(reasoningDetailsBuf)) {
29534
29551
  const piece = t.slice(reasoningDetailsBuf.length);
29535
29552
  reasoningDetailsBuf = t;
29536
- if (piece.length > 0) yield { kind: "thinking", delta: piece };
29553
+ if (piece.length > 0) {
29554
+ markUseful();
29555
+ yield { kind: "thinking", delta: piece };
29556
+ }
29537
29557
  } else {
29538
29558
  reasoningDetailsBuf += t;
29559
+ markUseful();
29539
29560
  yield { kind: "thinking", delta: t };
29540
29561
  }
29541
29562
  }
@@ -29556,6 +29577,7 @@ function openaiCompatibleProvider(config2) {
29556
29577
  }
29557
29578
  if (choice?.finish_reason) {
29558
29579
  yield* flushToolAccumulator();
29580
+ markUseful();
29559
29581
  const reason = choice.finish_reason === "stop" && emittedToolCall ? "tool_calls" : choice.finish_reason;
29560
29582
  yield { kind: "finish", reason };
29561
29583
  }