nova-terminal-assistant 0.1.0

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.

Potentially problematic release.


This version of nova-terminal-assistant might be problematic. Click here for more details.

Files changed (192) hide show
  1. package/README.md +358 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +12 -0
  4. package/package.json +67 -0
  5. package/src/cli/commands/SmartCompletion.ts +458 -0
  6. package/src/cli/index.ts +5 -0
  7. package/src/cli/startup/IFlowRepl.ts +212 -0
  8. package/src/cli/startup/InkBasedRepl.ts +1056 -0
  9. package/src/cli/startup/InteractiveRepl.ts +2833 -0
  10. package/src/cli/startup/NovaApp.ts +1861 -0
  11. package/src/cli/startup/index.ts +4 -0
  12. package/src/cli/startup/parseArgs.ts +293 -0
  13. package/src/cli/test-modules.ts +27 -0
  14. package/src/cli/ui/IFlowDropdown.ts +425 -0
  15. package/src/cli/ui/ModernReplUI.ts +276 -0
  16. package/src/cli/ui/SimpleSelector2.ts +215 -0
  17. package/src/cli/ui/components/ConfirmDialog.ts +176 -0
  18. package/src/cli/ui/components/ErrorPanel.ts +364 -0
  19. package/src/cli/ui/components/InkAppRunner.tsx +67 -0
  20. package/src/cli/ui/components/InkComponents.tsx +613 -0
  21. package/src/cli/ui/components/NovaInkApp.tsx +312 -0
  22. package/src/cli/ui/components/ProgressBar.ts +177 -0
  23. package/src/cli/ui/components/ProgressIndicator.ts +298 -0
  24. package/src/cli/ui/components/QuickActions.ts +396 -0
  25. package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
  26. package/src/cli/ui/components/StatusBar.ts +194 -0
  27. package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
  28. package/src/cli/ui/components/index.ts +27 -0
  29. package/src/cli/ui/ink-prototype.tsx +347 -0
  30. package/src/cli/utils/CliUI.ts +336 -0
  31. package/src/cli/utils/CompletionHelper.ts +388 -0
  32. package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
  33. package/src/cli/utils/EnhancedCompleter.ts +513 -0
  34. package/src/cli/utils/ErrorEnhancer.ts +429 -0
  35. package/src/cli/utils/OutputFormatter.ts +193 -0
  36. package/src/cli/utils/index.ts +9 -0
  37. package/src/core/agents/AgentOrchestrator.ts +515 -0
  38. package/src/core/agents/index.ts +17 -0
  39. package/src/core/audit/AuditLogger.ts +509 -0
  40. package/src/core/audit/index.ts +11 -0
  41. package/src/core/auth/AuthManager.d.ts.map +1 -0
  42. package/src/core/auth/AuthManager.ts +138 -0
  43. package/src/core/auth/index.d.ts.map +1 -0
  44. package/src/core/auth/index.ts +2 -0
  45. package/src/core/config/ConfigManager.d.ts.map +1 -0
  46. package/src/core/config/ConfigManager.test.ts +183 -0
  47. package/src/core/config/ConfigManager.ts +1219 -0
  48. package/src/core/config/index.d.ts.map +1 -0
  49. package/src/core/config/index.ts +1 -0
  50. package/src/core/context/ContextBuilder.d.ts.map +1 -0
  51. package/src/core/context/ContextBuilder.ts +171 -0
  52. package/src/core/context/ContextCompressor.d.ts.map +1 -0
  53. package/src/core/context/ContextCompressor.ts +642 -0
  54. package/src/core/context/LayeredMemoryManager.ts +657 -0
  55. package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
  56. package/src/core/context/MemoryDiscovery.ts +175 -0
  57. package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
  58. package/src/core/context/defaultSystemPrompt.ts +35 -0
  59. package/src/core/context/index.d.ts.map +1 -0
  60. package/src/core/context/index.ts +22 -0
  61. package/src/core/extensions/SkillGenerator.ts +421 -0
  62. package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
  63. package/src/core/extensions/SkillInstaller.ts +257 -0
  64. package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
  65. package/src/core/extensions/SkillRegistry.ts +361 -0
  66. package/src/core/extensions/SkillValidator.ts +525 -0
  67. package/src/core/extensions/index.ts +15 -0
  68. package/src/core/index.d.ts.map +1 -0
  69. package/src/core/index.ts +42 -0
  70. package/src/core/mcp/McpManager.d.ts.map +1 -0
  71. package/src/core/mcp/McpManager.ts +632 -0
  72. package/src/core/mcp/index.d.ts.map +1 -0
  73. package/src/core/mcp/index.ts +2 -0
  74. package/src/core/model/ModelClient.d.ts.map +1 -0
  75. package/src/core/model/ModelClient.ts +217 -0
  76. package/src/core/model/ModelConnectionTester.ts +363 -0
  77. package/src/core/model/ModelValidator.ts +348 -0
  78. package/src/core/model/index.d.ts.map +1 -0
  79. package/src/core/model/index.ts +6 -0
  80. package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
  81. package/src/core/model/providers/AnthropicProvider.ts +279 -0
  82. package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
  83. package/src/core/model/providers/CodingPlanProvider.ts +210 -0
  84. package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  85. package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
  86. package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
  87. package/src/core/model/providers/OllamaManager.ts +201 -0
  88. package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
  89. package/src/core/model/providers/OllamaProvider.ts +73 -0
  90. package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  91. package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
  92. package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
  93. package/src/core/model/providers/OpenAIProvider.ts +29 -0
  94. package/src/core/model/providers/index.d.ts.map +1 -0
  95. package/src/core/model/providers/index.ts +12 -0
  96. package/src/core/model/types.d.ts.map +1 -0
  97. package/src/core/model/types.ts +77 -0
  98. package/src/core/security/ApprovalManager.d.ts.map +1 -0
  99. package/src/core/security/ApprovalManager.ts +174 -0
  100. package/src/core/security/FileFilter.d.ts.map +1 -0
  101. package/src/core/security/FileFilter.ts +141 -0
  102. package/src/core/security/HookExecutor.d.ts.map +1 -0
  103. package/src/core/security/HookExecutor.ts +178 -0
  104. package/src/core/security/SandboxExecutor.ts +447 -0
  105. package/src/core/security/index.d.ts.map +1 -0
  106. package/src/core/security/index.ts +8 -0
  107. package/src/core/session/AgentLoop.d.ts.map +1 -0
  108. package/src/core/session/AgentLoop.ts +501 -0
  109. package/src/core/session/SessionManager.d.ts.map +1 -0
  110. package/src/core/session/SessionManager.test.ts +183 -0
  111. package/src/core/session/SessionManager.ts +460 -0
  112. package/src/core/session/index.d.ts.map +1 -0
  113. package/src/core/session/index.ts +3 -0
  114. package/src/core/telemetry/Telemetry.d.ts.map +1 -0
  115. package/src/core/telemetry/Telemetry.ts +90 -0
  116. package/src/core/telemetry/TelemetryService.ts +531 -0
  117. package/src/core/telemetry/index.d.ts.map +1 -0
  118. package/src/core/telemetry/index.ts +12 -0
  119. package/src/core/testing/AutoFixer.ts +385 -0
  120. package/src/core/testing/ErrorAnalyzer.ts +499 -0
  121. package/src/core/testing/TestRunner.ts +265 -0
  122. package/src/core/testing/agent-cli-tests.ts +538 -0
  123. package/src/core/testing/index.ts +11 -0
  124. package/src/core/tools/ToolRegistry.d.ts.map +1 -0
  125. package/src/core/tools/ToolRegistry.test.ts +206 -0
  126. package/src/core/tools/ToolRegistry.ts +260 -0
  127. package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
  128. package/src/core/tools/impl/EditFileTool.ts +97 -0
  129. package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  130. package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
  131. package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
  132. package/src/core/tools/impl/MemoryTool.ts +102 -0
  133. package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
  134. package/src/core/tools/impl/ReadFileTool.ts +58 -0
  135. package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
  136. package/src/core/tools/impl/SearchContentTool.ts +94 -0
  137. package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
  138. package/src/core/tools/impl/SearchFileTool.ts +61 -0
  139. package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
  140. package/src/core/tools/impl/ShellTool.ts +118 -0
  141. package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
  142. package/src/core/tools/impl/TaskTool.ts +207 -0
  143. package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
  144. package/src/core/tools/impl/TodoTool.ts +122 -0
  145. package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
  146. package/src/core/tools/impl/WebFetchTool.ts +103 -0
  147. package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
  148. package/src/core/tools/impl/WebSearchTool.ts +89 -0
  149. package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
  150. package/src/core/tools/impl/WriteFileTool.ts +49 -0
  151. package/src/core/tools/impl/index.d.ts.map +1 -0
  152. package/src/core/tools/impl/index.ts +16 -0
  153. package/src/core/tools/index.d.ts.map +1 -0
  154. package/src/core/tools/index.ts +7 -0
  155. package/src/core/tools/schemas/execution.d.ts.map +1 -0
  156. package/src/core/tools/schemas/execution.ts +42 -0
  157. package/src/core/tools/schemas/file.d.ts.map +1 -0
  158. package/src/core/tools/schemas/file.ts +119 -0
  159. package/src/core/tools/schemas/index.d.ts.map +1 -0
  160. package/src/core/tools/schemas/index.ts +11 -0
  161. package/src/core/tools/schemas/memory.d.ts.map +1 -0
  162. package/src/core/tools/schemas/memory.ts +52 -0
  163. package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
  164. package/src/core/tools/schemas/orchestration.ts +44 -0
  165. package/src/core/tools/schemas/search.d.ts.map +1 -0
  166. package/src/core/tools/schemas/search.ts +112 -0
  167. package/src/core/tools/schemas/todo.d.ts.map +1 -0
  168. package/src/core/tools/schemas/todo.ts +32 -0
  169. package/src/core/tools/schemas/web.d.ts.map +1 -0
  170. package/src/core/tools/schemas/web.ts +86 -0
  171. package/src/core/types/config.d.ts.map +1 -0
  172. package/src/core/types/config.ts +200 -0
  173. package/src/core/types/errors.d.ts.map +1 -0
  174. package/src/core/types/errors.ts +204 -0
  175. package/src/core/types/index.d.ts.map +1 -0
  176. package/src/core/types/index.ts +8 -0
  177. package/src/core/types/session.d.ts.map +1 -0
  178. package/src/core/types/session.ts +216 -0
  179. package/src/core/types/tools.d.ts.map +1 -0
  180. package/src/core/types/tools.ts +157 -0
  181. package/src/core/utils/CheckpointManager.d.ts.map +1 -0
  182. package/src/core/utils/CheckpointManager.ts +327 -0
  183. package/src/core/utils/Logger.d.ts.map +1 -0
  184. package/src/core/utils/Logger.ts +98 -0
  185. package/src/core/utils/RetryManager.ts +471 -0
  186. package/src/core/utils/TokenCounter.d.ts.map +1 -0
  187. package/src/core/utils/TokenCounter.ts +414 -0
  188. package/src/core/utils/VectorMemoryStore.ts +440 -0
  189. package/src/core/utils/helpers.d.ts.map +1 -0
  190. package/src/core/utils/helpers.ts +89 -0
  191. package/src/core/utils/index.d.ts.map +1 -0
  192. package/src/core/utils/index.ts +19 -0
