@compilr-dev/agents 0.3.1 → 0.3.4

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.
Files changed (44) hide show
  1. package/dist/agent.d.ts +107 -2
  2. package/dist/agent.js +151 -22
  3. package/dist/context/manager.d.ts +8 -0
  4. package/dist/context/manager.js +25 -2
  5. package/dist/errors.d.ts +20 -1
  6. package/dist/errors.js +44 -2
  7. package/dist/index.d.ts +16 -1
  8. package/dist/index.js +13 -1
  9. package/dist/messages/index.d.ts +12 -5
  10. package/dist/messages/index.js +53 -15
  11. package/dist/providers/claude.js +7 -0
  12. package/dist/providers/fireworks.d.ts +86 -0
  13. package/dist/providers/fireworks.js +123 -0
  14. package/dist/providers/gemini-native.d.ts +86 -0
  15. package/dist/providers/gemini-native.js +374 -0
  16. package/dist/providers/groq.d.ts +86 -0
  17. package/dist/providers/groq.js +123 -0
  18. package/dist/providers/index.d.ts +17 -2
  19. package/dist/providers/index.js +13 -2
  20. package/dist/providers/openai-compatible.js +12 -1
  21. package/dist/providers/openrouter.d.ts +95 -0
  22. package/dist/providers/openrouter.js +138 -0
  23. package/dist/providers/perplexity.d.ts +86 -0
  24. package/dist/providers/perplexity.js +123 -0
  25. package/dist/providers/together.d.ts +86 -0
  26. package/dist/providers/together.js +123 -0
  27. package/dist/providers/types.d.ts +19 -0
  28. package/dist/state/agent-state.d.ts +1 -0
  29. package/dist/state/agent-state.js +2 -0
  30. package/dist/state/serializer.js +20 -2
  31. package/dist/state/types.d.ts +5 -0
  32. package/dist/tools/builtin/ask-user-simple.js +1 -0
  33. package/dist/tools/builtin/ask-user.js +1 -0
  34. package/dist/tools/builtin/bash.js +123 -2
  35. package/dist/tools/builtin/shell-manager.d.ts +15 -0
  36. package/dist/tools/builtin/shell-manager.js +51 -0
  37. package/dist/tools/builtin/suggest.js +1 -0
  38. package/dist/tools/builtin/todo.js +2 -0
  39. package/dist/tools/define.d.ts +6 -0
  40. package/dist/tools/define.js +1 -0
  41. package/dist/tools/types.d.ts +19 -0
  42. package/dist/utils/index.d.ts +119 -4
  43. package/dist/utils/index.js +164 -13
  44. package/package.json +7 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Utility functions
2
+ * Utility functions for @compilr-dev/agents
3
3
  */
4
4
  /**
5
5
  * Generate a unique ID for tool uses
@@ -8,30 +8,181 @@ export function generateId() {
8
8
  return `toolu_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
9
9
  }
10
10
  /**
11
- * Sleep for a specified duration
11
+ * Sleep for a specified duration, respecting AbortSignal
12
+ *
13
+ * @param ms - Duration in milliseconds
14
+ * @param signal - Optional AbortSignal to cancel sleep
15
+ * @throws Error if signal is aborted
12
16
  */
