@mobileai/react-native 0.9.18 → 0.9.20

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 (81) hide show
  1. package/LICENSE +28 -20
  2. package/MobileAIFloatingOverlay.podspec +25 -0
  3. package/android/build.gradle +61 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/mobileai/overlay/FloatingOverlayView.kt +151 -0
  6. package/android/src/main/java/com/mobileai/overlay/MobileAIOverlayPackage.kt +23 -0
  7. package/android/src/newarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +45 -0
  8. package/android/src/oldarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +29 -0
  9. package/ios/MobileAIFloatingOverlayComponentView.mm +73 -0
  10. package/lib/module/components/AIAgent.js +902 -136
  11. package/lib/module/components/AIConsentDialog.js +439 -0
  12. package/lib/module/components/AgentChatBar.js +828 -134
  13. package/lib/module/components/AgentOverlay.js +2 -1
  14. package/lib/module/components/DiscoveryTooltip.js +21 -9
  15. package/lib/module/components/FloatingOverlayWrapper.js +108 -0
  16. package/lib/module/components/Icons.js +123 -0
  17. package/lib/module/config/endpoints.js +12 -2
  18. package/lib/module/core/AgentRuntime.js +373 -27
  19. package/lib/module/core/FiberAdapter.js +56 -0
  20. package/lib/module/core/FiberTreeWalker.js +186 -80
  21. package/lib/module/core/IdleDetector.js +19 -0
  22. package/lib/module/core/NativeAlertInterceptor.js +191 -0
  23. package/lib/module/core/systemPrompt.js +203 -45
  24. package/lib/module/index.js +3 -0
  25. package/lib/module/providers/GeminiProvider.js +72 -56
  26. package/lib/module/providers/ProviderFactory.js +6 -2
  27. package/lib/module/services/AudioInputService.js +3 -12
  28. package/lib/module/services/AudioOutputService.js +1 -13
  29. package/lib/module/services/ConversationService.js +166 -0
  30. package/lib/module/services/MobileAIKnowledgeRetriever.js +41 -0
  31. package/lib/module/services/VoiceService.js +29 -8
  32. package/lib/module/services/telemetry/MobileAI.js +44 -0
  33. package/lib/module/services/telemetry/TelemetryService.js +13 -1
  34. package/lib/module/services/telemetry/TouchAutoCapture.js +44 -18
  35. package/lib/module/specs/FloatingOverlayNativeComponent.ts +19 -0
  36. package/lib/module/support/CSATSurvey.js +95 -12
  37. package/lib/module/support/EscalationSocket.js +70 -1
  38. package/lib/module/support/ReportedIssueEventSource.js +148 -0
  39. package/lib/module/support/escalateTool.js +4 -2
  40. package/lib/module/support/index.js +1 -0
  41. package/lib/module/support/reportIssueTool.js +127 -0
  42. package/lib/module/support/supportPrompt.js +77 -9
  43. package/lib/module/tools/guideTool.js +2 -1
  44. package/lib/module/tools/longPressTool.js +4 -3
  45. package/lib/module/tools/pickerTool.js +6 -4
  46. package/lib/module/tools/tapTool.js +12 -3
  47. package/lib/module/tools/typeTool.js +19 -10
  48. package/lib/module/utils/logger.js +175 -6
  49. package/lib/typescript/react-native.config.d.ts +11 -0
  50. package/lib/typescript/src/components/AIAgent.d.ts +28 -2
  51. package/lib/typescript/src/components/AIConsentDialog.d.ts +153 -0
  52. package/lib/typescript/src/components/AgentChatBar.d.ts +15 -2
  53. package/lib/typescript/src/components/DiscoveryTooltip.d.ts +3 -1
  54. package/lib/typescript/src/components/FloatingOverlayWrapper.d.ts +51 -0
  55. package/lib/typescript/src/components/Icons.d.ts +8 -0
  56. package/lib/typescript/src/config/endpoints.d.ts +5 -3
  57. package/lib/typescript/src/core/AgentRuntime.d.ts +4 -0
  58. package/lib/typescript/src/core/FiberAdapter.d.ts +25 -0
  59. package/lib/typescript/src/core/FiberTreeWalker.d.ts +2 -0
  60. package/lib/typescript/src/core/IdleDetector.d.ts +11 -0
  61. package/lib/typescript/src/core/NativeAlertInterceptor.d.ts +55 -0
  62. package/lib/typescript/src/core/types.d.ts +106 -1
  63. package/lib/typescript/src/index.d.ts +9 -4
  64. package/lib/typescript/src/providers/GeminiProvider.d.ts +6 -5
  65. package/lib/typescript/src/services/ConversationService.d.ts +55 -0
  66. package/lib/typescript/src/services/MobileAIKnowledgeRetriever.d.ts +9 -0
  67. package/lib/typescript/src/services/telemetry/MobileAI.d.ts +7 -0
  68. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts +1 -1
  69. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts +9 -6
  70. package/lib/typescript/src/services/telemetry/types.d.ts +3 -1
  71. package/lib/typescript/src/specs/FloatingOverlayNativeComponent.d.ts +17 -0
  72. package/lib/typescript/src/support/EscalationSocket.d.ts +17 -0
  73. package/lib/typescript/src/support/ReportedIssueEventSource.d.ts +24 -0
  74. package/lib/typescript/src/support/escalateTool.d.ts +5 -0
  75. package/lib/typescript/src/support/index.d.ts +2 -1
  76. package/lib/typescript/src/support/reportIssueTool.d.ts +20 -0
  77. package/lib/typescript/src/support/types.d.ts +56 -1
  78. package/lib/typescript/src/utils/logger.d.ts +15 -0
  79. package/package.json +20 -5
  80. package/react-native.config.js +12 -0
  81. package/src/specs/FloatingOverlayNativeComponent.ts +19 -0