@@ -0,0 +1,471 @@
1
+ // ============================================================================
2
+ // RetryManager - Exponential backoff retry with jitter
3
+ // ============================================================================
4
+
5
+ import { createLogger } from './Logger.js';
6
+
7
+ const logger = createLogger('RetryManager');
8
+
9
+ /**
10
+ * Retry configuration options
11
+ */
12
+ export interface RetryConfig {
13
+ /** Maximum number of retry attempts */
14
+ maxAttempts: number;
15
+ /** Initial delay in milliseconds */
16
+ initialDelay: number;
17
+ /** Maximum delay in milliseconds */
18
+ maxDelay: number;
19
+ /** Backoff multiplier (default 2) */
20
+ backoffMultiplier?: number;
21
+ /** Jitter factor (0-1, default 0.1) */
22
+ jitterFactor?: number;
23
+ /** Errors that should trigger retry */
24
+ retryableErrors?: string[];
25
+ /** HTTP status codes that should trigger retry */
26
+ retryableStatusCodes?: number[];
27
+ }
28
+
29
+ /**
30
+ * Default retry configuration
31
+ */
32
+ const DEFAULT_RETRY_CONFIG: RetryConfig = {
33
+ maxAttempts: 3,
34
+ initialDelay: 1000,
35
+ maxDelay: 30000,
36
+ backoffMultiplier: 2,
37
+ jitterFactor: 0.1,
38
+ retryableErrors: [
39
+ 'ECONNRESET',
40
+ 'ENOTFOUND',
41
+ 'ETIMEDOUT',
42
+ 'ECONNREFUSED',
43
+ 'ENETDOWN',
44
+ 'ENETUNREACH',
45
+ 'EHOSTDOWN',
46
+ 'EHOSTUNREACH',
47
+ 'EPIPE',
48
+ 'rate_limit',
49
+ 'overloaded',
50
+ 'timeout',
51
+ ],
52
+ retryableStatusCodes: [429, 500, 502, 503, 504],
53
+ };
54
+
55
+ /**
56
+ * Calculate delay with exponential backoff and jitter
57
+ */
58
+ function calculateDelay(
59
+ attempt: number,
60
+ config: RetryConfig
61
+ ): number {
62
+ const { initialDelay, maxDelay, backoffMultiplier = 2, jitterFactor = 0.1 } = config;
63
+
64
+ // Exponential backoff: initialDelay * (2 ^ attempt)
65
+ const exponentialDelay = initialDelay * Math.pow(backoffMultiplier, attempt);
66
+
67
+ // Cap at max delay
68
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
69
+
70
+ // Add jitter: random variation to prevent thundering herd
71
+ const jitter = cappedDelay * jitterFactor * (Math.random() * 2 - 1);
72
+
73
+ return Math.max(0, cappedDelay + jitter);
74
+ }
75
+
76
+ /**
77
+ * Check if an error is retryable
78
+ */
79
+ function isRetryableError(
80
+ error: unknown,
81
+ config: RetryConfig
82
+ ): boolean {
83
+ const { retryableErrors = [], retryableStatusCodes = [] } = config;
84
+
85
+ // Check error message
86
+ const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
87
+ for (const retryable of retryableErrors) {
88
+ if (errorMessage.includes(retryable.toLowerCase())) {
89
+ return true;
90
+ }
91
+ }
92
+
93
+ // Check error name
94
+ if (error instanceof Error) {
95
+ for (const retryable of retryableErrors) {
96
+ if (error.name.toLowerCase().includes(retryable.toLowerCase())) {
97
+ return true;
98
+ }
99
+ }
100
+ }
101
+
102
+ // Check status code
103
+ const anyError = error as any;
104
+ if (anyError?.status && retryableStatusCodes.includes(anyError.status)) {
105
+ return true;
106
+ }
107
+ if (anyError?.statusCode && retryableStatusCodes.includes(anyError.statusCode)) {
108
+ return true;
109
+ }
110
+
111
+ // Check for specific error types
112
+ if (anyError?.error?.type === 'rate_limit_error') return true;
113
+ if (anyError?.error?.type === 'overloaded_error') return true;
114
+
115
+ return false;
116
+ }
117
+
118
+ /**
119
+ * Sleep for a given duration
120
+ */
121
+ function sleep(ms: number): Promise<void> {
122
+ return new Promise((resolve) => setTimeout(resolve, ms));
123
+ }
124
+
125
+ /**
126
+ * Execute a function with retry logic
127
+ */
128
+ export async function withRetry<T>(
129
+ fn: () => Promise<T>,
130
+ config: Partial<RetryConfig> = {}
131
+ ): Promise<T> {
132
+ const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
133
+ const { maxAttempts } = fullConfig;
134
+
135
+ let lastError: unknown;
136
+
137
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
138
+ try {
139
+ return await fn();
140
+ } catch (error) {
141
+ lastError = error;
142
+
143
+ // Check if retryable
144
+ if (!isRetryableError(error, fullConfig)) {
145
+ throw error;
146
+ }
147
+
148
+ // Check if we've exhausted attempts
149
+ if (attempt >= maxAttempts - 1) {
150
+ logger.warn(`Retry exhausted after ${maxAttempts} attempts`, { error });
151
+ throw error;
152
+ }
153
+
154
+ // Calculate delay and wait
155
+ const delay = calculateDelay(attempt, fullConfig);
156
+ logger.debug(`Retrying in ${delay}ms (attempt ${attempt + 1}/${maxAttempts})`, { error });
157
+
158
+ await sleep(delay);
159
+ }
160
+ }
161
+
162
+ throw lastError;
163
+ }
164
+
165
+ /**
166
+ * Create a retry wrapper for a function
167
+ */
168
+ export function createRetryWrapper<TArgs extends any[], TResult>(
169
+ fn: (...args: TArgs) => Promise<TResult>,
170
+ config: Partial<RetryConfig> = {}
171
+ ): (...args: TArgs) => Promise<TResult> {
172
+ return (...args: TArgs) => withRetry(() => fn(...args), config);
173
+ }
174
+
175
+ // ============================================================================
176
+ // RateLimiter - Token bucket rate limiting
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Rate limiter configuration
181
+ */
182
+ export interface RateLimiterConfig {
183
+ /** Maximum tokens in bucket */
184
+ maxTokens: number;
185
+ /** Tokens replenished per second */
186
+ tokensPerSecond: number;
187
+ /** Maximum wait time in milliseconds (default 60000) */
188
+ maxWaitTime?: number;
189
+ }
190
+
191
+ /**
192
+ * Token bucket rate limiter
193
+ */
194
+ export class RateLimiter {
195
+ private tokens: number;
196
+ private lastRefill: number;
197
+ private config: RateLimiterConfig;
198
+ private waitQueue: Array<{
199
+ tokens: number;
200
+ resolve: () => void;
201
+ reject: (err: Error) => void;
202
+ timestamp: number;
203
+ }> = [];
204
+
205
+ constructor(config: RateLimiterConfig) {
206
+ this.config = config;
207
+ this.tokens = config.maxTokens;
208
+ this.lastRefill = Date.now();
209
+ }
210
+
211
+ /**
212
+ * Refill tokens based on elapsed time
213
+ */
214
+ private refill(): void {
215
+ const now = Date.now();
216
+ const elapsed = (now - this.lastRefill) / 1000;
217
+ const tokensToAdd = elapsed * this.config.tokensPerSecond;
218
+
219
+ this.tokens = Math.min(this.config.maxTokens, this.tokens + tokensToAdd);
220
+ this.lastRefill = now;
221
+ }
222
+
223
+ /**
224
+ * Try to acquire tokens without waiting
225
+ * @returns true if tokens were acquired, false otherwise
226
+ */
227
+ tryAcquire(tokens: number): boolean {
228
+ this.refill();
229
+
230
+ if (this.tokens >= tokens) {
231
+ this.tokens -= tokens;
232
+ return true;
233
+ }
234
+
235
+ return false;
236
+ }
237
+
238
+ /**
239
+ * Acquire tokens, waiting if necessary
240
+ */
241
+ async acquire(tokens: number): Promise<void> {
242
+ this.refill();
243
+
244
+ // If we have enough tokens, acquire immediately
245
+ if (this.tokens >= tokens) {
246
+ this.tokens -= tokens;
247
+ return;
248
+ }
249
+
250
+ // Calculate wait time
251
+ const tokensNeeded = tokens - this.tokens;
252
+ const waitTimeMs = (tokensNeeded / this.config.tokensPerSecond) * 1000;
253
+ const maxWaitTime = this.config.maxWaitTime ?? 60000;
254
+
255
+ if (waitTimeMs > maxWaitTime) {
256
+ throw new Error(`Rate limit wait time (${waitTimeMs}ms) exceeds maximum (${maxWaitTime}ms)`);
257
+ }
258
+
259
+ // Add to wait queue
260
+ return new Promise((resolve, reject) => {
261
+ this.waitQueue.push({
262
+ tokens,
263
+ resolve,
264
+ reject,
265
+ timestamp: Date.now(),
266
+ });
267
+
268
+ // Start processing queue
269
+ this.processQueue();
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Process waiting requests
275
+ */
276
+ private processQueue(): void {
277
+ const checkAndProcess = () => {
278
+ this.refill();
279
+
280
+ // Process queue in order
281
+ while (this.waitQueue.length > 0 && this.tokens >= (this.waitQueue[0]?.tokens ?? 0)) {
282
+ const request = this.waitQueue.shift();
283
+ if (request) {
284
+ this.tokens -= request.tokens;
285
+ request.resolve();
286
+ }
287
+ }
288
+
289
+ // Schedule next check if queue not empty
290
+ const nextRequest = this.waitQueue[0];
291
+ if (nextRequest) {
292
+ const tokensNeeded = nextRequest.tokens - this.tokens;
293
+ const waitTimeMs = (tokensNeeded / this.config.tokensPerSecond) * 1000;
294
+
295
+ setTimeout(checkAndProcess, Math.min(waitTimeMs, 100));
296
+ }
297
+ };
298
+
299
+ // Check for timed-out requests
300
+ const maxWaitTime = this.config.maxWaitTime ?? 60000;
301
+ const now = Date.now();
302
+ this.waitQueue = this.waitQueue.filter((request) => {
303
+ if (now - request.timestamp > maxWaitTime) {
304
+ request.reject(new Error('Rate limit wait timeout'));
305
+ return false;
306
+ }
307
+ return true;
308
+ });
309
+
310
+ checkAndProcess();
311
+ }
312
+
313
+ /**
314
+ * Get current token count
315
+ */
316
+ getTokens(): number {
317
+ this.refill();
318
+ return this.tokens;
319
+ }
320
+
321
+ /**
322
+ * Get estimated wait time for a given number of tokens
323
+ */
324
+ getWaitTime(tokens: number): number {
325
+ this.refill();
326
+
327
+ if (this.tokens >= tokens) return 0;
328
+
329
+ const tokensNeeded = tokens - this.tokens;
330
+ return (tokensNeeded / this.config.tokensPerSecond) * 1000;
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Create a rate-limited wrapper for a function
336
+ */
337
+ export function withRateLimit<TArgs extends any[], TResult>(
338
+ fn: (...args: TArgs) => Promise<TResult>,
339
+ limiter: RateLimiter,
340
+ tokensPerCall: number = 1
341
+ ): (...args: TArgs) => Promise<TResult> {
342
+ return async (...args: TArgs) => {
343
+ await limiter.acquire(tokensPerCall);
344
+ return fn(...args);
345
+ };
346
+ }
347
+
348
+ // ============================================================================
349
+ // ConcurrencyLimiter - Limit concurrent operations
350
+ // ============================================================================
351
+
352
+ /**
353
+ * Concurrency limiter using semaphore pattern
354
+ */
355
+ export class ConcurrencyLimiter {
356
+ private running = 0;
357
+ private queue: Array<{
358
+ resolve: () => void;
359
+ reject: (err: Error) => void;
360
+ }> = [];
361
+
362
+ constructor(private maxConcurrent: number) {}
363
+
364
+ /**
365
+ * Acquire a slot
366
+ */
367
+ async acquire(): Promise<void> {
368
+ if (this.running < this.maxConcurrent) {
369
+ this.running++;
370
+ return;
371
+ }
372
+
373
+ return new Promise((resolve, reject) => {
374
+ this.queue.push({ resolve, reject });
375
+ });
376
+ }
377
+
378
+ /**
379
+ * Release a slot
380
+ */
381
+ release(): void {
382
+ this.running--;
383
+
384
+ if (this.queue.length > 0) {
385
+ const next = this.queue.shift()!;
386
+ this.running++;
387
+ next.resolve();
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Execute a function with concurrency limit
393
+ */
394
+ async run<T>(fn: () => Promise<T>): Promise<T> {
395
+ await this.acquire();
396
+ try {
397
+ return await fn();
398
+ } finally {
399
+ this.release();
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Get current state
405
+ */
406
+ getState(): { running: number; queued: number; available: number } {
407
+ return {
408
+ running: this.running,
409
+ queued: this.queue.length,
410
+ available: this.maxConcurrent - this.running,
411
+ };
412
+ }
413
+ }
414
+
415
+ // ============================================================================
416
+ // Composite utilities
417
+ // ============================================================================
418
+
419
+ /**
420
+ * Create a resilient function with retry, rate limiting, and concurrency control
421
+ */
422
+ export function createResilientFunction<TArgs extends any[], TResult>(
423
+ fn: (...args: TArgs) => Promise<TResult>,
424
+ options: {
425
+ retry?: Partial<RetryConfig>;
426
+ rateLimit?: RateLimiterConfig;
427
+ maxConcurrent?: number;
428
+ } = {}
429
+ ): (...args: TArgs) => Promise<TResult> {
430
+ let wrapped = fn;
431
+
432
+ // Add retry
433
+ if (options.retry) {
434
+ wrapped = createRetryWrapper(wrapped, options.retry);
435
+ }
436
+
437
+ // Add rate limiting
438
+ if (options.rateLimit) {
439
+ const limiter = new RateLimiter(options.rateLimit);
440
+ wrapped = withRateLimit(wrapped, limiter);
441
+ }
442
+
443
+ // Add concurrency control
444
+ if (options.maxConcurrent) {
445
+ const limiter = new ConcurrencyLimiter(options.maxConcurrent);
446
+ const originalWrapped = wrapped;
447
+ wrapped = async (...args: TArgs) => limiter.run(() => originalWrapped(...args));
448
+ }
449
+
450
+ return wrapped;
451
+ }
452
+
453
+ /**
454
+ * Default rate limiter for API calls
455
+ * - 60 requests per minute (1 per second)
456
+ * - Burst of up to 10 requests
457
+ */
458
+ export function createDefaultApiRateLimiter(): RateLimiter {
459
+ return new RateLimiter({
460
+ maxTokens: 10,
461
+ tokensPerSecond: 1,
462
+ maxWaitTime: 60000,
463
+ });
464
+ }
465
+
466
+ /**
467
+ * Default concurrency limiter for API calls
468
+ */
469
+ export function createDefaultConcurrencyLimiter(): ConcurrencyLimiter {
470
+ return new ConcurrencyLimiter(5);
471
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenCounter.d.ts","sourceRoot":"","sources":["TokenCounter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiIjE;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IACtC,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,YAAY,CAAQ;IAE5B,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,YAAY;IAOlC;;OAEG;IACH,OAAO,CAAC,YAAY;IAYpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,MAAM;IAyB5D;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,GAAE,MAAkB,GAAG,MAAM;IAsBvE;;OAEG;IACH,uBAAuB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,GAAE,MAAkB,GAAG,MAAM;IAsC/E;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,GAAE,MAAkB,GAAG,MAAM;IAc3E;;OAEG;IACH,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,MAAM;IAKhF;;;OAGG;IACH,eAAe,CACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAkB,EACzB,OAAO,GAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;IAsD1D;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIpD;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU;IAOzE;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB;AAGD,eAAO,MAAM,YAAY,cAA6B,CAAC;AAGvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/E"}