@compilr-dev/agents 0.3.0 → 0.3.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/agent.d.ts CHANGED
@@ -16,6 +16,7 @@ import { ContextManager } from './context/manager.js';
16
16
  import { FileAccessTracker } from './context/file-tracker.js';
17
17
  import { AnchorManager } from './anchors/manager.js';
18
18
  import { GuardrailManager } from './guardrails/manager.js';
19
+ import { type RetryConfig } from './utils/index.js';
19
20
  /**
20
21
  * Event types emitted during agent execution
21
22
  */
@@ -146,6 +147,18 @@ export type AgentEvent = {
146
147
  type: 'iteration_limit_extended';
147
148
  newMaxIterations: number;
148
149
  addedIterations: number;
150
+ } | {
151
+ type: 'llm_retry';
152
+ attempt: number;
153
+ maxAttempts: number;
154
+ error: string;
155
+ delayMs: number;
156
+ provider: string;
157
+ } | {
158
+ type: 'llm_retry_exhausted';
159
+ attempts: number;
160
+ error: string;
161
+ provider: string;
149
162
  };
150
163
  /**
151
164
  * Event handler function type
@@ -268,6 +281,29 @@ export interface AgentConfig {
268
281
  * Event handler for monitoring agent execution
269
282
  */
270
283
  onEvent?: AgentEventHandler;
284
+ /**
285
+ * Configuration for automatic retry on transient LLM errors.
286
+ * Enabled by default with sensible defaults (10 attempts, exponential backoff).
287
+ *
288
+ * Retryable errors include:
289
+ * - Rate limit (429)
290
+ * - Server errors (5xx)
291
+ * - Connection/network errors
292
+ * - Anthropic overloaded (529)
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * const agent = new Agent({
297
+ * provider,
298
+ * retry: {
299
+ * maxAttempts: 5, // Max 5 retries
300
+ * baseDelayMs: 2000, // Start with 2s delay
301
+ * maxDelayMs: 60000, // Cap at 60s
302
+ * }
303
+ * });
304
+ * ```
305
+ */
306
+ retry?: RetryConfig;
271
307
  /**
272
308
  * Checkpointer for persisting agent state.
273
309
  * If provided, enables checkpoint() and resume() functionality.
@@ -377,6 +413,15 @@ export interface AgentConfig {
377
413
  * ```
378
414
  */
379
415
  permissions?: PermissionManagerOptions;
416
+ /**
417
+ * Pre-existing PermissionManager instance.
418
+ *
419
+ * Use this to share a PermissionManager between agents (e.g., parent and sub-agents).
420
+ * When provided, takes precedence over `permissions` options.
421
+ *
422
+ * This is primarily used internally for sub-agent permission inheritance.
423
+ */
424
+ permissionManager?: PermissionManager;
380
425
  /**
381
426
  * Pre-loaded project memory to prepend to system prompt.
382
427
  *
@@ -649,6 +694,20 @@ export interface SubAgentConfig {
649
694
  * Default: false (uses shared store)
650
695
  */
651
696
  stateIsolation?: boolean;
697
+ /**
698
+ * Inherit parent's permission manager.
699
+ *
700
+ * When true (default), the sub-agent uses the parent's PermissionManager,
701
+ * sharing session grants and permission rules. This ensures:
702
+ * - Sub-agents follow the same permission rules as parent
703
+ * - Session grants from parent are available to sub-agents
704
+ * - User sees permission prompts for sub-agent tool usage
705
+ *
706
+ * Set to false to allow sub-agents to bypass permissions (use with caution).
707
+ *
708
+ * Default: true
709
+ */
710
+ inheritPermissions?: boolean;
652
711
  }
653
712
  /**
654
713
  * Result from a sub-agent execution
@@ -712,6 +771,7 @@ export declare class Agent {
712
771
  private readonly autoContextManagement;
713
772
  private readonly onEvent?;
714
773
  private readonly onIterationLimitReached?;
774
+ private readonly retryConfig;
715
775
  private readonly checkpointer?;
716
776
  private readonly _sessionId;
717
777
  private readonly autoCheckpoint;
@@ -1136,8 +1196,14 @@ export declare class Agent {
1136
1196
  /**
1137
1197
  * Set the conversation history (for manual compaction/restoration)
1138
1198
  * Also updates the context manager's token count if configured.
1199
+ *
1200
+ * @param messages - The message history to restore
1201
+ * @param options - Optional restore options
1202
+ * @param options.turnCount - The turn count to restore (important for compaction)
1139
1203
  */
