@compilr-dev/agents 0.3.20 → 0.3.22

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.
@@ -352,7 +352,7 @@ export class ClaudeProvider {
352
352
  return new ProviderError('Rate limit exceeded', 'claude', 429, error);
353
353
  }
354
354
  if (error instanceof Anthropic.AuthenticationError) {
355
- return new ProviderError('Authentication failed: Invalid API key', 'claude', 401, error);
355
+ return new ProviderError(`Authentication failed: Invalid API key. (${error.message})`, 'claude', 401, error);
356
356
  }
357
357
  if (error instanceof Error) {
358
358
  return new ProviderError(error.message, 'claude', undefined, error);
@@ -96,9 +96,9 @@ export class FireworksProvider extends OpenAICompatibleProvider {
96
96
  }
97
97
  switch (status) {
98
98
  case 401:
99
- return new ProviderError('Invalid Fireworks AI API key. Check your FIREWORKS_API_KEY.', 'fireworks', 401);
99
+ return new ProviderError(`Invalid Fireworks AI API key. Check your FIREWORKS_API_KEY. (${message})`, 'fireworks', 401);
100
100
  case 403:
101
- return new ProviderError('Access denied. Check your Fireworks AI API key permissions.', 'fireworks', 403);
101
+ return new ProviderError(`Access denied. Check your Fireworks AI API key permissions. (${message})`, 'fireworks', 403);
102
102
  case 429:
103
103
  return new ProviderError('Fireworks AI rate limit exceeded. Please wait and try again.', 'fireworks', 429);
104
104
  case 500:
@@ -93,9 +93,9 @@ export class GeminiProvider extends OpenAICompatibleProvider {
93
93
  case 400:
94
94
  return new ProviderError(`Invalid request to Gemini API: ${message}`, 'gemini', 400);
95
95
  case 401:
96
- return new ProviderError('Invalid Gemini API key. Check your GOOGLE_AI_API_KEY or GEMINI_API_KEY.', 'gemini', 401);
96
+ return new ProviderError(`Invalid Gemini API key. Check your GOOGLE_AI_API_KEY or GEMINI_API_KEY. (${message})`, 'gemini', 401);
97
97
  case 403:
98
- return new ProviderError('Access denied to Gemini API. Check your API key permissions.', 'gemini', 403);
98
+ return new ProviderError(`Access denied to Gemini API. Check your API key permissions. (${message})`, 'gemini', 403);
99
99
  case 429:
100
100
  return new ProviderError('Gemini rate limit exceeded. Please wait and try again.', 'gemini', 429);
101
101
  case 500:
@@ -81,9 +81,9 @@ export class GroqProvider extends OpenAICompatibleProvider {
81
81
  }
82
82
  switch (status) {
83
83
  case 401:
84
- return new ProviderError('Invalid Groq API key. Check your GROQ_API_KEY.', 'groq', 401);
84
+ return new ProviderError(`Invalid Groq API key. Check your GROQ_API_KEY. (${message})`, 'groq', 401);
85
85
  case 403:
86
- return new ProviderError('Access denied. Check your Groq API key permissions.', 'groq', 403);
86
+ return new ProviderError(`Access denied. Check your Groq API key permissions. (${message})`, 'groq', 403);
87
87
  case 429:
88
88
  return new ProviderError('Groq rate limit exceeded. Please wait and try again.', 'groq', 429);
89
89
  case 500:
@@ -169,6 +169,16 @@ export declare abstract class OpenAICompatibleProvider implements LLMProvider {
169
169
  * Convert library messages to OpenAI format
170
170
  */
171
171
  protected convertMessages(messages: Message[]): OpenAIMessage[];
172
+ /**
173
+ * Ensure every assistant tool_call has a matching tool response message.
174
+ *
175
+ * OpenAI strictly requires that each tool_call_id in an assistant message
176
+ * is followed by a tool-role response before the next non-tool message.
177
+ * This can break after ToolLoopError (partial results) or context
178
+ * compaction (message reordering). We add synthetic error responses
179
+ * for any orphaned tool_calls.
180
+ */
181
+ protected repairOpenAIToolPairing(messages: OpenAIMessage[]): OpenAIMessage[];
172
182
  /**
173
183
  * Map library role to OpenAI role
174
184
  */
@@ -271,7 +271,47 @@ export class OpenAICompatibleProvider {
271
271
  }
272
272
  }
273
273
  }
274
- return result;
274
+ return this.repairOpenAIToolPairing(result);
275
+ }
276
+ /**
277
+ * Ensure every assistant tool_call has a matching tool response message.
278
+ *
279
+ * OpenAI strictly requires that each tool_call_id in an assistant message
280
+ * is followed by a tool-role response before the next non-tool message.
281
+ * This can break after ToolLoopError (partial results) or context
282
+ * compaction (message reordering). We add synthetic error responses
283
+ * for any orphaned tool_calls.
284
+ */
285
+ repairOpenAIToolPairing(messages) {
286
+ const repaired = [];
287
+ for (let i = 0; i < messages.length; i++) {
288
+ const msg = messages[i];
289
+ repaired.push(msg);
290
+ // Only check assistant messages with tool_calls
291
+ if (msg.role !== 'assistant' || !msg.tool_calls || msg.tool_calls.length === 0) {
292
+ continue;
293
+ }
294
+ // Collect expected tool_call_ids
295
+ const expectedIds = new Set(msg.tool_calls.map((tc) => tc.id));
296
+ // Push all consecutive tool messages first, tracking which IDs are satisfied
297
+ while (i + 1 < messages.length && messages[i + 1].role === 'tool') {
298
+ i++;
299
+ const toolMsg = messages[i];
300
+ repaired.push(toolMsg);
301
+ if (toolMsg.tool_call_id) {
302
+ expectedIds.delete(toolMsg.tool_call_id);
303
+ }
304
+ }
305
+ // Add synthetic responses for any missing tool_call_ids
306
+ for (const missingId of expectedIds) {
307
+ repaired.push({
308
+ role: 'tool',
309
+ tool_call_id: missingId,
310
+ content: '[Error: Tool execution was interrupted]',
311
+ });
312
+ }
313
+ }
314
+ return repaired;
275
315
  }
276
316
  /**
277
317
  * Map library role to OpenAI role
@@ -87,9 +87,9 @@ export class OpenAIProvider extends OpenAICompatibleProvider {
87
87
  }
88
88
  switch (status) {
89
89
  case 401:
90
- return new ProviderError('Invalid OpenAI API key. Check your OPENAI_API_KEY.', 'openai', 401);
90
+ return new ProviderError(`Invalid OpenAI API key. Check your OPENAI_API_KEY. (${message})`, 'openai', 401);
91
91
  case 403:
92
- return new ProviderError('Access denied. Check your OpenAI API key permissions.', 'openai', 403);
92
+ return new ProviderError(`Access denied. Check your OpenAI API key permissions. (${message})`, 'openai', 403);
93
93
  case 429:
94
94
  return new ProviderError('OpenAI rate limit exceeded. Please wait and try again.', 'openai', 429);
95
95
  case 500:
@@ -94,9 +94,9 @@ export class OpenRouterProvider extends OpenAICompatibleProvider {
94
94
  }
95
95
  switch (status) {
96
96
  case 401:
97
- return new ProviderError('Invalid OpenRouter API key. Check your OPENROUTER_API_KEY.', 'openrouter', 401);
97
+ return new ProviderError(`Invalid OpenRouter API key. Check your OPENROUTER_API_KEY. (${message})`, 'openrouter', 401);
98
98
  case 403:
99
- return new ProviderError('Access denied. Check your OpenRouter API key permissions or credits.', 'openrouter', 403);
99
+ return new ProviderError(`Access denied. Check your OpenRouter API key permissions or credits. (${message})`, 'openrouter', 403);
100
100
  case 429:
101
101
  return new ProviderError('OpenRouter rate limit exceeded. Please wait and try again.', 'openrouter', 429);
102
102
  case 500:
@@ -81,9 +81,9 @@ export class PerplexityProvider extends OpenAICompatibleProvider {
81
81
  }
82
82
  switch (status) {
83
83
  case 401:
84
- return new ProviderError('Invalid Perplexity API key. Check your PERPLEXITY_API_KEY.', 'perplexity', 401);
84
+ return new ProviderError(`Invalid Perplexity API key. Check your PERPLEXITY_API_KEY. (${message})`, 'perplexity', 401);
85
85
  case 403:
86
- return new ProviderError('Access denied. Check your Perplexity API key permissions.', 'perplexity', 403);
86
+ return new ProviderError(`Access denied. Check your Perplexity API key permissions. (${message})`, 'perplexity', 403);
87
87
  case 429:
88
88
  return new ProviderError('Perplexity rate limit exceeded. Please wait and try again.', 'perplexity', 429);
89
89
  case 500:
@@ -81,9 +81,9 @@ export class TogetherProvider extends OpenAICompatibleProvider {
81
81
  }
82
82
  switch (status) {
83
83
  case 401:
84
- return new ProviderError('Invalid Together AI API key. Check your TOGETHER_API_KEY.', 'together', 401);
84
+ return new ProviderError(`Invalid Together AI API key. Check your TOGETHER_API_KEY. (${message})`, 'together', 401);
85
85
  case 403:
86
- return new ProviderError('Access denied. Check your Together AI API key permissions.', 'together', 403);
86
+ return new ProviderError(`Access denied. Check your Together AI API key permissions. (${message})`, 'together', 403);
87
87
  case 429:
88
88
  return new ProviderError('Together AI rate limit exceeded. Please wait and try again.', 'together', 429);
89
89
  case 500:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/agents",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "Lightweight multi-LLM agent library for building CLI AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",