@@ -0,0 +1,439 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * AIConsentDialog — Apple App Store Guideline 5.1.2(i) compliant consent flow.
5
+ *
6
+ * Displays a modal before the first AI interaction that:
7
+ * 1. Names the specific third-party AI provider (e.g., "Google Gemini")
8
+ * 2. Explains what data is shared (screen content, messages)
9
+ * 3. Collects explicit user consent via affirmative tap
10
+ *
11
+ * Persists consent via AsyncStorage so the dialog is shown once per device.
12
+ * If AsyncStorage is unavailable, consent is session-scoped (per app launch).
13
+ *
14
+ * ## Business rationale
15
+ * Apple rejects apps that silently send personal data to third-party AI services.
16
+ * This component ensures compliance WITHOUT the app developer needing to build
17
+ * their own consent flow — they just set `requireConsent={true}` on <AIAgent>.
18
+ */
19
+
20
+ import React, { useCallback, useEffect, useState } from 'react';
21
+ import { View, Text, Pressable, Modal, StyleSheet, Linking, Animated } from 'react-native';
22
+ import { isNativeOverlayActive } from "./FloatingOverlayWrapper.js";
23
+
24
+ // ─── AsyncStorage Helper ──────────────────────────────────────
25
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
+ const CONSENT_STORAGE_KEY = '@mobileai_ai_consent_granted';
27
+ function getStorage() {
28
+ try {
29
+ const origError = console.error;
30
+ console.error = (...args) => {
31
+ const msg = args[0];
32
+ if (typeof msg === 'string' && msg.includes('AsyncStorage')) return;
33
+ origError.apply(console, args);
34
+ };
35
+ try {
36
+ const mod = require('@react-native-async-storage/async-storage');
37
+ const candidate = mod?.default ?? mod?.AsyncStorage ?? null;
38
+ if (candidate && typeof candidate.getItem === 'function') return candidate;
39
+ return null;
40
+ } finally {
41
+ console.error = origError;
42
+ }
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ // ─── Provider Display Names ───────────────────────────────────
49
+
50
+ const PROVIDER_INFO = {
51
+ gemini: {
52
+ name: 'Google Gemini',
53
+ company: 'Google',
54
+ url: 'https://ai.google.dev/terms'
55
+ },
56
+ openai: {
57
+ name: 'OpenAI GPT',
58
+ company: 'OpenAI',
59
+ url: 'https://openai.com/policies/terms-of-use'
60
+ }
61
+ };
62
+ const DEFAULT_THEME = {
63
+ backdrop: 'rgba(9, 12, 16, 0.52)',
64
+ cardBackground: '#ffffff',
65
+ cardBorder: 'rgba(15, 23, 42, 0.08)',
66
+ iconBackground: '#eef4ff',
67
+ iconColor: '#3156d3',
68
+ title: '#111827',
69
+ body: '#4b5563',
70
+ muted: '#6b7280',
71
+ sectionBackground: '#f8fafc',
72
+ sectionBorder: 'rgba(148, 163, 184, 0.22)',
73
+ bullet: '#3156d3',
74
+ badgeBackground: '#eef4ff',
75
+ badgeText: '#3156d3',
76
+ secondaryButtonBackground: '#f3f4f6',
77
+ secondaryButtonText: '#374151',
78
+ primaryButtonBackground: '#111827',
79
+ primaryButtonText: '#ffffff',
80
+ link: '#3156d3'
81
+ };
82
+
83
+ // ─── Public Types ─────────────────────────────────────────────
84
+
85
+ // ─── Props ────────────────────────────────────────────────────
86
+
87
+ // ─── Component ────────────────────────────────────────────────
88
+
89
+ export function AIConsentDialog({
90
+ visible,
91
+ provider,
92
+ config,
93
+ onConsent,
94
+ onDecline,
95
+ language = 'en'
96
+ }) {
97
+ const fadeAnim = React.useRef(new Animated.Value(0)).current;
98
+ const isArabic = language === 'ar';
99
+ const providerInfo = PROVIDER_INFO[provider] || PROVIDER_INFO.gemini;
100
+ const theme = {
101
+ ...DEFAULT_THEME,
102
+ ...(config.theme || {})
103
+ };
104
+ const providerLabel = config.providerLabel || (isArabic ? 'خدمة الذكاء الاصطناعي المفعلة في التطبيق' : 'the AI service configured for this app');
105
+ const providerUrl = config.providerUrl || providerInfo.url;
106
+ const showProviderBadge = config.showProviderBadge === true;
107
+ useEffect(() => {
108
+ if (visible) {
109
+ Animated.timing(fadeAnim, {
110
+ toValue: 1,
111
+ duration: 250,
112
+ useNativeDriver: true
113
+ }).start();
114
+ }
115
+ }, [visible, fadeAnim]);
116
+ const title = isArabic ? config.titleAr || 'مساعد الذكاء الاصطناعي' : config.title || 'AI Assistant';
117
+ const sharedDataItems = isArabic ? config.sharedDataItemsAr || ['رسالتك', 'نقدر خصوصيتك، لا نرى شاشتك بأي شكل، نحن ندرك فقط سياق التطبيق الحالي'] : config.sharedDataItems || ['Your message', "We appreciate your privacy, we don't see your screen in any way, we are just aware of current app context"];
118
+ const handlePrivacyPolicy = useCallback(() => {
119
+ if (config.privacyPolicyUrl) {
120
+ Linking.openURL(config.privacyPolicyUrl);
121
+ }
122
+ }, [config.privacyPolicyUrl]);
123
+ const ContentWrapper = isNativeOverlayActive ? View : Modal;
124
+ const wrapperProps = isNativeOverlayActive ? {
125
+ style: [StyleSheet.absoluteFill, {
126
+ zIndex: 99999,
127
+ elevation: 99999
128
+ }],
129
+ pointerEvents: 'auto'
130
+ } : {
131
+ visible,
132
+ transparent: true,
133
+ animationType: "none",
134
+ statusBarTranslucent: true,
135
+ onRequestClose: onDecline
136
+ };
137
+ if (!visible) return null;
138
+ return /*#__PURE__*/_jsx(ContentWrapper, {
139
+ ...wrapperProps,
140
+ children: /*#__PURE__*/_jsx(View, {
141
+ style: [dialogStyles.backdrop, {
142
+ backgroundColor: theme.backdrop
143
+ }],
144
+ children: /*#__PURE__*/_jsxs(Animated.View, {
145
+ style: [dialogStyles.card, {
146
+ opacity: fadeAnim,
147
+ backgroundColor: theme.cardBackground,
148
+ borderColor: theme.cardBorder
149
+ }],
150
+ children: [/*#__PURE__*/_jsx(View, {
151
+ style: [dialogStyles.iconWrap, {
152
+ backgroundColor: theme.iconBackground
153
+ }],
154
+ children: /*#__PURE__*/_jsx(Text, {
155
+ style: [dialogStyles.iconText, {
156
+ color: theme.iconColor
157
+ }],
158
+ children: "\u25CE"
159
+ })
160
+ }), /*#__PURE__*/_jsx(Text, {
161
+ style: [dialogStyles.title, {
162
+ color: theme.title
163
+ }, isArabic && dialogStyles.textRTL],
164
+ children: title
165
+ }), /*#__PURE__*/_jsxs(View, {
166
+ style: [dialogStyles.dataSection, {
167
+ backgroundColor: theme.sectionBackground,
168
+ borderColor: theme.sectionBorder
169
+ }],
170
+ children: [/*#__PURE__*/_jsx(Text, {
171
+ style: [dialogStyles.dataLabel, {
172
+ color: theme.muted
173
+ }, isArabic && dialogStyles.textRTL],
174
+ children: isArabic ? 'يُشارك مع مساعد الذكاء الاصطناعي:' : 'Shared with the AI agent:'
175
+ }), sharedDataItems.map((item, index) => /*#__PURE__*/_jsxs(View, {
176
+ style: dialogStyles.dataItem,
177
+ children: [/*#__PURE__*/_jsx(Text, {
178
+ style: [dialogStyles.dataBullet, {
179
+ color: theme.bullet
180
+ }],
181
+ children: "\u2022"
182
+ }), /*#__PURE__*/_jsx(Text, {
183
+ style: [dialogStyles.dataText, {
184
+ color: theme.body
185
+ }, isArabic && dialogStyles.textRTL],
186
+ children: item
187
+ })]
188
+ }, `${item}-${index}`))]
189
+ }), showProviderBadge && /*#__PURE__*/_jsx(View, {
190
+ style: [dialogStyles.providerBadge, {
191
+ backgroundColor: theme.badgeBackground
192
+ }],
193
+ children: /*#__PURE__*/_jsx(Text, {
194
+ style: [dialogStyles.providerBadgeText, {
195
+ color: theme.badgeText
196
+ }],
197
+ children: config.providerBadgeText || (isArabic ? `تعمل بواسطة ${providerLabel}` : `Uses ${providerLabel}`)
198
+ })
199
+ }), config.privacyPolicyUrl && /*#__PURE__*/_jsx(Pressable, {
200
+ onPress: handlePrivacyPolicy,
201
+ style: dialogStyles.privacyLink,
202
+ children: /*#__PURE__*/_jsx(Text, {
203
+ style: [dialogStyles.privacyLinkText, {
204
+ color: theme.link
205
+ }],
206
+ children: isArabic ? 'سياسة الخصوصية' : 'Privacy Policy'
207
+ })
208
+ }), !config.privacyPolicyUrl && providerUrl && showProviderBadge && /*#__PURE__*/_jsx(Pressable, {
209
+ onPress: () => Linking.openURL(providerUrl),
210
+ style: dialogStyles.privacyLink,
211
+ children: /*#__PURE__*/_jsx(Text, {
212
+ style: [dialogStyles.privacyLinkText, {
213
+ color: theme.link
214
+ }],
215
+ children: isArabic ? 'معلومات إضافية' : 'Learn more'
216
+ })
217
+ }), /*#__PURE__*/_jsxs(View, {
218
+ style: dialogStyles.buttonRow,
219
+ children: [/*#__PURE__*/_jsx(Pressable, {
220
+ style: [dialogStyles.declineBtn, {
221
+ backgroundColor: theme.secondaryButtonBackground
222
+ }],
223
+ onPress: onDecline,
224
+ accessibilityLabel: isArabic ? 'رفض' : 'Decline',
225
+ children: /*#__PURE__*/_jsx(Text, {
226
+ style: [dialogStyles.declineBtnText, {
227
+ color: theme.secondaryButtonText
228
+ }],
229
+ children: isArabic ? 'ليس الآن' : 'Not now'
230
+ })
231
+ }), /*#__PURE__*/_jsx(Pressable, {
232
+ style: [dialogStyles.consentBtn, {
233
+ backgroundColor: theme.primaryButtonBackground
234
+ }],
235
+ onPress: onConsent,
236
+ accessibilityLabel: isArabic ? 'متابعة' : 'Continue',
237
+ children: /*#__PURE__*/_jsx(Text, {
238
+ style: [dialogStyles.consentBtnText, {
239
+ color: theme.primaryButtonText
240
+ }],
241
+ children: isArabic ? 'متابعة' : 'Continue'
242
+ })
243
+ })]
244
+ })]
245
+ })
246
+ })
247
+ });
248
+ }
249
+
250
+ // ─── Hook: useAIConsent ───────────────────────────────────────
251
+
252
+ /**
253
+ * Manages consent state persistence via AsyncStorage.
254
+ * Falls back to session-scoped state if AsyncStorage is unavailable.
255
+ *
256
+ * @returns [hasConsented, grantConsent, revokeConsent, isLoading]
257
+ */
258
+ export function useAIConsent(persist = false) {
259
+ const [hasConsented, setHasConsented] = useState(false);
260
+ const [isLoading, setIsLoading] = useState(true);
261
+
262
+ // Load persisted consent on mount (ONLY if persist is true)
263
+ useEffect(() => {
264
+ void (async () => {
265
+ try {
266
+ if (!persist) return;
267
+ const AS = getStorage();
268
+ if (AS) {
269
+ const stored = await AS.getItem(CONSENT_STORAGE_KEY);
270
+ if (stored === 'true') {
271
+ setHasConsented(true);
272
+ }
273
+ }
274
+ } catch {
275
+ // No persisted consent — will prompt
276
+ } finally {
277
+ setIsLoading(false);
278
+ }
279
+ })();
280
+ }, [persist]);
281
+ const grantConsent = useCallback(async () => {
282
+ setHasConsented(true);
283
+ try {
284
+ if (!persist) return;
285
+ const AS = getStorage();
286
+ await AS?.setItem(CONSENT_STORAGE_KEY, 'true');
287
+ } catch {
288
+ // Consent granted session-only
289
+ }
290
+ }, [persist]);
291
+ const revokeConsent = useCallback(async () => {
292
+ setHasConsented(false);
293
+ try {
294
+ if (!persist) return;
295
+ const AS = getStorage();
296
+ await AS?.removeItem(CONSENT_STORAGE_KEY);
297
+ } catch {
298
+ // Best effort
299
+ }
300
+ }, [persist]);
301
+ return [hasConsented, grantConsent, revokeConsent, isLoading];
302
+ }
303
+
304
+ // ─── Styles ───────────────────────────────────────────────────
305
+
306
+ const dialogStyles = StyleSheet.create({
307
+ backdrop: {
308
+ flex: 1,
309
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
310
+ justifyContent: 'center',
311
+ alignItems: 'center',
312
+ padding: 24
313
+ },
314
+ card: {
315
+ borderRadius: 20,
316
+ padding: 24,
317
+ width: '100%',
318
+ maxWidth: 380,
319
+ alignItems: 'center',
320
+ borderWidth: 1,
321
+ shadowColor: '#0f172a',
322
+ shadowOffset: {
323
+ width: 0,
324
+ height: 4
325
+ },
326
+ shadowOpacity: 0.12,
327
+ shadowRadius: 22,
328
+ elevation: 10
329
+ },
330
+ iconWrap: {
331
+ width: 56,
332
+ height: 56,
333
+ borderRadius: 28,
334
+ backgroundColor: 'rgba(123, 104, 238, 0.15)',
335
+ alignItems: 'center',
336
+ justifyContent: 'center',
337
+ marginBottom: 16
338
+ },
339
+ iconText: {
340
+ fontSize: 26,
341
+ fontWeight: '700'
342
+ },
343
+ title: {
344
+ fontSize: 20,
345
+ fontWeight: '700',
346
+ color: '#ffffff',
347
+ marginBottom: 12,
348
+ textAlign: 'center'
349
+ },
350
+ body: {
351
+ fontSize: 14,
352
+ lineHeight: 20,
353
+ textAlign: 'center',
354
+ marginBottom: 10
355
+ },
356
+ summary: {
357
+ fontSize: 13,
358
+ lineHeight: 19,
359
+ textAlign: 'center',
360
+ marginBottom: 16
361
+ },
362
+ textRTL: {
363
+ textAlign: 'right',
364
+ writingDirection: 'rtl'
365
+ },
366
+ dataSection: {
367
+ width: '100%',
368
+ borderRadius: 12,
369
+ padding: 14,
370
+ marginBottom: 14,
371
+ borderWidth: 1
372
+ },
373
+ dataLabel: {
374
+ fontSize: 12,
375
+ fontWeight: '600',
376
+ color: 'rgba(255, 255, 255, 0.5)',
377
+ textTransform: 'uppercase',
378
+ letterSpacing: 0.5,
379
+ marginBottom: 8
380
+ },
381
+ dataItem: {
382
+ flexDirection: 'row',
383
+ alignItems: 'flex-start',
384
+ marginBottom: 4
385
+ },
386
+ dataBullet: {
387
+ color: '#7B68EE',
388
+ fontSize: 14,
389
+ marginRight: 8,
390
+ marginTop: 1
391
+ },
392
+ dataText: {
393
+ fontSize: 13,
394
+ flex: 1
395
+ },
396
+ providerBadge: {
397
+ borderRadius: 8,
398
+ paddingHorizontal: 12,
399
+ paddingVertical: 6,
400
+ marginBottom: 12
401
+ },
402
+ providerBadgeText: {
403
+ fontSize: 12,
404
+ fontWeight: '600'
405
+ },
406
+ privacyLink: {
407
+ marginBottom: 20
408
+ },
409
+ privacyLinkText: {
410
+ fontSize: 13,
411
+ textDecorationLine: 'underline'
412
+ },
413
+ buttonRow: {
414
+ flexDirection: 'row',
415
+ gap: 12,
416
+ width: '100%'
417
+ },
418
+ declineBtn: {
419
+ flex: 1,
420
+ paddingVertical: 14,
421
+ borderRadius: 12,
422
+ alignItems: 'center'
423
+ },
424
+ declineBtnText: {
425
+ fontSize: 15,
426
+ fontWeight: '600'
427
+ },
428
+ consentBtn: {
429
+ flex: 1,
430
+ paddingVertical: 14,
431
+ borderRadius: 12,
432
+ alignItems: 'center'
433
+ },
434
+ consentBtnText: {
435
+ fontSize: 15,
436
+ fontWeight: '700'
437
+ }
438
+ });
439
+ //# sourceMappingURL=AIConsentDialog.js.map