13
- export function sleep(ms) {
14
- return new Promise((resolve) => setTimeout(resolve, ms));
17
+ export function sleep(ms, signal) {
18
+ return new Promise((resolve, reject) => {
19
+ if (signal?.aborted) {
20
+ reject(new Error('Aborted'));
21
+ return;
22
+ }
23
+ const timeout = setTimeout(resolve, ms);
24
+ if (signal) {
25
+ const abortHandler = () => {
26
+ clearTimeout(timeout);
27
+ reject(new Error('Aborted'));
28
+ };
29
+ signal.addEventListener('abort', abortHandler, { once: true });
30
+ // Clean up abort listener when timer completes
31
+ const cleanup = () => {
32
+ signal.removeEventListener('abort', abortHandler);
33
+ resolve();
34
+ };
35
+ clearTimeout(timeout);
36
+ setTimeout(cleanup, ms);
37
+ }
38
+ });
15
39
  }
16
40
  /**
17
- * Retry a function with exponential backoff
41
+ * Default retry configuration
18
42
  */
19
- export async function retry(fn, options = {}) {
20
- const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000 } = options;
43
+ export const DEFAULT_RETRY_CONFIG = {
44
+ enabled: true,
45
+ maxAttempts: 10,
46
+ baseDelayMs: 1000,
47
+ maxDelayMs: 30000,
48
+ };
49
+ /**
50
+ * Calculate delay with exponential backoff and jitter
51
+ *
52
+ * @param attempt - Current attempt number (0-indexed)
53
+ * @param baseDelayMs - Base delay in milliseconds
54
+ * @param maxDelayMs - Maximum delay cap in milliseconds
55
+ * @returns Delay in milliseconds
56
+ */
57
+ export function calculateBackoffDelay(attempt, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs) {
58
+ // Exponential backoff: baseDelay * 2^attempt
59
+ const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
60
+ // Cap at maxDelay
61
+ const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
62
+ // Add jitter (0 to 50% of base delay)
63
+ const jitter = Math.random() * baseDelayMs * 0.5;
64
+ return Math.floor(cappedDelay + jitter);
65
+ }
66
+ /**
67
+ * Execute an async function with automatic retry on failure
68
+ *
69
+ * @param fn - Async function to execute
70
+ * @param options - Retry options
71
+ * @returns Result of the function
72
+ * @throws Last error if all retries exhausted
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const result = await withRetry(
77
+ * async () => {
78
+ * const response = await fetch(url);
79
+ * if (!response.ok) throw new Error(`HTTP ${response.status}`);
80
+ * return response.json();
81
+ * },
82
+ * {
83
+ * maxAttempts: 5,
84
+ * isRetryable: (error) => error.message.includes('HTTP 5'),
85
+ * onRetry: (attempt, max, error, delay) => {
86
+ * console.log(`Retry ${attempt}/${max} in ${delay}ms: ${error.message}`);
87
+ * },
88
+ * }
89
+ * );
90
+ * ```
91
+ */
92
+ export async function withRetry(fn, options = {}) {
93
+ const { maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs, isRetryable = () => true, onRetry, onExhausted, signal, } = options;
21
94
  let lastError;
22
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
95
+ let attempt = 0;
96
+ while (attempt <= maxAttempts) {
97
+ // Check for abort before each attempt
98
+ if (signal?.aborted) {
99
+ throw new Error('Aborted');
100
+ }
23
101
  try {
24
102
  return await fn();
25
103
  }
26
104
  catch (error) {
27
- lastError = error instanceof Error ? error : new Error(String(error));
28
- if (attempt < maxRetries) {
29
- const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
30
- await sleep(delay);
105
+ lastError = error;
106
+ // Check if error is retryable
107
+ if (!isRetryable(lastError)) {
108
+ throw lastError;
109
+ }
110
+ // Check if we have attempts remaining
111
+ if (attempt >= maxAttempts) {
112
+ onExhausted?.(attempt + 1, lastError);
113
+ throw lastError;
114
+ }
115
+ // Calculate delay and notify
116
+ const delayMs = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
117
+ onRetry?.(attempt + 1, maxAttempts, lastError, delayMs);
118
+ // Wait before next attempt
119
+ await sleep(delayMs, signal);
120
+ attempt++;
121
+ }
122
+ }
123
+ // This should never be reached, but TypeScript needs it
124
+ throw lastError ?? new Error('Retry failed with no error');
125
+ }
126
+ /**
127
+ * Create a retryable async generator for streaming responses
128
+ *
129
+ * This is specifically designed for streaming LLM responses where we need to
130
+ * retry the entire stream if it fails partway through.
131
+ *
132
+ * @param fn - Function that returns an async iterable
133
+ * @param options - Retry options
134
+ * @returns Async generator that retries on failure
135
+ */
136
+ export async function* withRetryGenerator(fn, options = {}) {
137
+ const { maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs, isRetryable = () => true, onRetry, onExhausted, signal, } = options;
138
+ let attempt = 0;
139
+ while (attempt <= maxAttempts) {
140
+ // Check for abort before each attempt
141
+ if (signal?.aborted) {
142
+ throw new Error('Aborted');
143
+ }
144
+ try {
145
+ // Yield all items from the generator
146
+ for await (const item of fn()) {
147
+ // Check for abort during iteration
148
+ if (signal?.aborted) {
149
+ throw new Error('Aborted');
150
+ }
151
+ yield item;
152
+ }
153
+ // Successfully completed, return
154
+ return;
155
+ }
156
+ catch (error) {
157
+ const typedError = error;
158
+ // Check if error is retryable
159
+ if (!isRetryable(typedError)) {
160
+ throw typedError;
161
+ }
162
+ // Check if we have attempts remaining
163
+ if (attempt >= maxAttempts) {
164
+ onExhausted?.(attempt + 1, typedError);
165
+ throw typedError;
31
166
  }
167
+ // Calculate delay and notify
168
+ const delayMs = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
169
+ onRetry?.(attempt + 1, maxAttempts, typedError, delayMs);
170
+ // Wait before next attempt
171
+ await sleep(delayMs, signal);
172
+ attempt++;
32
173
  }
33
174
  }
34
- throw lastError ?? new Error('Retry failed with no error captured');
175
+ }
176
+ /**
177
+ * @deprecated Use withRetry instead. This function is kept for backward compatibility.
178
+ */
179
+ export async function retry(fn, options = {}) {
180
+ const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000 } = options;
181
+ return withRetry(fn, {
182
+ maxAttempts: maxRetries,
183
+ baseDelayMs: baseDelay,
184
+ maxDelayMs: maxDelay,
185
+ });
35
186
  }
36
187
  /**
37
188
  * Truncate a string to a maximum length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/agents",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "description": "Lightweight multi-LLM agent library for building CLI AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -75,5 +75,11 @@
75
75
  "typescript": "^5.3.0",
76
76
  "typescript-eslint": "^8.48.0",
77
77
  "vitest": "^3.2.4"
78
+ },
79
+ "dependencies": {
80
+ "@google/genai": "^1.38.0"
81
+ },
82
+ "overrides": {
83
+ "hono": "^4.11.7"
78
84
  }
79
85
  }