@kenkaiiii/gg-agent 5.19.0 → 5.19.2

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.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, StopReason, Usage, AssistantMessage } from '@kenkaiiii/gg-ai';
1
+ import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, Usage, StopReason, AssistantMessage } from '@kenkaiiii/gg-ai';
2
2
  import { z } from 'zod';
3
3
 
4
4
  interface StructuredToolResult {
@@ -136,6 +136,14 @@ interface AgentFollowUpMessageEvent {
136
136
  content: Message["content"];
137
137
  }
138
138
  type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
139
+ interface TransformContextOptions {
140
+ /** Force a transform after the provider reports context overflow. */
141
+ force?: boolean;
142
+ /** Latest successful provider usage, anchored at its assistant message. */
143
+ usage?: Usage;
144
+ /** Messages appended after that usage sample and not yet seen by the provider. */
145
+ pendingMessages: Message[];
146
+ }
139
147
  interface AgentOptions {
140
148
  provider: StreamOptions["provider"];
141
149
  model: string;
@@ -180,6 +188,10 @@ interface AgentOptions {
180
188
  clearToolUses?: boolean;
181
189
  /** Max characters for a single tool result. Results exceeding this are truncated with a notice. */
182
190
  maxToolResultChars?: number;
191
+ /** Aggregate budget for ALL tool results in one assistant turn. Protects
192
+ * against parallel fan-outs injecting huge uncached context in one turn;
193
+ * the largest results are trimmed (water-filling) with a re-run notice. */
194
+ maxTurnToolResultChars?: number;
183
195
  /** Max consecutive pause_turn continuations before stopping (default: 5).
184
196
  * Prevents infinite loops when server-side tools keep pausing. */
185
197
  maxContinuations?: number;
@@ -188,12 +200,12 @@ interface AgentOptions {
188
200
  * the messages array (e.g. compaction, truncation). Return the same array
189
201
  * for no-op, or a new array to replace the conversation context.
190
202
  *
203
+ * The latest provider usage is authoritative for the history through its
204
+ * assistant response. `pendingMessages` contains context appended afterward.
191
205
  * When `options.force` is true, the caller should compact unconditionally
192
206
  * (e.g. after a context overflow error from the API).
193
207
  */
194
- transformContext?: (messages: Message[], options?: {
195
- force?: boolean;
196
- }) => Message[] | Promise<Message[]>;
208
+ transformContext?: (messages: Message[], options: TransformContextOptions) => Message[] | Promise<Message[]>;
197
209
  /**
198
210
  * Polled after tool execution completes each turn. Returns user messages
199
211
  * to inject into the conversation before the next LLM call (steering).
@@ -299,4 +311,4 @@ declare function isBillingError(err: unknown): boolean;
299
311
  declare function isUsageLimitError(err: unknown): boolean;
300
312
  declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
301
313
 
302
- export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
314
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, StopReason, Usage, AssistantMessage } from '@kenkaiiii/gg-ai';
1
+ import { StreamOptions, Message, Tool, ToolResultContent, ServerToolDefinition, Usage, StopReason, AssistantMessage } from '@kenkaiiii/gg-ai';
2
2
  import { z } from 'zod';
3
3
 
4
4
  interface StructuredToolResult {
@@ -136,6 +136,14 @@ interface AgentFollowUpMessageEvent {
136
136
  content: Message["content"];
137
137
  }
138
138
  type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentToolCallDeltaEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentSteeringMessageEvent | AgentFollowUpMessageEvent | AgentRetryEvent | AgentTurnEndEvent | AgentDoneEvent | AgentMaxTurnsEvent | AgentErrorEvent;
139
+ interface TransformContextOptions {
140
+ /** Force a transform after the provider reports context overflow. */
141
+ force?: boolean;
142
+ /** Latest successful provider usage, anchored at its assistant message. */
143
+ usage?: Usage;
144
+ /** Messages appended after that usage sample and not yet seen by the provider. */
145
+ pendingMessages: Message[];
146
+ }
139
147
  interface AgentOptions {
140
148
  provider: StreamOptions["provider"];
141
149
  model: string;
@@ -180,6 +188,10 @@ interface AgentOptions {
180
188
  clearToolUses?: boolean;
181
189
  /** Max characters for a single tool result. Results exceeding this are truncated with a notice. */
182
190
  maxToolResultChars?: number;
191
+ /** Aggregate budget for ALL tool results in one assistant turn. Protects
192
+ * against parallel fan-outs injecting huge uncached context in one turn;
193
+ * the largest results are trimmed (water-filling) with a re-run notice. */
194
+ maxTurnToolResultChars?: number;
183
195
  /** Max consecutive pause_turn continuations before stopping (default: 5).
184
196
  * Prevents infinite loops when server-side tools keep pausing. */
185
197
  maxContinuations?: number;
@@ -188,12 +200,12 @@ interface AgentOptions {
188
200
  * the messages array (e.g. compaction, truncation). Return the same array
189
201
  * for no-op, or a new array to replace the conversation context.
190
202
  *
203
+ * The latest provider usage is authoritative for the history through its
204
+ * assistant response. `pendingMessages` contains context appended afterward.
191
205
  * When `options.force` is true, the caller should compact unconditionally
192
206
  * (e.g. after a context overflow error from the API).
193
207
  */
194
- transformContext?: (messages: Message[], options?: {
195
- force?: boolean;
196
- }) => Message[] | Promise<Message[]>;
208
+ transformContext?: (messages: Message[], options: TransformContextOptions) => Message[] | Promise<Message[]>;
197
209
  /**
198
210
  * Polled after tool execution completes each turn. Returns user messages
199
211
  * to inject into the conversation before the next LLM call (steering).
@@ -299,4 +311,4 @@ declare function isBillingError(err: unknown): boolean;
299
311
  declare function isUsageLimitError(err: unknown): boolean;
300
312
  declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
301
313
 
302
- export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
314
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentFollowUpMessageEvent, type AgentOptions, type AgentResult, type AgentRetryEvent, type AgentServerToolCallEvent, type AgentServerToolResultEvent, type AgentSteeringMessageEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallDeltaEvent, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type AgentTurnTiming, type StreamDiagnosticFn, type StructuredToolResult, type ToolContext, type ToolExecuteResult, type ToolExecutionMode, type TransformContextOptions, agentLoop, isAbortError, isBillingError, isContextOverflow, isUsageLimitError, setStreamDiagnostic };
package/dist/index.js CHANGED
@@ -189,6 +189,8 @@ async function* agentLoop(messages, options) {
189
189
  const maxContinuations = options.maxContinuations ?? 5;
190
190
  let toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
191
191
  const totalUsage = { inputTokens: 0, outputTokens: 0 };
192
+ let latestProviderUsage;
193
+ let usageAnchorIndex;
192
194
  let turn = 0;
193
195
  let hitMaxTurns = false;
194
196
  let firstTurn = true;
@@ -265,7 +267,11 @@ async function* agentLoop(messages, options) {
265
267
  firstTurn = false;
266
268
  if (options.transformContext) {
267
269
  diag("transform_start");
268
- const transformed = await options.transformContext(messages);
270
+ const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
271
+ const transformed = await options.transformContext(messages, {
272
+ usage: latestProviderUsage,
273
+ pendingMessages
274
+ });
269
275
  if (transformed !== messages) {
270
276
  diag("transform_compacted", {
271
277
  before: messages.length,
@@ -273,6 +279,8 @@ async function* agentLoop(messages, options) {
273
279
  });
274
280
  messages.length = 0;
275
281
  messages.push(...transformed);
282
+ latestProviderUsage = void 0;
283
+ usageAnchorIndex = void 0;
276
284
  }
277
285
  diag("transform_end");
278
286
  }
@@ -527,10 +535,17 @@ async function* agentLoop(messages, options) {
527
535
  ...overflowDetails
528
536
  });
529
537
  try {
530
- const compacted = await options.transformContext(messages, { force: true });
538
+ const pendingMessages = usageAnchorIndex === void 0 ? [] : messages.slice(usageAnchorIndex + 1);
539
+ const compacted = await options.transformContext(messages, {
540
+ force: true,
541
+ usage: latestProviderUsage,
542
+ pendingMessages
543
+ });
531
544
  if (compacted !== messages && compacted.length < messages.length) {
532
545
  messages.length = 0;
533
546
  messages.push(...compacted);
547
+ latestProviderUsage = void 0;
548
+ usageAnchorIndex = void 0;
534
549
  diag("overflow_compact_success", {
535
550
  attempt: overflowCompactionAttempts,
536
551
  messages: messages.length,
@@ -736,6 +751,8 @@ async function* agentLoop(messages, options) {
736
751
  totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
737
752
  }
738
753
  messages.push(response.message);
754
+ latestProviderUsage = response.usage;
755
+ usageAnchorIndex = messages.length - 1;
739
756
  const completedAt = Date.now();
740
757
  const outputTokensPerSecond = providerDurationMs > 0 && response.usage.outputTokens > 0 ? response.usage.outputTokens / (providerDurationMs / 1e3) : void 0;
741
758
  const timing = {
@@ -823,6 +840,7 @@ async function* agentLoop(messages, options) {
823
840
  const executionOptions = {
824
841
  signal: options.signal,
825
842
  maxToolResultChars: options.maxToolResultChars,
843
+ maxTurnToolResultChars: options.maxTurnToolResultChars,
826
844
  toolMap,
827
845
  invalidToolArgumentCounts,
828
846
  markFatalToolArgumentError
@@ -1059,6 +1077,7 @@ async function* executeToolCallsMixed(toolCalls, initialToolResults, options) {
1059
1077
  }
1060
1078
  const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
1061
1079
  capToolResults(toolResults, options.maxToolResultChars);
1080
+ capTurnToolResults(toolResults, options.maxTurnToolResultChars);
1062
1081
  return { toolResults, aborted };
1063
1082
  }
1064
1083
  async function* executeToolCallsParallel(toolCalls, initialToolResults, options) {
@@ -1098,6 +1117,7 @@ async function* executeToolCallsParallel(toolCalls, initialToolResults, options)
1098
1117
  }
1099
1118
  const toolResults = buildToolResults(initialToolResults, toolCalls, resultsById);
1100
1119
  capToolResults(toolResults, options.maxToolResultChars);
1120
+ capTurnToolResults(toolResults, options.maxTurnToolResultChars);
1101
1121
  return { toolResults, aborted };
1102
1122
  }
1103
1123
  function buildToolResults(initialToolResults, toolCalls, resultsById) {
@@ -1140,6 +1160,34 @@ function capToolResults(toolResults, maxToolResultChars) {
1140
1160
  ` + tail;
1141
1161
  }
1142
1162
  }
1163
+ function capTurnToolResults(toolResults, maxTurnToolResultChars) {
1164
+ if (!maxTurnToolResultChars) return;
1165
+ const textResults = toolResults.filter(
1166
+ (toolResult) => typeof toolResult.content === "string"
1167
+ );
1168
+ const total = textResults.reduce((sum, toolResult) => sum + toolResult.content.length, 0);
1169
+ if (total <= maxTurnToolResultChars) return;
1170
+ const bySize = [...textResults].sort((a, b) => a.content.length - b.content.length);
1171
+ let remaining = maxTurnToolResultChars;
1172
+ let left = bySize.length;
1173
+ for (const toolResult of bySize) {
1174
+ const fairShare = Math.floor(remaining / left);
1175
+ left--;
1176
+ if (toolResult.content.length <= fairShare) {
1177
+ remaining -= toolResult.content.length;
1178
+ continue;
1179
+ }
1180
+ remaining -= fairShare;
1181
+ const headChars = Math.floor(fairShare * 0.7);
1182
+ const tailChars = fairShare - headChars;
1183
+ const omitted = toolResult.content.length - fairShare;
1184
+ toolResult.content = toolResult.content.slice(0, headChars) + `
1185
+
1186
+ [... ${omitted} characters trimmed: this turn's combined tool results exceeded the per-turn budget. Re-run this call alone with narrower filters or offset/limit if you need the omitted content ...]
1187
+
1188
+ ` + (tailChars > 0 ? toolResult.content.slice(-tailChars) : "");
1189
+ }
1190
+ }
1143
1191
  function normalizeToolResult(raw) {
1144
1192
  return typeof raw === "string" ? { content: raw } : raw;
1145
1193
  }