@kenkaiiii/gg-agent 5.83.2 → 5.83.4

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
@@ -7,6 +7,7 @@ import {
7
7
  stream,
8
8
  EventStream,
9
9
  GGAIError,
10
+ ResponseStalledError,
10
11
  environmentSecrets,
11
12
  isHardBillingMessage,
12
13
  redactValue,
@@ -605,6 +606,9 @@ async function* agentLoop(messages, options) {
605
606
  let overloadRetries = 0;
606
607
  let emptyResponseRetries = 0;
607
608
  let stallRetries = 0;
609
+ let splitToolCallHintAdded = false;
610
+ let stalledInToolCall = false;
611
+ let stalledAfterConnecting = false;
608
612
  let runawayToolcallRetries = 0;
609
613
  let overflowCompactionAttempts = 0;
610
614
  let toolResultTruncationAttempted = false;
@@ -622,6 +626,7 @@ async function* agentLoop(messages, options) {
622
626
  const STALL_DELAY_MS = 1e3;
623
627
  const MIN_PARTIAL_PRESERVE_CHARS = 200;
624
628
  const PARTIAL_CONTINUATION_PROMPT = "[Your previous response was cut off by a connection failure. The text above is what was already delivered to the user. Continue exactly from where it stopped \u2014 do not repeat or restart it.]";
629
+ const SPLIT_TOOL_CALL_PROMPT = "[Your previous response was cut off while you were still writing a tool call's arguments, after minutes without data from the provider. None of that call was applied. Do not resend it as one large call: split the work into smaller pieces (e.g. a shorter first version, then further edits, or one file per call).]";
625
630
  const MAX_OUTPUT_CONTINUATIONS = 2;
626
631
  const MAX_TOKENS_CONTINUATION_PROMPT = "[Your previous response hit the output-token limit and was cut off. The text above is what was already delivered to the user. Continue exactly from where it stopped \u2014 do not repeat or restart it.]";
627
632
  let maxTokensContinuations = 0;
@@ -647,7 +652,7 @@ async function* agentLoop(messages, options) {
647
652
  const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
648
653
  const nonStreamingHardTimeoutMs = options.thinking != null ? STREAM_THINKING_HARD_TIMEOUT_MS : NON_STREAMING_HARD_TIMEOUT_MS;
649
654
  const VISIBLE_STALL_AFTER_MS = 6e4;
650
- const usesSilentReasoningBudget = options.provider === "sakana" || options.provider === "openai" && options.thinking != null;
655
+ const usesSilentReasoningBudget = options.provider === "sakana" || options.provider === "xai" || options.provider === "openai" && options.thinking != null;
651
656
  const localBackend = isLocalBackendUrl(options.baseUrl);
652
657
  const baseFirstEventTimeoutMs = localBackend ? Number.POSITIVE_INFINITY : usesSilentReasoningBudget ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
653
658
  const baseHardTimeoutMs = localBackend || usesSilentReasoningBudget ? STREAM_THINKING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
@@ -869,7 +874,7 @@ async function* agentLoop(messages, options) {
869
874
  }, hardTimeoutMs);
870
875
  }
871
876
  }
872
- if (event.type === "thinking_delta" && !hasReceivedThinking) {
877
+ if ((event.type === "thinking_delta" || event.type === "keepalive" && event.reasoning === true) && !hasReceivedThinking) {
873
878
  hasReceivedThinking = true;
874
879
  if (hardTimer) clearTimeout(hardTimer);
875
880
  hardTimeoutMs = STREAM_THINKING_HARD_TIMEOUT_MS;
@@ -1198,6 +1203,9 @@ async function* agentLoop(messages, options) {
1198
1203
  const malformed = isMalformedStream(err);
1199
1204
  const socketDrop = isTransportFailure(err);
1200
1205
  const transportFailure = (idleTimedOut || malformed || socketDrop) && !options.signal?.aborted;
1206
+ const attemptMs = Date.now() - streamCallStart;
1207
+ if (transportFailure && toolInputOpen) stalledInToolCall = true;
1208
+ if (transportFailure && streamEventCount > 0) stalledAfterConnecting = true;
1201
1209
  if (transportFailure && stallRetries < MAX_STALL_RETRIES) {
1202
1210
  stallRetries++;
1203
1211
  const cause = malformed ? "malformed_stream" : socketDrop ? "socket_drop" : "stream_stall";
@@ -1228,6 +1236,20 @@ async function* agentLoop(messages, options) {
1228
1236
  });
1229
1237
  preservedChars = attemptText.length;
1230
1238
  }
1239
+ let splitHint = false;
1240
+ if (toolInputOpen && attemptMs >= VISIBLE_STALL_AFTER_MS && !splitToolCallHintAdded) {
1241
+ splitToolCallHintAdded = true;
1242
+ splitHint = true;
1243
+ messages.push({
1244
+ role: "user",
1245
+ content: SPLIT_TOOL_CALL_PROMPT,
1246
+ provenance: {
1247
+ source: "runtime",
1248
+ kind: "continuation",
1249
+ visibility: "hidden"
1250
+ }
1251
+ });
1252
+ }
1231
1253
  diag("retry", {
1232
1254
  reason: cause,
1233
1255
  attempt: stallRetries,
@@ -1235,7 +1257,8 @@ async function* agentLoop(messages, options) {
1235
1257
  delayMs,
1236
1258
  events: streamEventCount,
1237
1259
  nonStreaming: useNonStreamingFallback,
1238
- preservedChars
1260
+ preservedChars,
1261
+ splitHint
1239
1262
  });
1240
1263
  yield {
1241
1264
  type: "retry",
@@ -1262,9 +1285,25 @@ async function* agentLoop(messages, options) {
1262
1285
  eventTypes: eventTypeCounts,
1263
1286
  lastEventType,
1264
1287
  sinceLastEventMs: Date.now() - lastEventTime,
1265
- attemptDurationMs: Date.now() - streamCallStart,
1266
- maxConsumerLagMs
1288
+ attemptDurationMs: attemptMs,
1289
+ maxConsumerLagMs,
1290
+ stalledAfterConnecting,
1291
+ stalledInToolCall
1267
1292
  });
1293
+ if (stalledAfterConnecting) {
1294
+ yield {
1295
+ type: "error",
1296
+ error: new ResponseStalledError(
1297
+ `The provider stopped sending data before the response finished, ${MAX_STALL_RETRIES} automatic retries in a row. Your conversation is preserved.`,
1298
+ {
1299
+ inToolCall: stalledInToolCall,
1300
+ hint: stalledInToolCall ? "The model was writing one very large tool call each time. Ask it to make the change in smaller pieces (shorter files or several edits), then retry." : "Retry once. If it keeps happening, choose another model or lower the thinking level, and disable any VPN or proxy that may buffer streamed responses.",
1301
+ cause: err
1302
+ }
1303
+ )
1304
+ };
1305
+ break;
1306
+ }
1268
1307
  yield {
1269
1308
  type: "error",
1270
1309
  error: new GGAIError(
@@ -1313,6 +1352,9 @@ async function* agentLoop(messages, options) {
1313
1352
  }
1314
1353
  overloadRetries = 0;
1315
1354
  stallRetries = 0;
1355
+ splitToolCallHintAdded = false;
1356
+ stalledInToolCall = false;
1357
+ stalledAfterConnecting = false;
1316
1358
  runawayToolcallRetries = 0;
1317
1359
  const contentArr = Array.isArray(response.message.content) ? response.message.content : null;
1318
1360
  const hasActionableContent = response.message.content !== "" && contentArr !== null && contentArr.some(