@kenkaiiii/gg-agent 5.4.1 → 5.4.3

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.d.ts CHANGED
@@ -60,9 +60,21 @@ interface AgentDoneEvent {
60
60
  totalTurns: number;
61
61
  totalUsage: Usage;
62
62
  }
63
+ /**
64
+ * Terminal signal emitted when the loop stops because it exhausted its turn
65
+ * budget (`maxTurns`) mid-task — i.e. the model still wanted to run tools but
66
+ * ran out of turns. Distinguishes a hard cut-off from a clean completion so
67
+ * callers (e.g. the subagent spawner) can tell the parent the output may be
68
+ * incomplete. Yielded immediately before the final `agent_done`.
69
+ */
70
+ interface AgentMaxTurnsEvent {
71
+ type: "max_turns";
72
+ totalTurns: number;
73
+ maxTurns: number;
74
+ }
63
75
  interface AgentRetryEvent {
64
76
  type: "retry";
65
- reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact";
77
+ reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch";
66
78
  attempt: number;
67
79
  maxAttempts: number;
68
80
  delayMs: number;
@@ -101,7 +113,7 @@ interface AgentFollowUpMessageEvent {
101
113
  type: "follow_up_message";
102
114
  content: Message["content"];
103
115
  }
104
- type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentErrorEvent;
116
+ type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
105
117
  interface AgentOptions {
106
118
  provider: StreamOptions["provider"];
107
119
  model: string;
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { ZodError, prettifyError } from "zod";
6
6
  import {
7
7
  stream,
8
8
  EventStream,
9
+ GGAIError,
9
10
  isHardBillingMessage
10
11
  } from "@kenkaiiii/gg-ai";
11
12
  var DEFAULT_MAX_TURNS = 300;
@@ -188,6 +189,7 @@ async function* agentLoop(messages, options) {
188
189
  const toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
189
190
  const totalUsage = { inputTokens: 0, outputTokens: 0 };
190
191
  let turn = 0;
192
+ let hitMaxTurns = false;
191
193
  let firstTurn = true;
192
194
  let consecutivePauses = 0;
193
195
  let toolPairingRepaired = false;
@@ -198,6 +200,7 @@ async function* agentLoop(messages, options) {
198
200
  let overflowCompactionAttempts = 0;
199
201
  let toolResultTruncationAttempted = false;
200
202
  const invalidToolArgumentCounts = /* @__PURE__ */ new Map();
203
+ let toolArgumentAutoContinueUsed = false;
201
204
  let useNonStreamingFallback = false;
202
205
  const MAX_OVERLOAD_RETRIES = 10;
203
206
  const MAX_EMPTY_RESPONSE_RETRIES = 2;
@@ -764,8 +767,12 @@ async function* agentLoop(messages, options) {
764
767
  }
765
768
  }
766
769
  let fatalToolArgumentError = null;
767
- const markFatalToolArgumentError = (error) => {
770
+ let fatalToolArgumentRecoverable = false;
771
+ let fatalToolArgumentToolName = "";
772
+ const markFatalToolArgumentError = (error, recoverable, toolName) => {
768
773
  fatalToolArgumentError = error;
774
+ fatalToolArgumentRecoverable = recoverable;
775
+ fatalToolArgumentToolName = toolName;
769
776
  };
770
777
  const executionOptions = {
771
778
  signal: options.signal,
@@ -781,8 +788,24 @@ async function* agentLoop(messages, options) {
781
788
  messages.push({ role: "tool", content: executionResult.toolResults });
782
789
  const toolsAborted = executionResult.aborted;
783
790
  if (fatalToolArgumentError) {
784
- yield { type: "error", error: fatalToolArgumentError };
785
- break;
791
+ if (fatalToolArgumentRecoverable && !toolArgumentAutoContinueUsed) {
792
+ toolArgumentAutoContinueUsed = true;
793
+ for (const key of invalidToolArgumentCounts.keys()) {
794
+ if (key.startsWith(`${fatalToolArgumentToolName}:`))
795
+ invalidToolArgumentCounts.delete(key);
796
+ }
797
+ yield {
798
+ type: "retry",
799
+ reason: "tool_argument_glitch",
800
+ attempt: 1,
801
+ maxAttempts: 1,
802
+ delayMs: 0,
803
+ silent: false
804
+ };
805
+ } else {
806
+ yield { type: "error", error: fatalToolArgumentError };
807
+ break;
808
+ }
786
809
  }
787
810
  if (toolsAborted) break;
788
811
  if (options.getSteeringMessages) {
@@ -794,6 +817,9 @@ async function* agentLoop(messages, options) {
794
817
  }
795
818
  }
796
819
  }
820
+ if (turn >= maxTurns) {
821
+ hitMaxTurns = true;
822
+ }
797
823
  }
798
824
  } finally {
799
825
  sanitizeOrphanedServerTools(messages);
@@ -805,6 +831,19 @@ async function* agentLoop(messages, options) {
805
831
  break;
806
832
  }
807
833
  }
834
+ if (hitMaxTurns) {
835
+ diag("max_turns_reached", {
836
+ turn,
837
+ maxTurns,
838
+ provider: options.provider,
839
+ model: options.model
840
+ });
841
+ yield {
842
+ type: "max_turns",
843
+ totalTurns: turn,
844
+ maxTurns
845
+ };
846
+ }
808
847
  yield {
809
848
  type: "agent_done",
810
849
  totalTurns: turn,
@@ -867,10 +906,17 @@ async function executeSingleToolCall(toolCall, options, pushEvent) {
867
906
  resultContent = `Invalid arguments for tool \`${toolCall.name}\`:
868
907
  ` + prettyError + "\nRe-issue the call with each field as the correct type.";
869
908
  if (failureCount >= 3) {
909
+ const recoverable = Object.keys(toolCall.args ?? {}).length === 0;
870
910
  options.markFatalToolArgumentError(
871
- new Error(
872
- `The model repeatedly issued invalid arguments for tool \`${toolCall.name}\`. This is usually an upstream model/tool-calling bug. Your conversation is preserved; send another message or switch models to continue.`
873
- )
911
+ new GGAIError(
912
+ `The model repeatedly issued invalid arguments for tool \`${toolCall.name}\`. This is usually an upstream model/tool-calling bug` + (recoverable ? " (the provider's stream returned empty tool-call arguments)" : "") + `. Your conversation is preserved; send another message or switch models to continue.`,
913
+ {
914
+ source: "provider",
915
+ hint: "This is the model/provider's fault, not a ggcoder bug. " + (recoverable ? "ggcoder already retried automatically once; if it recurs, send another message or switch models." : "Send another message or switch models to continue.")
916
+ }
917
+ ),
918
+ recoverable,
919
+ toolCall.name
874
920
  );
875
921
  }
876
922
  } else {