@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.js CHANGED
@@ -298,6 +298,35 @@ function abortableSleep(ms, signal) {
298
298
  signal?.addEventListener("abort", onAbort, { once: true });
299
299
  });
300
300
  }
301
+ var PREFILL_TIMEOUT_MS_PER_1K_TOKENS = 640;
302
+ var STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
303
+ var STREAM_FIRST_EVENT_TIMEOUT_MAX_MS = 18e4;
304
+ var STREAM_FIRST_EVENT_TIMEOUT_SCALE_MIN_TOKENS = 2e4;
305
+ function scaledFirstEventTimeoutMs(promptTokens) {
306
+ if (promptTokens < STREAM_FIRST_EVENT_TIMEOUT_SCALE_MIN_TOKENS) return null;
307
+ return Math.min(
308
+ STREAM_FIRST_EVENT_TIMEOUT_MAX_MS,
309
+ STREAM_FIRST_EVENT_TIMEOUT_MS + promptTokens / 1e3 * PREFILL_TIMEOUT_MS_PER_1K_TOKENS
310
+ );
311
+ }
312
+ var CACHE_HEALTH_MIN_PROMPT_TOKENS = 4e4;
313
+ var CACHE_HEALTH_LOW_RATIO = 0.5;
314
+ function assessCacheHealth(usage) {
315
+ const input = usage.inputTokens ?? 0;
316
+ const cacheRead = usage.cacheRead ?? 0;
317
+ const cacheWrite = usage.cacheWrite ?? 0;
318
+ const promptTokens = input + cacheRead + cacheWrite;
319
+ if (promptTokens < CACHE_HEALTH_MIN_PROMPT_TOKENS) {
320
+ return { ratio: null, promptTokens, cacheRead, low: false };
321
+ }
322
+ const ratio = cacheRead / promptTokens;
323
+ return {
324
+ ratio,
325
+ promptTokens,
326
+ cacheRead,
327
+ low: ratio < CACHE_HEALTH_LOW_RATIO
328
+ };
329
+ }
301
330
  async function* agentLoop(messages, options) {
302
331
  const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
303
332
  let effectiveMaxTurns = maxTurns;
@@ -339,6 +368,7 @@ async function* agentLoop(messages, options) {
339
368
  let providerCalls = 0;
340
369
  let nonStreamingCalls = 0;
341
370
  let warnedNonStreaming = false;
371
+ let warnedPromptCacheMiss = false;
342
372
  const MAX_OUTPUT_CEILING_RETRIES = 1;
343
373
  let outputCeilingRetries = 0;
344
374
  const ceilingKey = outputRouteKey({
@@ -348,7 +378,6 @@ async function* agentLoop(messages, options) {
348
378
  });
349
379
  const OVERLOAD_BASE_DELAY_MS = 2e3;
350
380
  const OVERLOAD_MAX_DELAY_MS = 3e4;
351
- const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
352
381
  const STREAM_IDLE_TIMEOUT_MS = 9e4;
353
382
  const STREAM_HARD_TIMEOUT_MS = 9e4;
354
383
  const STREAM_OUTPUT_HARD_TIMEOUT_MS = 3e5;
@@ -357,8 +386,8 @@ async function* agentLoop(messages, options) {
357
386
  const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
358
387
  const usesSilentReasoningBudget = options.provider === "sakana" || options.provider === "openai" && options.thinking != null;
359
388
  const localBackend = isLocalBackendUrl(options.baseUrl);
360
- const firstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
361
- const initialHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
389
+ const baseFirstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
390
+ const baseHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
362
391
  const MAX_TOOLCALL_DELTA_CHARS = 1e6;
363
392
  const MAX_TOOLCALL_DELTA_EVENTS = 2e4;
364
393
  let logicalTurnStartedAt = 0;
@@ -370,17 +399,28 @@ async function* agentLoop(messages, options) {
370
399
  turn++;
371
400
  if (logicalTurnStartedAt === 0) logicalTurnStartedAt = Date.now();
372
401
  toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
373
- if (_diagFn) {
374
- let msgChars = 0;
375
- for (const m of messages) {
376
- if (typeof m.content === "string") msgChars += m.content.length;
377
- else if (Array.isArray(m.content)) {
378
- for (const p of m.content) {
379
- if ("text" in p && typeof p.text === "string") msgChars += p.text.length;
380
- if ("content" in p && typeof p.content === "string") msgChars += p.content.length;
381
- }
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;
382
409
  }
383
410
  }
411
+ }
412
+ let firstEventTimeoutMs;
413
+ let initialHardTimeoutMs;
414
+ if (baseFirstEventTimeoutMs === STREAM_FIRST_EVENT_TIMEOUT_MS) {
415
+ const promptTokens = Math.ceil(msgChars / 3);
416
+ const scaled = scaledFirstEventTimeoutMs(promptTokens);
417
+ firstEventTimeoutMs = scaled ?? baseFirstEventTimeoutMs;
418
+ initialHardTimeoutMs = Math.max(baseHardTimeoutMs, firstEventTimeoutMs + 3e4);
419
+ } else {
420
+ firstEventTimeoutMs = baseFirstEventTimeoutMs;
421
+ initialHardTimeoutMs = baseHardTimeoutMs;
422
+ }
423
+ if (_diagFn) {
384
424
  diag("turn_start", {
385
425
  turn,
386
426
  messages: messages.length,
@@ -990,6 +1030,27 @@ async function* agentLoop(messages, options) {
990
1030
  if (response.usage.cacheWrite) {
991
1031
  totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
992
1032
  }
1033
+ const cacheHealth = assessCacheHealth(response.usage);
1034
+ if (cacheHealth.ratio !== null) {
1035
+ diag("cache_health", {
1036
+ promptTokens: cacheHealth.promptTokens,
1037
+ cacheRead: cacheHealth.cacheRead,
1038
+ ratio: Math.round(cacheHealth.ratio * 100) / 100,
1039
+ provider: options.provider,
1040
+ model: options.model
1041
+ });
1042
+ if (cacheHealth.low && !warnedPromptCacheMiss) {
1043
+ warnedPromptCacheMiss = true;
1044
+ diag("prompt_cache_miss", {
1045
+ promptTokens: cacheHealth.promptTokens,
1046
+ cacheRead: cacheHealth.cacheRead,
1047
+ ratio: Math.round(cacheHealth.ratio * 100) / 100,
1048
+ provider: options.provider,
1049
+ model: options.model,
1050
+ 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)"
1051
+ });
1052
+ }
1053
+ }
993
1054
  if (!emptyExhausted) {
994
1055
  messages.push(response.message);
995
1056
  latestProviderUsage = response.usage;