@nuvin/nuvin-core 1.6.0 → 1.6.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/VERSION CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "1.6.0",
3
- "commit": "ec1e31a"
2
+ "version": "1.6.1",
3
+ "commit": "3d1a9e0"
4
4
  }
package/dist/index.d.ts CHANGED
@@ -540,7 +540,6 @@ type SendMessageOptions = {
540
540
  conversationId?: string;
541
541
  stream?: boolean;
542
542
  signal?: AbortSignal;
543
- retry?: boolean;
544
543
  };
545
544
  type UserAttachment = ImageContentPart & {
546
545
  token?: string;
@@ -1994,6 +1993,38 @@ declare class GithubAuthTransport implements HttpTransport {
1994
1993
  get(url: string, headers?: HttpHeaders, signal?: AbortSignal): Promise<TransportResponse>;
1995
1994
  }
1996
1995
 
1996
+ interface RetryConfig {
1997
+ maxRetries: number;
1998
+ baseDelayMs: number;
1999
+ maxDelayMs: number;
2000
+ backoffMultiplier: number;
2001
+ jitterFactor: number;
2002
+ retryableStatusCodes: number[];
2003
+ onRetry?: (attempt: number, error: Error, delayMs: number) => void;
2004
+ onExhausted?: (error: Error, attempts: number) => void;
2005
+ }
2006
+ declare const DEFAULT_RETRY_CONFIG: RetryConfig;
2007
+ declare class AbortError extends Error {
2008
+ constructor(message?: string);
2009
+ }
2010
+ declare class RetryTransport implements HttpTransport {
2011
+ private inner;
2012
+ private config;
2013
+ constructor(inner: HttpTransport, config?: Partial<RetryConfig>);
2014
+ get(url: string, headers?: HttpHeaders, signal?: AbortSignal): Promise<TransportResponse>;
2015
+ post(url: string, body: unknown, headers?: HttpHeaders, signal?: AbortSignal): Promise<Response>;
2016
+ private executeWithRetry;
2017
+ private isRetryableResponse;
2018
+ private getDelayForResponse;
2019
+ }
2020
+
2021
+ declare function isRetryableStatusCode(status: number, retryableStatusCodes?: number[]): boolean;
2022
+ declare function isRetryableError(error: unknown, retryableStatusCodes?: number[]): boolean;
2023
+
2024
+ interface BaseLLMOptions {
2025
+ enablePromptCaching?: boolean;
2026
+ retry?: Partial<RetryConfig>;
2027
+ }
1997
2028
  declare class LLMError extends Error {
1998
2029
  readonly statusCode?: number | undefined;
1999
2030
  readonly isRetryable: boolean;
@@ -2003,9 +2034,8 @@ declare abstract class BaseLLM implements LLMPort {
2003
2034
  protected transport: HttpTransport | null;
2004
2035
  protected apiUrl: string;
2005
2036
  protected enablePromptCaching: boolean;
2006
- constructor(apiUrl: string, options?: {
2007
- enablePromptCaching?: boolean;
2008
- });
2037
+ protected retryConfig?: Partial<RetryConfig>;
2038
+ constructor(apiUrl: string, options?: BaseLLMOptions);
2009
2039
  protected abstract createTransport(): HttpTransport;
2010
2040
  protected transformUsage(rawUsage: unknown): UsageData | undefined;
2011
2041
  protected getTransport(): HttpTransport;
@@ -2038,11 +2068,12 @@ type GithubOptions = {
2038
2068
  accessToken?: string;
2039
2069
  apiUrl?: string;
2040
2070
  httpLogFile?: string;
2071
+ retry?: Partial<RetryConfig>;
2041
2072
  };
2042
2073
  declare class GithubLLM extends BaseLLM implements LLMPort {
2043
2074
  private readonly opts;
2044
2075
  constructor(opts?: GithubOptions);
2045
- protected createTransport(): GithubAuthTransport;
2076
+ protected createTransport(): GithubAuthTransport | RetryTransport;
2046
2077
  protected transformUsage(rawUsage: unknown): UsageData | undefined;
2047
2078
  getModels(signal?: AbortSignal): Promise<ModelInfo[]>;
2048
2079
  private handleError;
@@ -2109,6 +2140,7 @@ interface LLMOptions {
2109
2140
  includeUsage?: boolean;
2110
2141
  version?: string;
2111
2142
  providerName?: string;
2143
+ retry?: Partial<RetryConfig>;
2112
2144
  }
2113
2145
  interface CustomProviderDefinition {
2114
2146
  type?: 'openai-compat' | 'anthropic';
@@ -2211,4 +2243,4 @@ declare function resolveBackspaces(s: string): string;
2211
2243
  declare function stripAnsiAndControls(s: string): string;
2212
2244
  declare function canonicalizeTerminalPaste(raw: string): string;
2213
2245
 
2214
- export { AGENT_CREATOR_SYSTEM_PROMPT, type AgentAwareToolPort, type AgentCatalog, type AgentConfig, type AgentEvent, AgentEventTypes, AgentFilePersistence, AgentManager, AgentManagerCommandRunner, AgentOrchestrator, AgentRegistry, type AgentTemplate, AnthropicAISDKLLM, type AssignErrorResult, type AssignParams, type AssignResult, type AssignSuccessResult$1 as AssignSuccessResult, type AssignTaskArgs, type AssignTaskMetadata, type BashErrorResult, type BashParams, type BashResult, type BashSuccessResult$1 as BashSuccessResult, BashTool, type BashToolArgs, type BashToolMetadata, type CommandMetadata, type CompleteAgent, CompositeToolPort, type Conversation, ConversationContext, type ConversationMetadata, type ConversationSnapshot, ConversationStore, CoreMCPClient, DefaultDelegationPolicy, DefaultDelegationResultFormatter, DefaultDelegationService, DefaultSpecialistAgentFactory, type DelegationMetadata, type DelegationService, type DelegationServiceConfig, DelegationServiceFactory, type DirEntry, type DirLsArgs, type DirLsMetadata, type DirLsParams, type DirLsResult, type DirLsSuccessResult$1 as DirLsSuccessResult, type ErrorMetadata, ErrorReason, type ExecResult, type ExecResultError, type ExecResultSuccess, type FileEditArgs, type FileEditMetadata, type FileEditResult, type FileEditSuccessResult$1 as FileEditSuccessResult, type FileMetadata, type FileNewArgs, type FileNewMetadata, type FileNewParams, type FileNewResult, type FileNewSuccessResult$1 as FileNewSuccessResult, type FileReadArgs, type FileReadErrorResult, type FileReadMetadata, type FileReadParams, type FileReadResult, type FileReadSuccessResult$1 as FileReadSuccessResult, type FolderTreeOptions, type FunctionTool, GithubLLM, InMemoryMemory, InMemoryMetadata, InMemoryMetricsPort, JsonFileMemoryPersistence, type LLMConfig, LLMError, type LLMFactory, type LLMOptions, type LLMPort, LLMResolver, type LineRangeMetadata, type MCPConfig, type MCPServerConfig, MCPToolPort, type MemoryPort, MemoryPortMetadataAdapter, type Message, type MessageContent, type MessageContentPart, type MetadataPort, type MetricsChangeHandler, type MetricsPort, type MetricsSnapshot, type ModelInfo, type ModelLimits, NoopMetricsPort, NoopReminders, type OrchestratorAwareToolPort, type ParseResult, PersistedMemory, PersistingConsoleEventPort, RuntimeEnv, type SendMessageOptions, SimpleContextBuilder, SimpleCost, SimpleId, type SpecialistAgentConfig, type SpecialistAgentResult, type SubAgentState, type SubAgentToolCall, SystemClock, type TodoWriteArgs, type TodoWriteMetadata, type TodoWriteResult, type TodoWriteSuccessResult$1 as TodoWriteSuccessResult, type ToolApprovalDecision, type ToolArguments, type ToolCall, type ToolCallValidation, type ToolErrorMetadata, type ToolExecutionContext, type ToolExecutionResult, type ToolMetadataMap, type ToolName, type ToolParameterMap, type ToolPort, ToolRegistry, type ToolValidator, type TypedToolInvocation, type UsageData, type UserAttachment, type UserMessagePayload, type ValidationResult, type WebFetchArgs, type WebFetchMetadata, type WebFetchParams, type WebFetchResult, type WebFetchSuccessResult$1 as WebFetchSuccessResult, type WebSearchArgs, type WebSearchMetadata, type WebSearchParams, type WebSearchResult, type WebSearchSuccessResult$1 as WebSearchSuccessResult, type WebSearchToolResult, buildAgentCreationPrompt, buildInjectedSystem, canonicalizeTerminalPaste, convertToolCall, convertToolCalls, createEmptySnapshot, createLLM, err, generateFolderTree, getAvailableProviders, getFallbackLimits, getProviderLabel, isAssignResult, isAssignSuccess, isAssignTaskArgs, isBashResult, isBashSuccess, isBashToolArgs, isDirLsArgs, isDirLsResult, isDirLsSuccess, isError, isFileEditArgs, isFileEditResult, isFileEditSuccess, isFileNewArgs, isFileNewResult, isFileNewSuccess, isFileReadArgs, isFileReadResult, isFileReadSuccess, isJsonResult, isSuccess, isSuccessJson, isSuccessText, isTextResult, isTodoWriteArgs, isTodoWriteResult, isTodoWriteSuccess, isWebFetchArgs, isWebFetchResult, isWebFetchSuccess, isWebSearchArgs, isWebSearchResult, isWebSearchSuccess, loadMCPConfig, normalizeModelInfo, normalizeModelLimits, normalizeNewlines, okJson, okText, parseJSON, parseSubAgentToolCallArguments, parseToolArguments, renderTemplate, resolveBackspaces, resolveCarriageReturns, stripAnsiAndControls, supportsGetModels, toolValidators };
2246
+ export { AGENT_CREATOR_SYSTEM_PROMPT, AbortError, type AgentAwareToolPort, type AgentCatalog, type AgentConfig, type AgentEvent, AgentEventTypes, AgentFilePersistence, AgentManager, AgentManagerCommandRunner, AgentOrchestrator, AgentRegistry, type AgentTemplate, AnthropicAISDKLLM, type AssignErrorResult, type AssignParams, type AssignResult, type AssignSuccessResult$1 as AssignSuccessResult, type AssignTaskArgs, type AssignTaskMetadata, type BaseLLMOptions, type BashErrorResult, type BashParams, type BashResult, type BashSuccessResult$1 as BashSuccessResult, BashTool, type BashToolArgs, type BashToolMetadata, type CommandMetadata, type CompleteAgent, CompositeToolPort, type Conversation, ConversationContext, type ConversationMetadata, type ConversationSnapshot, ConversationStore, CoreMCPClient, DEFAULT_RETRY_CONFIG, DefaultDelegationPolicy, DefaultDelegationResultFormatter, DefaultDelegationService, DefaultSpecialistAgentFactory, type DelegationMetadata, type DelegationService, type DelegationServiceConfig, DelegationServiceFactory, type DirEntry, type DirLsArgs, type DirLsMetadata, type DirLsParams, type DirLsResult, type DirLsSuccessResult$1 as DirLsSuccessResult, type ErrorMetadata, ErrorReason, type ExecResult, type ExecResultError, type ExecResultSuccess, type FileEditArgs, type FileEditMetadata, type FileEditResult, type FileEditSuccessResult$1 as FileEditSuccessResult, type FileMetadata, type FileNewArgs, type FileNewMetadata, type FileNewParams, type FileNewResult, type FileNewSuccessResult$1 as FileNewSuccessResult, type FileReadArgs, type FileReadErrorResult, type FileReadMetadata, type FileReadParams, type FileReadResult, type FileReadSuccessResult$1 as FileReadSuccessResult, type FolderTreeOptions, type FunctionTool, GithubLLM, InMemoryMemory, InMemoryMetadata, InMemoryMetricsPort, JsonFileMemoryPersistence, type LLMConfig, LLMError, type LLMFactory, type LLMOptions, type LLMPort, LLMResolver, type LineRangeMetadata, type MCPConfig, type MCPServerConfig, MCPToolPort, type MemoryPort, MemoryPortMetadataAdapter, type Message, type MessageContent, type MessageContentPart, type MetadataPort, type MetricsChangeHandler, type MetricsPort, type MetricsSnapshot, type ModelInfo, type ModelLimits, NoopMetricsPort, NoopReminders, type OrchestratorAwareToolPort, type ParseResult, PersistedMemory, PersistingConsoleEventPort, type RetryConfig, RetryTransport, RuntimeEnv, type SendMessageOptions, SimpleContextBuilder, SimpleCost, SimpleId, type SpecialistAgentConfig, type SpecialistAgentResult, type SubAgentState, type SubAgentToolCall, SystemClock, type TodoWriteArgs, type TodoWriteMetadata, type TodoWriteResult, type TodoWriteSuccessResult$1 as TodoWriteSuccessResult, type ToolApprovalDecision, type ToolArguments, type ToolCall, type ToolCallValidation, type ToolErrorMetadata, type ToolExecutionContext, type ToolExecutionResult, type ToolMetadataMap, type ToolName, type ToolParameterMap, type ToolPort, ToolRegistry, type ToolValidator, type TypedToolInvocation, type UsageData, type UserAttachment, type UserMessagePayload, type ValidationResult, type WebFetchArgs, type WebFetchMetadata, type WebFetchParams, type WebFetchResult, type WebFetchSuccessResult$1 as WebFetchSuccessResult, type WebSearchArgs, type WebSearchMetadata, type WebSearchParams, type WebSearchResult, type WebSearchSuccessResult$1 as WebSearchSuccessResult, type WebSearchToolResult, buildAgentCreationPrompt, buildInjectedSystem, canonicalizeTerminalPaste, convertToolCall, convertToolCalls, createEmptySnapshot, createLLM, err, generateFolderTree, getAvailableProviders, getFallbackLimits, getProviderLabel, isAssignResult, isAssignSuccess, isAssignTaskArgs, isBashResult, isBashSuccess, isBashToolArgs, isDirLsArgs, isDirLsResult, isDirLsSuccess, isError, isFileEditArgs, isFileEditResult, isFileEditSuccess, isFileNewArgs, isFileNewResult, isFileNewSuccess, isFileReadArgs, isFileReadResult, isFileReadSuccess, isJsonResult, isRetryableError, isRetryableStatusCode, isSuccess, isSuccessJson, isSuccessText, isTextResult, isTodoWriteArgs, isTodoWriteResult, isTodoWriteSuccess, isWebFetchArgs, isWebFetchResult, isWebFetchSuccess, isWebSearchArgs, isWebSearchResult, isWebSearchSuccess, loadMCPConfig, normalizeModelInfo, normalizeModelLimits, normalizeNewlines, okJson, okText, parseJSON, parseSubAgentToolCallArguments, parseToolArguments, renderTemplate, resolveBackspaces, resolveCarriageReturns, stripAnsiAndControls, supportsGetModels, toolValidators };
package/dist/index.js CHANGED
@@ -1015,37 +1015,30 @@ var AgentOrchestrator = class {
1015
1015
  if (!_llm) {
1016
1016
  throw new Error("LLM provider not set");
1017
1017
  }
1018
- if (opts.retry) {
1019
- providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, []);
1020
- userMessages = [];
1021
- userDisplay = "[Retry]";
1022
- enhanced = [];
1018
+ const normalized = typeof content === "string" ? { text: content, displayText: content, attachments: [] } : {
1019
+ text: content.text ?? "",
1020
+ displayText: content.displayText,
1021
+ attachments: Array.isArray(content.attachments) ? content.attachments : []
1022
+ };
1023
+ const attachments = normalized.attachments;
1024
+ enhanced = this.reminders.enhance(normalized.text, { conversationId: convo });
1025
+ const enhancedCombined = enhanced.join("\n");
1026
+ const messageParts = buildMessageParts(enhancedCombined, attachments);
1027
+ let userContent;
1028
+ if (attachments.length > 0 || messageParts.some((part) => part.type === "image")) {
1029
+ userContent = { type: "parts", parts: messageParts };
1030
+ } else if (messageParts.length === 1 && messageParts[0]?.type === "text") {
1031
+ userContent = messageParts[0].text;
1032
+ } else if (messageParts.length > 0) {
1033
+ userContent = { type: "parts", parts: messageParts };
1023
1034
  } else {
1024
- const normalized = typeof content === "string" ? { text: content, displayText: content, attachments: [] } : {
1025
- text: content.text ?? "",
1026
- displayText: content.displayText,
1027
- attachments: Array.isArray(content.attachments) ? content.attachments : []
1028
- };
1029
- const attachments = normalized.attachments;
1030
- enhanced = this.reminders.enhance(normalized.text, { conversationId: convo });
1031
- const enhancedCombined = enhanced.join("\n");
1032
- const messageParts = buildMessageParts(enhancedCombined, attachments);
1033
- let userContent;
1034
- if (attachments.length > 0 || messageParts.some((part) => part.type === "image")) {
1035
- userContent = { type: "parts", parts: messageParts };
1036
- } else if (messageParts.length === 1 && messageParts[0]?.type === "text") {
1037
- userContent = messageParts[0].text;
1038
- } else if (messageParts.length > 0) {
1039
- userContent = { type: "parts", parts: messageParts };
1040
- } else {
1041
- userContent = enhancedCombined;
1042
- }
1043
- providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, [userContent]);
1044
- userDisplay = resolveDisplayText(normalized.text, attachments, normalized.displayText);
1045
- const userTimestamp = this.clock.iso();
1046
- userMessages = [{ id: this.ids.uuid(), role: "user", content: userContent, timestamp: userTimestamp }];
1047
- await this.memory.append(convo, userMessages);
1035
+ userContent = enhancedCombined;
1048
1036
  }
1037
+ providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, [userContent]);
1038
+ userDisplay = resolveDisplayText(normalized.text, attachments, normalized.displayText);
1039
+ const userTimestamp = this.clock.iso();
1040
+ userMessages = [{ id: this.ids.uuid(), role: "user", content: userContent, timestamp: userTimestamp }];
1041
+ await this.memory.append(convo, userMessages);
1049
1042
  if (opts.signal?.aborted) throw new Error("Aborted");
1050
1043
  const toolDefs = this.tools.getToolDefinitions(this.cfg.enabledTools ?? []);
1051
1044
  const toolNames = toolDefs.map((t) => t.function.name);
@@ -3751,7 +3744,7 @@ var AgentManager = class {
3751
3744
  }
3752
3745
  };
3753
3746
  }
3754
- this.eventCallback?.({
3747
+ await this.eventCallback?.({
3755
3748
  type: AgentEventTypes.SubAgentStarted,
3756
3749
  conversationId: config.conversationId ?? "default",
3757
3750
  messageId: config.messageId ?? "",
@@ -3770,7 +3763,7 @@ var AgentManager = class {
3770
3763
  events.push(event);
3771
3764
  if (event.type === AgentEventTypes.ToolCalls) {
3772
3765
  for (const toolCall of event.toolCalls) {
3773
- this.eventCallback?.({
3766
+ await this.eventCallback?.({
3774
3767
  type: AgentEventTypes.SubAgentToolCall,
3775
3768
  conversationId: config.conversationId ?? "default",
3776
3769
  messageId: config.messageId ?? "",
@@ -3781,7 +3774,7 @@ var AgentManager = class {
3781
3774
  });
3782
3775
  }
3783
3776
  } else if (event.type === AgentEventTypes.ToolResult) {
3784
- this.eventCallback?.({
3777
+ await this.eventCallback?.({
3785
3778
  type: AgentEventTypes.SubAgentToolResult,
3786
3779
  conversationId: config.conversationId ?? "default",
3787
3780
  messageId: config.messageId ?? "",
@@ -3845,7 +3838,7 @@ var AgentManager = class {
3845
3838
  }
3846
3839
  }
3847
3840
  const snapshot = metricsPort.getSnapshot();
3848
- this.eventCallback?.({
3841
+ await this.eventCallback?.({
3849
3842
  type: AgentEventTypes.SubAgentCompleted,
3850
3843
  conversationId: config.conversationId ?? "default",
3851
3844
  messageId: config.messageId ?? "",
@@ -3875,7 +3868,7 @@ var AgentManager = class {
3875
3868
  const isTimeout = error instanceof Error && error.message.includes("timeout");
3876
3869
  const isAborted = error instanceof Error && (error.message.includes("aborted") || error.name === "AbortError");
3877
3870
  const status = isTimeout ? "timeout" : isAborted ? "error" : "error";
3878
- this.eventCallback?.({
3871
+ await this.eventCallback?.({
3879
3872
  type: AgentEventTypes.SubAgentCompleted,
3880
3873
  conversationId: config.conversationId ?? "default",
3881
3874
  messageId: config.messageId ?? "",
@@ -4634,9 +4627,11 @@ var BaseLLM = class {
4634
4627
  transport = null;
4635
4628
  apiUrl;
4636
4629
  enablePromptCaching = false;
4630
+ retryConfig;
4637
4631
  constructor(apiUrl, options) {
4638
4632
  this.apiUrl = apiUrl;
4639
4633
  this.enablePromptCaching = options?.enablePromptCaching ?? false;
4634
+ this.retryConfig = options?.retry;
4640
4635
  }
4641
4636
  transformUsage(rawUsage) {
4642
4637
  if (!rawUsage) return void 0;
@@ -5299,6 +5294,206 @@ function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, custom
5299
5294
  return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, customHeaders);
5300
5295
  }
5301
5296
 
5297
+ // src/transports/backoff.ts
5298
+ function calculateBackoff(attempt, baseDelayMs, maxDelayMs, multiplier, jitterFactor) {
5299
+ const exponentialDelay = baseDelayMs * multiplier ** attempt;
5300
+ const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
5301
+ const jitterRange = cappedDelay * jitterFactor;
5302
+ const jitter = jitterRange * (Math.random() * 2 - 1);
5303
+ return Math.max(0, Math.round(cappedDelay + jitter));
5304
+ }
5305
+ function parseRetryAfterHeader(header) {
5306
+ if (!header) return null;
5307
+ const seconds = parseInt(header, 10);
5308
+ if (!Number.isNaN(seconds) && seconds >= 0) {
5309
+ return seconds * 1e3;
5310
+ }
5311
+ const date = Date.parse(header);
5312
+ if (!Number.isNaN(date)) {
5313
+ const delayMs = date - Date.now();
5314
+ return delayMs > 0 ? delayMs : null;
5315
+ }
5316
+ return null;
5317
+ }
5318
+
5319
+ // src/transports/error-classification.ts
5320
+ var DEFAULT_RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];
5321
+ var RETRYABLE_NETWORK_ERRORS = [
5322
+ "econnrefused",
5323
+ "enotfound",
5324
+ "etimedout",
5325
+ "econnreset",
5326
+ "epipe",
5327
+ "network",
5328
+ "fetch failed",
5329
+ "socket hang up"
5330
+ ];
5331
+ var NON_RETRYABLE_PATTERNS = [
5332
+ "invalid",
5333
+ "unauthorized",
5334
+ "forbidden",
5335
+ "not found",
5336
+ "bad request",
5337
+ "api key",
5338
+ "authentication",
5339
+ "permission",
5340
+ "invalid_api_key",
5341
+ "insufficient_quota"
5342
+ ];
5343
+ function isRetryableStatusCode(status, retryableStatusCodes = DEFAULT_RETRYABLE_STATUS_CODES) {
5344
+ return retryableStatusCodes.includes(status);
5345
+ }
5346
+ function isRetryableError(error, retryableStatusCodes = DEFAULT_RETRYABLE_STATUS_CODES) {
5347
+ if (!error) return false;
5348
+ if (error instanceof Error && error.name === "AbortError") {
5349
+ return false;
5350
+ }
5351
+ if (error instanceof LLMError) {
5352
+ if (error.statusCode) {
5353
+ return isRetryableStatusCode(error.statusCode, retryableStatusCodes);
5354
+ }
5355
+ return error.isRetryable;
5356
+ }
5357
+ const err2 = error;
5358
+ const statusCode = err2.statusCode ?? err2.status;
5359
+ if (statusCode) {
5360
+ return isRetryableStatusCode(statusCode, retryableStatusCodes);
5361
+ }
5362
+ const message = err2.message?.toLowerCase() || "";
5363
+ const code = err2.code?.toLowerCase() || "";
5364
+ for (const pattern of NON_RETRYABLE_PATTERNS) {
5365
+ if (message.includes(pattern) || code.includes(pattern)) {
5366
+ return false;
5367
+ }
5368
+ }
5369
+ for (const networkError of RETRYABLE_NETWORK_ERRORS) {
5370
+ if (message.includes(networkError) || code.includes(networkError)) {
5371
+ return true;
5372
+ }
5373
+ }
5374
+ return false;
5375
+ }
5376
+
5377
+ // src/transports/retry-transport.ts
5378
+ var DEFAULT_RETRY_CONFIG = {
5379
+ maxRetries: 3,
5380
+ baseDelayMs: 1e3,
5381
+ maxDelayMs: 6e4,
5382
+ backoffMultiplier: 2,
5383
+ jitterFactor: 0.2,
5384
+ retryableStatusCodes: [429, 500, 502, 503, 504]
5385
+ };
5386
+ var AbortError = class _AbortError extends Error {
5387
+ constructor(message = "Operation aborted") {
5388
+ super(message);
5389
+ this.name = "AbortError";
5390
+ if (Error.captureStackTrace) {
5391
+ Error.captureStackTrace(this, _AbortError);
5392
+ }
5393
+ }
5394
+ };
5395
+ var sleep = (ms, signal) => {
5396
+ return new Promise((resolve6, reject) => {
5397
+ if (signal?.aborted) {
5398
+ reject(new AbortError("Operation aborted"));
5399
+ return;
5400
+ }
5401
+ const timeout = setTimeout(resolve6, ms);
5402
+ signal?.addEventListener(
5403
+ "abort",
5404
+ () => {
5405
+ clearTimeout(timeout);
5406
+ reject(new AbortError("Operation aborted during retry delay"));
5407
+ },
5408
+ { once: true }
5409
+ );
5410
+ });
5411
+ };
5412
+ var RetryTransport = class {
5413
+ constructor(inner, config = {}) {
5414
+ this.inner = inner;
5415
+ this.config = { ...DEFAULT_RETRY_CONFIG, ...config };
5416
+ }
5417
+ config;
5418
+ async get(url, headers, signal) {
5419
+ return this.executeWithRetry(() => this.inner.get(url, headers, signal), signal);
5420
+ }
5421
+ async post(url, body, headers, signal) {
5422
+ return this.executeWithRetry(() => this.inner.post(url, body, headers, signal), signal);
5423
+ }
5424
+ async executeWithRetry(fn, signal) {
5425
+ let lastError;
5426
+ let attempt = 0;
5427
+ while (attempt <= this.config.maxRetries) {
5428
+ if (signal?.aborted) {
5429
+ throw new AbortError("Operation aborted before execution");
5430
+ }
5431
+ try {
5432
+ const response = await fn();
5433
+ if (this.isRetryableResponse(response)) {
5434
+ const error = new Error(`HTTP ${response.status}`);
5435
+ error.status = response.status;
5436
+ if (attempt < this.config.maxRetries) {
5437
+ const delayMs = this.getDelayForResponse(response, attempt);
5438
+ this.config.onRetry?.(attempt + 1, error, delayMs);
5439
+ await sleep(delayMs, signal);
5440
+ attempt++;
5441
+ continue;
5442
+ }
5443
+ lastError = error;
5444
+ break;
5445
+ }
5446
+ return response;
5447
+ } catch (error) {
5448
+ if (error instanceof AbortError || error instanceof Error && error.name === "AbortError") {
5449
+ throw error;
5450
+ }
5451
+ lastError = error instanceof Error ? error : new Error(String(error));
5452
+ if (!isRetryableError(lastError, this.config.retryableStatusCodes)) {
5453
+ throw lastError;
5454
+ }
5455
+ if (attempt < this.config.maxRetries) {
5456
+ const delayMs = calculateBackoff(
5457
+ attempt,
5458
+ this.config.baseDelayMs,
5459
+ this.config.maxDelayMs,
5460
+ this.config.backoffMultiplier,
5461
+ this.config.jitterFactor
5462
+ );
5463
+ this.config.onRetry?.(attempt + 1, lastError, delayMs);
5464
+ await sleep(delayMs, signal);
5465
+ attempt++;
5466
+ } else {
5467
+ break;
5468
+ }
5469
+ }
5470
+ }
5471
+ if (lastError) {
5472
+ this.config.onExhausted?.(lastError, attempt + 1);
5473
+ throw lastError;
5474
+ }
5475
+ throw new Error("Retry exhausted without error");
5476
+ }
5477
+ isRetryableResponse(response) {
5478
+ return isRetryableStatusCode(response.status, this.config.retryableStatusCodes);
5479
+ }
5480
+ getDelayForResponse(response, attempt) {
5481
+ const headers = "headers" in response && response.headers instanceof Headers ? response.headers : null;
5482
+ const retryAfter = headers?.get("retry-after") ?? null;
5483
+ const retryAfterMs = parseRetryAfterHeader(retryAfter);
5484
+ if (retryAfterMs !== null) {
5485
+ return Math.min(retryAfterMs, this.config.maxDelayMs);
5486
+ }
5487
+ return calculateBackoff(
5488
+ attempt,
5489
+ this.config.baseDelayMs,
5490
+ this.config.maxDelayMs,
5491
+ this.config.backoffMultiplier,
5492
+ this.config.jitterFactor
5493
+ );
5494
+ }
5495
+ };
5496
+
5302
5497
  // src/llm-providers/model-limits.ts
5303
5498
  function normalizeModelLimits(provider, model) {
5304
5499
  switch (provider.toLowerCase()) {
@@ -5416,7 +5611,7 @@ function normalizeModelInfo(provider, model) {
5416
5611
  var GithubLLM = class extends BaseLLM {
5417
5612
  opts;
5418
5613
  constructor(opts = {}) {
5419
- super(opts.apiUrl ?? "https://api.individual.githubcopilot.com");
5614
+ super(opts.apiUrl ?? "https://api.individual.githubcopilot.com", { retry: opts.retry });
5420
5615
  this.opts = opts;
5421
5616
  }
5422
5617
  createTransport() {
@@ -5427,11 +5622,15 @@ var GithubLLM = class extends BaseLLM {
5427
5622
  maxFileSize: 5 * 1024 * 1024,
5428
5623
  captureResponseBody: true
5429
5624
  });
5430
- return new GithubAuthTransport(base, {
5625
+ const authTransport = new GithubAuthTransport(base, {
5431
5626
  baseUrl: this.opts.apiUrl,
5432
5627
  apiKey: this.opts.apiKey,
5433
5628
  accessToken: this.opts.accessToken
5434
5629
  });
5630
+ if (this.retryConfig) {
5631
+ return new RetryTransport(authTransport, this.retryConfig);
5632
+ }
5633
+ return authTransport;
5435
5634
  }
5436
5635
  transformUsage(rawUsage) {
5437
5636
  if (!rawUsage) return void 0;
@@ -6050,8 +6249,8 @@ var GenericLLM = class extends BaseLLM {
6050
6249
  providerName;
6051
6250
  customHeaders;
6052
6251
  constructor(baseUrl, modelConfig, opts = {}, customHeaders) {
6053
- const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", ...restOpts } = opts;
6054
- super(opts.apiUrl || baseUrl, { enablePromptCaching });
6252
+ const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", retry, ...restOpts } = opts;
6253
+ super(opts.apiUrl || baseUrl, { enablePromptCaching, retry });
6055
6254
  this.includeUsage = includeUsage;
6056
6255
  this.modelConfig = modelConfig;
6057
6256
  this.providerName = providerName;
@@ -6066,7 +6265,7 @@ var GenericLLM = class extends BaseLLM {
6066
6265
  maxFileSize: 5 * 1024 * 1024,
6067
6266
  captureResponseBody: true
6068
6267
  });
6069
- return createTransport(
6268
+ const authTransport = createTransport(
6070
6269
  base,
6071
6270
  this.apiUrl,
6072
6271
  this.opts.apiKey,
@@ -6074,6 +6273,10 @@ var GenericLLM = class extends BaseLLM {
6074
6273
  this.opts.version,
6075
6274
  this.customHeaders
6076
6275
  );
6276
+ if (this.retryConfig) {
6277
+ return new RetryTransport(authTransport, this.retryConfig);
6278
+ }
6279
+ return authTransport;
6077
6280
  }
6078
6281
  async getModels(signal) {
6079
6282
  if (this.modelConfig === false) {
@@ -6464,6 +6667,7 @@ function isValidConfig(value) {
6464
6667
  }
6465
6668
  export {
6466
6669
  AGENT_CREATOR_SYSTEM_PROMPT,
6670
+ AbortError,
6467
6671
  AgentEventTypes,
6468
6672
  AgentFilePersistence,
6469
6673
  AgentManager,
@@ -6476,6 +6680,7 @@ export {
6476
6680
  ConversationContext,
6477
6681
  ConversationStore,
6478
6682
  CoreMCPClient,
6683
+ DEFAULT_RETRY_CONFIG,
6479
6684
  DefaultDelegationPolicy,
6480
6685
  DefaultDelegationResultFormatter,
6481
6686
  DefaultDelegationService,
@@ -6495,6 +6700,7 @@ export {
6495
6700
  NoopReminders,
6496
6701
  PersistedMemory,
6497
6702
  PersistingConsoleEventPort,
6703
+ RetryTransport,
6498
6704
  RuntimeEnv,
6499
6705
  SimpleContextBuilder,
6500
6706
  SimpleCost,
@@ -6533,6 +6739,8 @@ export {
6533
6739
  isFileReadResult,
6534
6740
  isFileReadSuccess,
6535
6741
  isJsonResult,
6742
+ isRetryableError,
6743
+ isRetryableStatusCode,
6536
6744
  isSuccess,
6537
6745
  isSuccessJson,
6538
6746
  isSuccessText,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuvin/nuvin-core",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "",
5
5
  "private": false,
6
6
  "main": "dist/index.js",