1140
- setHistory(messages: Message[]): Promise<this>;
1204
+ setHistory(messages: Message[], options?: {
1205
+ turnCount?: number;
1206
+ }): Promise<this>;
1141
1207
  /**
1142
1208
  * Get the context manager (if configured)
1143
1209
  */
@@ -1454,9 +1520,24 @@ export declare class Agent {
1454
1520
  * Process stream chunks into text, tool uses, and usage data
1455
1521
  */
1456
1522
  private processChunks;
1523
+ /**
1524
+ * Wrap provider.chat() with automatic retry on transient errors.
1525
+ *
1526
+ * Retries the entire stream on failure with exponential backoff.
1527
+ * Emits llm_retry events before each retry attempt.
1528
+ *
1529
+ * @param messages - Messages to send
1530
+ * @param options - Chat options
1531
+ * @param emit - Event emitter function
1532
+ * @param signal - Optional abort signal
1533
+ */
1534
+ private chatWithRetry;
1457
1535
  /**
1458
1536
  * Generate a summary of messages using the LLM provider.
1459
1537
  * Used for context summarization when approaching limits.
1538
+ *
1539
+ * If anchors are configured, the summary will prioritize keeping
1540
+ * information related to anchor content (anchor-weighted summarization).
1460
1541
  */
1461
1542
  private generateSummary;
1462
1543
  /**
package/dist/agent.js CHANGED
@@ -11,10 +11,11 @@ import { FileAccessTracker } from './context/file-tracker.js';
11
11
  import { createFileTrackingHook } from './context/file-tracking-hook.js';
12
12
  import { AnchorManager } from './anchors/manager.js';
13
13
  import { GuardrailManager } from './guardrails/manager.js';
14
- import { MaxIterationsError, ToolLoopError } from './errors.js';
14
+ import { MaxIterationsError, ToolLoopError, ProviderError } from './errors.js';
15
15
  import { generateSessionId, createAgentState, deserializeTodos } from './state/agent-state.js';
16
16
  import { getDefaultTodoStore, createIsolatedTodoStore } from './tools/builtin/todo.js';
17
17
  import { repairToolPairing } from './messages/index.js';
18
+ import { DEFAULT_RETRY_CONFIG, withRetryGenerator } from './utils/index.js';
18
19
  /**
19
20
  * Agent class - orchestrates LLM interactions with tool use
20
21
  *
@@ -44,6 +45,8 @@ export class Agent {
44
45
  autoContextManagement;
45
46
  onEvent;
46
47
  onIterationLimitReached;
48
+ // Retry configuration
49
+ retryConfig;
47
50
  // State management
48
51
  checkpointer;
49
52
  _sessionId;
@@ -120,7 +123,12 @@ export class Agent {
120
123
  this.guardrailManager = new GuardrailManager(config.guardrails);
121
124
  }
122
125
  // Permission management
123
- if (config.permissions !== undefined) {
126
+ // Use pre-existing permissionManager if provided (for sub-agent inheritance)
127
+ // Otherwise create one from options
128
+ if (config.permissionManager !== undefined) {
129
+ this.permissionManager = config.permissionManager;
130
+ }
131
+ else if (config.permissions !== undefined) {
124
132
  this.permissionManager = new PermissionManager(config.permissions);
125
133
  }
126
134
  // Project memory - store and prepend to system prompt
@@ -183,6 +191,13 @@ export class Agent {
183
191
  // Hooks manager without file tracking
184
192
  this.hooksManager = new HooksManager({ hooks: config.hooks });
185
193
  }
194
+ // Retry configuration with defaults
195
+ this.retryConfig = {
196
+ enabled: config.retry?.enabled ?? DEFAULT_RETRY_CONFIG.enabled,
197
+ maxAttempts: config.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts,
198
+ baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
199
+ maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,
200
+ };
186
201
  }
187
202
  // ==========================================================================
188
203
  // Static Factory Methods
@@ -710,11 +725,28 @@ export class Agent {
710
725
  /**
711
726
  * Set the conversation history (for manual compaction/restoration)
712
727
  * Also updates the context manager's token count if configured.
728
+ *
729
+ * @param messages - The message history to restore
730
+ * @param options - Optional restore options
731
+ * @param options.turnCount - The turn count to restore (important for compaction)
713
732
  */
714
- async setHistory(messages) {
715
- this.conversationHistory = [...messages];
733
+ async setHistory(messages, options) {
734
+ // Repair tool_use/tool_result pairing to fix any corrupted messages
735
+ // This is especially important after resuming from a saved session
736
+ const repairedMessages = repairToolPairing(messages);
737
+ this.conversationHistory = [...repairedMessages];
716
738
  if (this.contextManager) {
717
- await this.contextManager.updateTokenCount(messages);
739
+ await this.contextManager.updateTokenCount(repairedMessages);
740
+ // Restore turn count if provided
741
+ if (options?.turnCount !== undefined) {
742
+ this.contextManager.setTurnCount(options.turnCount);
743
+ }
744
+ else {
745
+ // Infer turn count from messages as a fallback
746
+ // Count user messages as a proxy for turns
747
+ const inferredTurnCount = repairedMessages.filter((m) => m.role === 'user').length;
748
+ this.contextManager.setTurnCount(inferredTurnCount);
749
+ }
718
750
  }
719
751
  return this;
720
752
  }
@@ -926,6 +958,7 @@ export class Agent {
926
958
  messages: this.conversationHistory,
927
959
  todos: todoStore.getAll(),
928
960
  currentIteration: this._currentIteration,
961
+ turnCount: this.contextManager?.getTurnCount() ?? 0,
929
962
  totalTokensUsed: this._totalTokensUsed,
930
963
  createdAt: this._createdAt,
931
964
  });
@@ -1054,6 +1087,11 @@ export class Agent {
1054
1087
  agent._createdAt = state.createdAt;
1055
1088
  agent._totalTokensUsed = state.totalTokensUsed;
1056
1089
  agent._currentIteration = state.currentIteration;
1090
+ // Restore turn count to context manager if configured
1091
+ // Note: turnCount is guaranteed to exist - deserializer ensures backward compatibility
1092
+ if (agent.contextManager) {
1093
+ agent.contextManager.setTurnCount(state.turnCount);
1094
+ }
1057
1095
  // Restore todos
1058
1096
  if (state.todos.length > 0) {
1059
1097
  const todoStore = getDefaultTodoStore();
@@ -1118,6 +1156,9 @@ export class Agent {
1118
1156
  for (const tool of toolsToRegister) {
1119
1157
  subAgentToolRegistry.register(tool);
1120
1158
  }
1159
+ // Determine if sub-agent should inherit parent's permissions
1160
+ // Default: true (sub-agents follow same permission rules as parent)
1161
+ const inheritPermissions = config.inheritPermissions ?? true;
1121
1162
  // Create the sub-agent
1122
1163
  const subAgent = new Agent({
1123
1164
  provider: this.provider,
@@ -1128,6 +1169,12 @@ export class Agent {
1128
1169
  contextManager: subAgentContextManager,
1129
1170
  autoContextManagement: true,
1130
1171
  onEvent: this.onEvent, // Forward events to parent
1172
+ // Sub-agents should summarize their findings when hitting iteration limit
1173
+ // rather than throwing an error with no response
1174
+ iterationLimitBehavior: 'summarize',
1175
+ // Inherit parent's permission manager if enabled
1176
+ // This shares session grants and permission rules with sub-agents
1177
+ permissionManager: inheritPermissions ? this.permissionManager : undefined,
1131
1178
  });
1132
1179
  // Store the sub-agent
1133
1180
  this.subAgents.set(config.name, { config, agent: subAgent });
@@ -1390,22 +1437,34 @@ export class Agent {
1390
1437
  // Build messages: system prompt + history + new user message
1391
1438
  let messages = [];
1392
1439
  // Add system message if present
1393
- if (this.systemPrompt) {
1394
- messages.push({
1395
- role: 'system',
1396
- content: this.systemPrompt,
1397
- });
1398
- }
1399
- // Inject anchors (critical information that survives context compaction)
1440
+ // NOTE: Anchors are now appended to the system prompt (not a separate message)
1441
+ // to avoid multiple system messages which can confuse Claude's role identity
1442
+ let systemContent = this.systemPrompt;
1443
+ // Inject anchors into the system prompt (not as separate message)
1400
1444
  if (this.anchorManager && this.anchorManager.size > 0) {
1401
1445
  const anchorsContent = this.anchorManager.format();
1402
1446
  if (anchorsContent) {
1403
- messages.push({
1404
- role: 'system',
1405
- content: `## Active Anchors (Critical Information)\n\n${anchorsContent}`,
1406
- });
1447
+ // Insert anchors BEFORE the role ending (if present) so role reinforcement stays at the end
1448
+ const roleEndingMarker = '## YOUR ASSIGNED ROLE:';
1449
+ const roleEndingIndex = systemContent.indexOf(roleEndingMarker);
1450
+ if (roleEndingIndex > 0) {
1451
+ // Insert anchors before the role ending
1452
+ const beforeRole = systemContent.substring(0, roleEndingIndex);
1453
+ const roleEnding = systemContent.substring(roleEndingIndex);
1454
+ systemContent = `${beforeRole}\n\n---\n\n## Active Anchors (Critical Information)\n\n${anchorsContent}\n\n---\n\n${roleEnding}`;
1455
+ }
1456
+ else {
1457
+ // No role ending, just append anchors
1458
+ systemContent = `${systemContent}\n\n---\n\n## Active Anchors (Critical Information)\n\n${anchorsContent}`;
1459
+ }
1407
1460
  }
1408
1461
  }
1462
+ if (systemContent) {
1463
+ messages.push({
1464
+ role: 'system',
1465
+ content: systemContent,
1466
+ });
1467
+ }
1409
1468
  // Add conversation history
1410
1469
  messages.push(...this.conversationHistory);
1411
1470
  // Add new user message
@@ -1514,10 +1573,10 @@ export class Agent {
1514
1573
  const llmStartTime = Date.now();
1515
1574
  const chunks = [];
1516
1575
  try {
1517
- for await (const chunk of this.provider.chat(messages, {
1576
+ for await (const chunk of this.chatWithRetry(messages, {
1518
1577
  ...chatOptions,
1519
1578
  tools: tools.length > 0 ? tools : undefined,
1520
- })) {
1579
+ }, emit, signal)) {
1521
1580
  // Check for abort during streaming
1522
1581
  if (signal?.aborted) {
1523
1582
  aborted = true;
@@ -1615,6 +1674,7 @@ export class Agent {
1615
1674
  id: tu.id,
1616
1675
  name: tu.name,
1617
1676
  input: tu.input,
1677
+ signature: tu.signature, // Gemini 3 thought signature (required for multi-turn)
1618
1678
  })),
1619
1679
  ],
1620
1680
  };
@@ -2111,6 +2171,7 @@ export class Agent {
2111
2171
  id: chunk.toolUse.id,
2112
2172
  name: chunk.toolUse.name,
2113
2173
  inputJson: '',
2174
+ signature: chunk.toolUse.signature, // Capture Gemini 3 thought signature
2114
2175
  };
2115
2176
  }
2116
2177
  break;
@@ -2129,6 +2190,7 @@ export class Agent {
2129
2190
  id: currentToolUse.id,
2130
2191
  name: currentToolUse.name,
2131
2192
  input,
2193
+ signature: currentToolUse.signature, // Pass through the signature
2132
2194
  });
2133
2195
  }
2134
2196
  catch {
@@ -2146,27 +2208,100 @@ export class Agent {
2146
2208
  }
2147
2209
  return { text, toolUses, usage, model };
2148
2210
  }
2211
+ /**
2212
+ * Wrap provider.chat() with automatic retry on transient errors.
2213
+ *
2214
+ * Retries the entire stream on failure with exponential backoff.
2215
+ * Emits llm_retry events before each retry attempt.
2216
+ *
2217
+ * @param messages - Messages to send
2218
+ * @param options - Chat options
2219
+ * @param emit - Event emitter function
2220
+ * @param signal - Optional abort signal
2221
+ */
2222
+ chatWithRetry(messages, options, emit, signal) {
2223
+ // If retry is disabled, return the raw provider stream
2224
+ if (!this.retryConfig.enabled) {
2225
+ return this.provider.chat(messages, options);
2226
+ }
2227
+ const providerName = this.provider.name;
2228
+ const { maxAttempts, baseDelayMs, maxDelayMs } = this.retryConfig;
2229
+ return withRetryGenerator(() => this.provider.chat(messages, options), {
2230
+ maxAttempts,
2231
+ baseDelayMs,
2232
+ maxDelayMs,
2233
+ isRetryable: (error) => {
2234
+ // Use the ProviderError's built-in retryable check
2235
+ return error instanceof ProviderError && error.isRetryable();
2236
+ },
2237
+ onRetry: (attempt, max, error, delayMs) => {
2238
+ emit({
2239
+ type: 'llm_retry',
2240
+ attempt,
2241
+ maxAttempts: max,
2242
+ error: error.message,
2243
+ delayMs,
2244
+ provider: providerName,
2245
+ });
2246
+ },
2247
+ onExhausted: (attempts, error) => {
2248
+ emit({
2249
+ type: 'llm_retry_exhausted',
2250
+ attempts,
2251
+ error: error.message,
2252
+ provider: providerName,
2253
+ });
2254
+ },
2255
+ signal,
2256
+ });
2257
+ }
2149
2258
  /**
2150
2259
  * Generate a summary of messages using the LLM provider.
2151
2260
  * Used for context summarization when approaching limits.
2261
+ *
2262
+ * If anchors are configured, the summary will prioritize keeping
2263
+ * information related to anchor content (anchor-weighted summarization).
2152
2264
  */
2153
2265
  async generateSummary(messages) {
2266
+ // Build anchor context if available (for weighted summarization)
2267
+ let anchorContext = '';
2268
+ if (this.anchorManager && this.anchorManager.size > 0) {
2269
+ const anchors = this.anchorManager.getAll();
2270
+ if (anchors.length > 0) {
2271
+ const anchorItems = anchors.map((a) => `- [${a.priority}] ${a.content}`).join('\n');
2272
+ anchorContext = `
2273
+ ## PRIORITY CONTEXT (Anchors)
2274
+
2275
+ The following are critical anchors that should be preserved in context. When summarizing,
2276
+ PRIORITIZE keeping information that relates to these anchors:
2277
+
2278
+ ${anchorItems}
2279
+
2280
+ Ensure the summary captures any conversation details related to these anchors.
2281
+ `;
2282
+ }
2283
+ }
2154
2284
  const summaryPrompt = `Please provide a concise summary of the following conversation. Focus on:
2155
2285
  1. Key decisions made
2156
2286
  2. Important context established
2157
2287
  3. Tasks completed or in progress
2158
2288
  4. Any relevant file paths or code snippets mentioned
2159
-
2289
+ ${anchorContext ? '5. Information related to the priority anchors listed below' : ''}
2290
+ ${anchorContext}
2160
2291
  Keep the summary under 500 words.
2161
2292
 
2162
2293
  Conversation to summarize:
2163
2294
  ${messages.map((m) => `${m.role}: ${typeof m.content === 'string' ? m.content : JSON.stringify(m.content)}`).join('\n\n')}`;
2164
2295
  const summaryMessages = [{ role: 'user', content: summaryPrompt }];
2296
+ // Create emit function for retry events (uses main event handler)
2297
+ const emit = (event) => {
2298
+ this.onEvent?.(event);
2299
+ };
2165
2300
  let summary = '';
2166
- for await (const chunk of this.provider.chat(summaryMessages, {
2301
+ for await (const chunk of this.chatWithRetry(summaryMessages, {
2167
2302
  ...this.chatOptions,
2168
2303
  maxTokens: this.contextManager?.getConfig().summarization.summaryMaxTokens ?? 2000,
2169
- })) {
2304
+ }, emit)) {
2170
2305
  if (chunk.type === 'text' && chunk.text) {
2171
2306
  summary += chunk.text;
2172
2307
  }
@@ -2205,11 +2340,15 @@ ${messages
2205
2340
  .map((m) => `${m.role}: ${typeof m.content === 'string' ? m.content.slice(0, 500) : '[complex content]'}`)
2206
2341
  .join('\n\n')}`;
2207
2342
  const summaryMessages = [{ role: 'user', content: summaryPrompt }];
2343
+ // Create emit function for retry events (uses main event handler)
2344
+ const emit = (event) => {
2345
+ this.onEvent?.(event);
2346
+ };
2208
2347
  let summary = '';
2209
- for await (const chunk of this.provider.chat(summaryMessages, {
2348
+ for await (const chunk of this.chatWithRetry(summaryMessages, {
2210
2349
  ...this.chatOptions,
2211
2350
  maxTokens: 1000, // Keep summary concise
2212
- })) {
2351
+ }, emit)) {
2213
2352
  if (chunk.type === 'text' && chunk.text) {
2214
2353
  summary += chunk.text;
2215
2354
  }
@@ -98,6 +98,14 @@ export declare class ContextManager {
98
98
  * Increment turn count (call after each assistant response)
99
99
  */
100
100
  incrementTurn(): void;
101
+ /**
102
+ * Get the current turn count
103
+ */
104
+ getTurnCount(): number;
105
+ /**
106
+ * Set the turn count (for restoring from saved state)
107
+ */
108
+ setTurnCount(count: number): void;
101
109
  /**
102
110
  * Check if compaction is needed
103
111
  */
@@ -17,6 +17,7 @@
17
17
  * }
18
18
  * ```
19
19
  */
20
+ import { repairToolPairing } from '../messages/index.js';
20
21
  /**
21
22
  * Default budget allocation
22
23
  */
@@ -176,6 +177,23 @@ export class ContextManager {
176
177
  incrementTurn() {
177
178
  this.turnCount++;
178
179
  }
180
+ /**
181
+ * Get the current turn count
182
+ */
183
+ getTurnCount() {
184
+ return this.turnCount;
185
+ }
186
+ /**
187
+ * Set the turn count (for restoring from saved state)
188
+ */
189
+ setTurnCount(count) {
190
+ this.turnCount = count;
191
+ // If we're restoring state, assume last compaction was at turn 0
192
+ // to prevent immediate compaction after restore
193
+ if (this.lastCompactionTurn === 0 && count > 0) {
194
+ this.lastCompactionTurn = count;
195
+ }
196
+ }
179
197
  /**
180
198
  * Check if compaction is needed
181
199
  */
@@ -588,12 +606,15 @@ export class ContextManager {
588
606
  }
589
607
  // Reconstruct messages in original order
590
608
  // System messages come first, then summary (if any), then tool results, then recent
591
- const finalMessages = [
609
+ let finalMessages = [
592
610
  ...categorized.system,
593
611
  ...compactedHistory,
594
612
  ...compactedToolResults,
595
613
  ...categorized.recentMessages,
596
614
  ];
615
+ // Repair tool_use/tool_result pairing after compaction
616
+ // This fixes orphaned blocks that cause API errors
617
+ finalMessages = repairToolPairing(finalMessages);
597
618
  // If history was summarized, add assistant acknowledgment after summary
598
619
  if (categoryStats.history.action === 'summarized' && compactedHistory.length > 0) {
599
620
  // Insert assistant acknowledgment after the summary message
@@ -894,7 +915,9 @@ export class ContextManager {
894
915
  compactedOld.push({ ...msg, content: compactedBlocks });
895
916
  }
896
917
  }
897
- const compactedMessages = [...compactedOld, ...recentMessages];
918
+ // Repair tool_use/tool_result pairing after compaction
919
+ // This fixes orphaned blocks that cause API errors
920
+ const compactedMessages = repairToolPairing([...compactedOld, ...recentMessages]);
898
921
  const tokensAfter = await this.countTokens(compactedMessages);
899
922
  this.compactionCount++;
900
923
  this.lastCompactionTurn = this.turnCount;
package/dist/errors.d.ts CHANGED
@@ -43,7 +43,26 @@ export declare class ProviderError extends AgentError {
43
43
  */
44
44
  isServerError(): boolean;
45
45
  /**
46
- * Check if this error is retryable
46
+ * Check if this is a connection/network error.
47
+ * These errors typically have no status code but are retryable.
48
+ */
49
+ isConnectionError(): boolean;
50
+ /**
51
+ * Check if this is an Anthropic overloaded error (529)
52
+ */
53
+ isOverloadedError(): boolean;
54
+ /**
55
+ * Check if this error is retryable.
56
+ * Retryable errors include:
57
+ * - Rate limit (429)
58
+ * - Server errors (5xx)
59
+ * - Connection/network errors
60
+ * - Anthropic overloaded (529)
61
+ *
62
+ * Non-retryable errors include:
63
+ * - Authentication errors (401, 403)
64
+ * - Bad request (400)
65
+ * - Not found (404)
47
66
  */
48
67
  isRetryable(): boolean;
49
68
  }
package/dist/errors.js CHANGED
@@ -62,10 +62,52 @@ export class ProviderError extends AgentError {
62
62
  return this.statusCode !== undefined && this.statusCode >= 500 && this.statusCode < 600;
63
63
  }
64
64
  /**
65
- * Check if this error is retryable
65
+ * Check if this is a connection/network error.
66
+ * These errors typically have no status code but are retryable.
67
+ */
68
+ isConnectionError() {
69
+ if (this.statusCode !== undefined) {
70
+ return false;
71
+ }
72
+ const msg = this.message.toLowerCase();
73
+ return (msg.includes('connection') ||
74
+ msg.includes('econnrefused') ||
75
+ msg.includes('econnreset') ||
76
+ msg.includes('etimedout') ||
77
+ msg.includes('enotfound') ||
78
+ msg.includes('network') ||
79
+ msg.includes('socket') ||
80
+ msg.includes('dns'));
81
+ }
82
+ /**
83
+ * Check if this is an Anthropic overloaded error (529)
84
+ */
85
+ isOverloadedError() {
86
+ return this.statusCode === 529;
87
+ }
88
+ /**
89
+ * Check if this error is retryable.
90
+ * Retryable errors include:
91
+ * - Rate limit (429)
92
+ * - Server errors (5xx)
93
+ * - Connection/network errors
94
+ * - Anthropic overloaded (529)
95
+ *
96
+ * Non-retryable errors include:
97
+ * - Authentication errors (401, 403)
98
+ * - Bad request (400)
99
+ * - Not found (404)
66
100
  */
67
101
  isRetryable() {
68
- return this.isRateLimitError() || this.isServerError();
102
+ // Never retry auth errors
103
+ if (this.isAuthError()) {
104
+ return false;
105
+ }
106
+ // Retry transient errors
107
+ return (this.isRateLimitError() ||
108
+ this.isServerError() ||
109
+ this.isConnectionError() ||
110
+ this.isOverloadedError());
69
111
  }
70
112
  }
71
113
  /**
package/dist/index.d.ts CHANGED
@@ -15,6 +15,10 @@ export { OpenAIProvider, createOpenAIProvider } from './providers/index.js';
15
15
  export type { OpenAIProviderConfig } from './providers/index.js';
16
16
  export { GeminiProvider, createGeminiProvider } from './providers/index.js';
17
17
  export type { GeminiProviderConfig } from './providers/index.js';
18
+ export { GeminiNativeProvider, createGeminiNativeProvider } from './providers/index.js';
19
+ export type { GeminiNativeProviderConfig } from './providers/index.js';
20
+ export { GeminiLegacyProvider, createGeminiLegacyProvider } from './providers/index.js';
21
+ export type { GeminiLegacyProviderConfig } from './providers/index.js';
18
22
  export { OpenAICompatibleProvider } from './providers/index.js';
19
23
  export type { OpenAICompatibleConfig } from './providers/index.js';
20
24
  export type { Tool, ToolHandler, ToolRegistry, ToolInputSchema, ToolExecutionResult, ToolRegistryOptions, DefineToolOptions, ReadFileInput, WriteFileInput, BashInput, BashResult, FifoDetectionResult, GrepInput, GlobInput, EditInput, TodoWriteInput, TodoReadInput, TodoItem, TodoStatus, TodoContextCleanupOptions, TaskInput, TaskResult, AgentTypeConfig, TaskToolOptions, ContextMode, ThoroughnessLevel, SubAgentEventInfo, SuggestInput, SuggestToolOptions, } from './tools/index.js';
@@ -22,7 +26,8 @@ export { defineTool, createSuccessResult, createErrorResult, wrapToolExecute, De
22
26
  export { readFileTool, createReadFileTool, writeFileTool, createWriteFileTool, bashTool, createBashTool, execStream, detectFifoUsage, bashOutputTool, createBashOutputTool, killShellTool, createKillShellTool, ShellManager, getDefaultShellManager, setDefaultShellManager, grepTool, createGrepTool, globTool, createGlobTool, editTool, createEditTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, resetDefaultTodoStore, getDefaultTodoStore, createIsolatedTodoStore, cleanupTodoContextMessages, getTodoContextStats, webFetchTool, createWebFetchTool, createTaskTool, defaultAgentTypes, suggestTool, createSuggestTool, builtinTools, allBuiltinTools, TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
23
27
  export { userMessage, assistantMessage, systemMessage, textBlock, toolUseBlock, toolResultBlock, getTextContent, getToolUses, getToolResults, hasToolUses, validateToolUseResultPairing, repairToolPairing, ensureMessageContent, normalizeMessages, } from './messages/index.js';
24
28
  export type { ToolPairingValidation } from './messages/index.js';
25
- export { generateId, sleep, retry, truncate } from './utils/index.js';
29
+ export { generateId, sleep, retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG, } from './utils/index.js';
30
+ export type { RetryConfig as LLMRetryConfig, WithRetryOptions } from './utils/index.js';
26
31
  export { AgentError, ProviderError, ToolError, ToolTimeoutError, ToolLoopError, ValidationError, MaxIterationsError, AbortError, ContextOverflowError, isAgentError, isProviderError, isToolError, isToolTimeoutError, isToolLoopError, isContextOverflowError, wrapError, } from './errors.js';
27
32
  export { ContextManager, DEFAULT_CONTEXT_CONFIG, FileAccessTracker, createFileTrackingHook, TRACKED_TOOLS, } from './context/index.js';
28
33
  export type { ContextManagerOptions, ContextCategory, BudgetAllocation, CategoryBudgetInfo, PreflightResult, VerbosityLevel, VerbosityConfig, ContextConfig, FilteringConfig, CompactionConfig, SummarizationConfig, CompactionResult, SummarizationResult, FilteringResult, ContextEvent, ContextEventHandler, ContextStats, FileAccessType, FileAccess, FileAccessTrackerOptions, FormatHintsOptions, FileAccessStats, } from './context/index.js';
package/dist/index.js CHANGED
@@ -11,6 +11,10 @@ export { ClaudeProvider, createClaudeProvider } from './providers/index.js';
11
11
  export { OllamaProvider, createOllamaProvider } from './providers/index.js';
12
12
  export { OpenAIProvider, createOpenAIProvider } from './providers/index.js';
13
13
  export { GeminiProvider, createGeminiProvider } from './providers/index.js';
14
+ // Also export native Gemini provider explicitly for clarity
15
+ export { GeminiNativeProvider, createGeminiNativeProvider } from './providers/index.js';
16
+ // Legacy Gemini provider (OpenAI-compatible endpoint)
17
+ export { GeminiLegacyProvider, createGeminiLegacyProvider } from './providers/index.js';
14
18
  export { OpenAICompatibleProvider } from './providers/index.js';
15
19
  // Tool utilities
16
20
  export { defineTool, createSuccessResult, createErrorResult, wrapToolExecute, DefaultToolRegistry, createToolRegistry, } from './tools/index.js';
@@ -25,7 +29,9 @@ TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
25
29
  // Message utilities
26
30
  export { userMessage, assistantMessage, systemMessage, textBlock, toolUseBlock, toolResultBlock, getTextContent, getToolUses, getToolResults, hasToolUses, validateToolUseResultPairing, repairToolPairing, ensureMessageContent, normalizeMessages, } from './messages/index.js';
27
31
  // Utilities
28
- export { generateId, sleep, retry, truncate } from './utils/index.js';
32
+ export { generateId, sleep,
33
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- Kept for backward compatibility
34
+ retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG, } from './utils/index.js';
29
35
  // Errors
30
36
  export { AgentError, ProviderError, ToolError, ToolTimeoutError, ToolLoopError, ValidationError, MaxIterationsError, AbortError, ContextOverflowError, isAgentError, isProviderError, isToolError, isToolTimeoutError, isToolLoopError, isContextOverflowError, wrapError, } from './errors.js';
31
37
  // Context management