@kenkaiiii/gg-agent 5.61.0 → 5.61.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.cjs CHANGED
@@ -326,6 +326,35 @@ function abortableSleep(ms, signal) {
326
326
  signal?.addEventListener("abort", onAbort, { once: true });
327
327
  });
328
328
  }
329
+ var PREFILL_TIMEOUT_MS_PER_1K_TOKENS = 640;
330
+ var STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
331
+ var STREAM_FIRST_EVENT_TIMEOUT_MAX_MS = 18e4;
332
+ var STREAM_FIRST_EVENT_TIMEOUT_SCALE_MIN_TOKENS = 2e4;
333
+ function scaledFirstEventTimeoutMs(promptTokens) {
334
+ if (promptTokens < STREAM_FIRST_EVENT_TIMEOUT_SCALE_MIN_TOKENS) return null;
335
+ return Math.min(
336
+ STREAM_FIRST_EVENT_TIMEOUT_MAX_MS,
337
+ STREAM_FIRST_EVENT_TIMEOUT_MS + promptTokens / 1e3 * PREFILL_TIMEOUT_MS_PER_1K_TOKENS
338
+ );
339
+ }
340
+ var CACHE_HEALTH_MIN_PROMPT_TOKENS = 4e4;
341
+ var CACHE_HEALTH_LOW_RATIO = 0.5;
342
+ function assessCacheHealth(usage) {
343
+ const input = usage.inputTokens ?? 0;
344
+ const cacheRead = usage.cacheRead ?? 0;
345
+ const cacheWrite = usage.cacheWrite ?? 0;
346
+ const promptTokens = input + cacheRead + cacheWrite;
347
+ if (promptTokens < CACHE_HEALTH_MIN_PROMPT_TOKENS) {
348
+ return { ratio: null, promptTokens, cacheRead, low: false };
349
+ }
350
+ const ratio = cacheRead / promptTokens;
351
+ return {
352
+ ratio,
353
+ promptTokens,
354
+ cacheRead,
355
+ low: ratio < CACHE_HEALTH_LOW_RATIO
356
+ };
357
+ }
329
358
  async function* agentLoop(messages, options) {
330
359
  const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
331
360
  let effectiveMaxTurns = maxTurns;
@@ -367,6 +396,7 @@ async function* agentLoop(messages, options) {
367
396
  let providerCalls = 0;
368
397
  let nonStreamingCalls = 0;
369
398
  let warnedNonStreaming = false;
399
+ let warnedPromptCacheMiss = false;
370
400
  const MAX_OUTPUT_CEILING_RETRIES = 1;
371
401
  let outputCeilingRetries = 0;
372
402
  const ceilingKey = outputRouteKey({
@@ -376,7 +406,6 @@ async function* agentLoop(messages, options) {
376
406
  });
377
407
  const OVERLOAD_BASE_DELAY_MS = 2e3;
378
408
  const OVERLOAD_MAX_DELAY_MS = 3e4;
379
- const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
380
409
  const STREAM_IDLE_TIMEOUT_MS = 9e4;
381
410
  const STREAM_HARD_TIMEOUT_MS = 9e4;
382
411
  const STREAM_OUTPUT_HARD_TIMEOUT_MS = 3e5;
@@ -385,8 +414,8 @@ async function* agentLoop(messages, options) {
385
414
  const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
386
415
  const usesSilentReasoningBudget = options.provider === "sakana" || options.provider === "openai" && options.thinking != null;
387
416
  const localBackend = isLocalBackendUrl(options.baseUrl);
388
- const firstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
389
- const initialHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
417
+ const baseFirstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
418
+ const baseHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
390
419
  const MAX_TOOLCALL_DELTA_CHARS = 1e6;
391
420
  const MAX_TOOLCALL_DELTA_EVENTS = 2e4;
392
421
  let logicalTurnStartedAt = 0;
@@ -398,17 +427,28 @@ async function* agentLoop(messages, options) {
398
427
  turn++;
399
428
  if (logicalTurnStartedAt === 0) logicalTurnStartedAt = Date.now();
400
429
  toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
401
- if (_diagFn) {
402
- let msgChars = 0;
403
- for (const m of messages) {
404
- if (typeof m.content === "string") msgChars += m.content.length;
405
- else if (Array.isArray(m.content)) {
406
- for (const p of m.content) {
407
- if ("text" in p && typeof p.text === "string") msgChars += p.text.length;
408
- if ("content" in p && typeof p.content === "string") msgChars += p.content.length;
409
- }
430
+ let msgChars = 0;
431
+ for (const m of messages) {
432
+ if (typeof m.content === "string") msgChars += m.content.length;
433
+ else if (Array.isArray(m.content)) {
434
+ for (const p of m.content) {
435
+ if ("text" in p && typeof p.text === "string") msgChars += p.text.length;
436
+ if ("content" in p && typeof p.content === "string") msgChars += p.content.length;
410
437
  }
411
438
  }
439
+ }
440
+ let firstEventTimeoutMs;
441
+ let initialHardTimeoutMs;
442
+ if (baseFirstEventTimeoutMs === STREAM_FIRST_EVENT_TIMEOUT_MS) {
443
+ const promptTokens = Math.ceil(msgChars / 3);
444
+ const scaled = scaledFirstEventTimeoutMs(promptTokens);
445
+ firstEventTimeoutMs = scaled ?? baseFirstEventTimeoutMs;
446
+ initialHardTimeoutMs = Math.max(baseHardTimeoutMs, firstEventTimeoutMs + 3e4);
447
+ } else {
448
+ firstEventTimeoutMs = baseFirstEventTimeoutMs;
449
+ initialHardTimeoutMs = baseHardTimeoutMs;
450
+ }
451
+ if (_diagFn) {
412
452
  diag("turn_start", {
413
453
  turn,
414
454
  messages: messages.length,
@@ -1018,6 +1058,27 @@ async function* agentLoop(messages, options) {
1018
1058
  if (response.usage.cacheWrite) {
1019
1059
  totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
1020
1060
  }
1061
+ const cacheHealth = assessCacheHealth(response.usage);
1062
+ if (cacheHealth.ratio !== null) {
1063
+ diag("cache_health", {
1064
+ promptTokens: cacheHealth.promptTokens,
1065
+ cacheRead: cacheHealth.cacheRead,
1066
+ ratio: Math.round(cacheHealth.ratio * 100) / 100,
1067
+ provider: options.provider,
1068
+ model: options.model
1069
+ });
1070
+ if (cacheHealth.low && !warnedPromptCacheMiss) {
1071
+ warnedPromptCacheMiss = true;
1072
+ diag("prompt_cache_miss", {
1073
+ promptTokens: cacheHealth.promptTokens,
1074
+ cacheRead: cacheHealth.cacheRead,
1075
+ ratio: Math.round(cacheHealth.ratio * 100) / 100,
1076
+ provider: options.provider,
1077
+ model: options.model,
1078
+ impact: "large prompts are consistently served mostly uncached \u2014 every turn re-prefills the whole context; if this persists, lower the provider's compaction latency cap (resolveCompactionPolicy)"
1079
+ });
1080
+ }
1081
+ }
1021
1082
  if (!emptyExhausted) {
1022
1083
  messages.push(response.message);
1023
1084
  latestProviderUsage = response.usage;