@mobileai/react-native 0.8.8 → 0.9.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.
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native';
7
+ import { CloseIcon } from './Icons';
7
8
 
8
9
  interface AgentOverlayProps {
9
10
  visible: boolean;
@@ -25,7 +26,7 @@ export function AgentOverlay({ visible, statusText, onCancel }: AgentOverlayProp
25
26
  style={styles.cancelButton}
26
27
  hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
27
28
  >
28
- <Text style={styles.cancelText}>✕</Text>
29
+ <CloseIcon size={12} color="#fff" />
29
30
  </TouchableOpacity>
30
31
  )}
31
32
  </View>
@@ -50,11 +51,13 @@ const styles = StyleSheet.create({
50
51
  paddingHorizontal: 16,
51
52
  paddingVertical: 10,
52
53
  borderRadius: 20,
54
+ maxWidth: '85%',
53
55
  },
54
56
  text: {
55
57
  color: '#fff',
56
58
  fontSize: 14,
57
59
  fontWeight: '500',
60
+ flexShrink: 1,
58
61
  },
59
62
  cancelButton: {
60
63
  marginLeft: 4,
@@ -64,10 +67,7 @@ const styles = StyleSheet.create({
64
67
  backgroundColor: 'rgba(255, 255, 255, 0.2)',
65
68
  alignItems: 'center',
66
69
  justifyContent: 'center',
67
- },
68
- cancelText: {
69
- color: '#fff',
70
- fontSize: 12,
71
- fontWeight: '700',
70
+ flexShrink: 0,
72
71
  },
73
72
  });
73
+
@@ -1254,7 +1254,7 @@ ${screen.elementsText}
1254
1254
  // Dynamic status update based on tool being executed + Reasoning
1255
1255
  const statusLabel = this.getToolStatusLabel(toolCall.name, toolCall.args);
1256
1256
  // Prefer the human-readable plan over the raw tool status if available to avoid double statuses
1257
- const statusDisplay = reasoning.plan ? `🤔 ${reasoning.plan}` : `👆 ${statusLabel}`;
1257
+ const statusDisplay = reasoning.plan || statusLabel;
1258
1258
  this.config.onStatusUpdate?.(statusDisplay);
1259
1259
 
1260
1260
  // Find and execute the tool
@@ -102,9 +102,8 @@ export class GeminiProvider implements AIProvider {
102
102
  } catch (error: any) {
103
103
  logger.error('GeminiProvider', 'Request failed:', error.message);
104
104
 
105
- // Preserve HTTP error format for backward compatibility with tests
106
105
  if (error.status) {
107
- throw new Error(`Gemini API error ${error.status}: ${error.message}`);
106
+ throw new Error(this.formatProviderError(error.status, error.message));
108
107
  }
109
108
  throw error;
110
109
  }
@@ -315,4 +314,44 @@ export class GeminiProvider implements AIProvider {
315
314
 
316
315
  return { promptTokens, completionTokens, totalTokens, estimatedCostUSD };
317
316
  }
317
+
318
+ // ─── Error Formatting ──────────────────────────────────────
319
+
320
+ /**
321
+ * Converts raw API errors into clean, user-friendly messages.
322
+ * Parses JSON error bodies and maps HTTP codes to plain language.
323
+ */
324
+ private formatProviderError(status: number, rawMessage: string): string {
325
+ // Try to extract the human-readable message from JSON body
326
+ let humanMessage = '';
327
+ try {
328
+ const parsed = JSON.parse(rawMessage);
329
+ humanMessage = parsed?.error?.message || parsed?.message || '';
330
+ } catch {
331
+ // rawMessage may contain JSON embedded in a string like "503: {json}"
332
+ const jsonMatch = rawMessage.match(/\{[\s\S]*\}/);
333
+ if (jsonMatch) {
334
+ try {
335
+ const parsed = JSON.parse(jsonMatch[0]);
336
+ humanMessage = parsed?.error?.message || parsed?.message || '';
337
+ } catch { /* ignore */ }
338
+ }
339
+ }
340
+
341
+ // Map status codes to friendly descriptions
342
+ switch (status) {
343
+ case 429:
344
+ return humanMessage || 'Too many requests. Please wait a moment and try again.';
345
+ case 503:
346
+ return humanMessage || 'The AI service is temporarily unavailable. Please try again shortly.';
347
+ case 500:
348
+ return humanMessage || 'The AI service encountered an internal error. Please try again.';
349
+ case 401:
350
+ return 'Authentication failed. Please check your API key.';
351
+ case 403:
352
+ return 'Access denied. Your API key may not have the required permissions.';
353
+ default:
354
+ return humanMessage || `Something went wrong (${status}). Please try again.`;
355
+ }
356
+ }
318
357
  }