@mobileai/react-native 0.9.17 → 0.9.19

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 (292) 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 -9
  80. package/react-native.config.js +12 -0
  81. package/lib/module/__cli_tmp__.js.map +0 -1
  82. package/lib/module/components/AIAgent.js.map +0 -1
  83. package/lib/module/components/AIZone.js.map +0 -1
  84. package/lib/module/components/AgentChatBar.js.map +0 -1
  85. package/lib/module/components/AgentErrorBoundary.js.map +0 -1
  86. package/lib/module/components/AgentOverlay.js.map +0 -1
  87. package/lib/module/components/DiscoveryTooltip.js.map +0 -1
  88. package/lib/module/components/HighlightOverlay.js.map +0 -1
  89. package/lib/module/components/Icons.js.map +0 -1
  90. package/lib/module/components/ProactiveHint.js.map +0 -1
  91. package/lib/module/components/cards/InfoCard.js.map +0 -1
  92. package/lib/module/components/cards/ReviewSummary.js.map +0 -1
  93. package/lib/module/config/endpoints.js.map +0 -1
  94. package/lib/module/core/ActionRegistry.js.map +0 -1
  95. package/lib/module/core/AgentRuntime.js.map +0 -1
  96. package/lib/module/core/FiberTreeWalker.js.map +0 -1
  97. package/lib/module/core/IdleDetector.js.map +0 -1
  98. package/lib/module/core/MCPBridge.js.map +0 -1
  99. package/lib/module/core/ScreenDehydrator.js.map +0 -1
  100. package/lib/module/core/ZoneRegistry.js.map +0 -1
  101. package/lib/module/core/systemPrompt.js.map +0 -1
  102. package/lib/module/core/types.js.map +0 -1
  103. package/lib/module/hooks/useAction.js.map +0 -1
  104. package/lib/module/index.js.map +0 -1
  105. package/lib/module/plugin/withAppIntents.js.map +0 -1
  106. package/lib/module/providers/GeminiProvider.js.map +0 -1
  107. package/lib/module/providers/OpenAIProvider.js.map +0 -1
  108. package/lib/module/providers/ProviderFactory.js.map +0 -1
  109. package/lib/module/services/AudioInputService.js.map +0 -1
  110. package/lib/module/services/AudioOutputService.js.map +0 -1
  111. package/lib/module/services/KnowledgeBaseService.js.map +0 -1
  112. package/lib/module/services/VoiceService.js.map +0 -1
  113. package/lib/module/services/flags/FlagService.js.map +0 -1
  114. package/lib/module/services/telemetry/MobileAI.js.map +0 -1
  115. package/lib/module/services/telemetry/PiiScrubber.js.map +0 -1
  116. package/lib/module/services/telemetry/TelemetryService.js.map +0 -1
  117. package/lib/module/services/telemetry/TouchAutoCapture.js.map +0 -1
  118. package/lib/module/services/telemetry/device.js.map +0 -1
  119. package/lib/module/services/telemetry/deviceMetadata.js.map +0 -1
  120. package/lib/module/services/telemetry/index.js.map +0 -1
  121. package/lib/module/services/telemetry/types.js.map +0 -1
  122. package/lib/module/support/CSATSurvey.js.map +0 -1
  123. package/lib/module/support/EscalationEventSource.js.map +0 -1
  124. package/lib/module/support/EscalationSocket.js.map +0 -1
  125. package/lib/module/support/SupportChatModal.js.map +0 -1
  126. package/lib/module/support/SupportGreeting.js.map +0 -1
  127. package/lib/module/support/TicketStore.js.map +0 -1
  128. package/lib/module/support/escalateTool.js.map +0 -1
  129. package/lib/module/support/index.js.map +0 -1
  130. package/lib/module/support/supportPrompt.js.map +0 -1
  131. package/lib/module/support/types.js.map +0 -1
  132. package/lib/module/tools/datePickerTool.js.map +0 -1
  133. package/lib/module/tools/guideTool.js.map +0 -1
  134. package/lib/module/tools/index.js.map +0 -1
  135. package/lib/module/tools/keyboardTool.js.map +0 -1
  136. package/lib/module/tools/longPressTool.js.map +0 -1
  137. package/lib/module/tools/pickerTool.js.map +0 -1
  138. package/lib/module/tools/restoreTool.js.map +0 -1
  139. package/lib/module/tools/scrollTool.js.map +0 -1
  140. package/lib/module/tools/simplifyTool.js.map +0 -1
  141. package/lib/module/tools/sliderTool.js.map +0 -1
  142. package/lib/module/tools/tapTool.js.map +0 -1
  143. package/lib/module/tools/typeTool.js.map +0 -1
  144. package/lib/module/tools/types.js.map +0 -1
  145. package/lib/module/types/jsx.d.js.map +0 -1
  146. package/lib/module/utils/audioUtils.js.map +0 -1
  147. package/lib/module/utils/logger.js.map +0 -1
  148. package/lib/typescript/babel.config.d.ts.map +0 -1
  149. package/lib/typescript/bin/generate-map.d.cts.map +0 -1
  150. package/lib/typescript/eslint.config.d.mts.map +0 -1
  151. package/lib/typescript/generate-map.d.ts.map +0 -1
  152. package/lib/typescript/src/__cli_tmp__.d.ts.map +0 -1
  153. package/lib/typescript/src/components/AIAgent.d.ts.map +0 -1
  154. package/lib/typescript/src/components/AIZone.d.ts.map +0 -1
  155. package/lib/typescript/src/components/AgentChatBar.d.ts.map +0 -1
  156. package/lib/typescript/src/components/AgentErrorBoundary.d.ts.map +0 -1
  157. package/lib/typescript/src/components/AgentOverlay.d.ts.map +0 -1
  158. package/lib/typescript/src/components/DiscoveryTooltip.d.ts.map +0 -1
  159. package/lib/typescript/src/components/HighlightOverlay.d.ts.map +0 -1
  160. package/lib/typescript/src/components/Icons.d.ts.map +0 -1
  161. package/lib/typescript/src/components/ProactiveHint.d.ts.map +0 -1
  162. package/lib/typescript/src/components/cards/InfoCard.d.ts.map +0 -1
  163. package/lib/typescript/src/components/cards/ReviewSummary.d.ts.map +0 -1
  164. package/lib/typescript/src/config/endpoints.d.ts.map +0 -1
  165. package/lib/typescript/src/core/ActionRegistry.d.ts.map +0 -1
  166. package/lib/typescript/src/core/AgentRuntime.d.ts.map +0 -1
  167. package/lib/typescript/src/core/FiberTreeWalker.d.ts.map +0 -1
  168. package/lib/typescript/src/core/IdleDetector.d.ts.map +0 -1
  169. package/lib/typescript/src/core/MCPBridge.d.ts.map +0 -1
  170. package/lib/typescript/src/core/ScreenDehydrator.d.ts.map +0 -1
  171. package/lib/typescript/src/core/ZoneRegistry.d.ts.map +0 -1
  172. package/lib/typescript/src/core/systemPrompt.d.ts.map +0 -1
  173. package/lib/typescript/src/core/types.d.ts.map +0 -1
  174. package/lib/typescript/src/hooks/useAction.d.ts.map +0 -1
  175. package/lib/typescript/src/index.d.ts.map +0 -1
  176. package/lib/typescript/src/plugin/withAppIntents.d.ts.map +0 -1
  177. package/lib/typescript/src/providers/GeminiProvider.d.ts.map +0 -1
  178. package/lib/typescript/src/providers/OpenAIProvider.d.ts.map +0 -1
  179. package/lib/typescript/src/providers/ProviderFactory.d.ts.map +0 -1
  180. package/lib/typescript/src/services/AudioInputService.d.ts.map +0 -1
  181. package/lib/typescript/src/services/AudioOutputService.d.ts.map +0 -1
  182. package/lib/typescript/src/services/KnowledgeBaseService.d.ts.map +0 -1
  183. package/lib/typescript/src/services/VoiceService.d.ts.map +0 -1
  184. package/lib/typescript/src/services/flags/FlagService.d.ts.map +0 -1
  185. package/lib/typescript/src/services/telemetry/MobileAI.d.ts.map +0 -1
  186. package/lib/typescript/src/services/telemetry/PiiScrubber.d.ts.map +0 -1
  187. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts.map +0 -1
  188. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts.map +0 -1
  189. package/lib/typescript/src/services/telemetry/device.d.ts.map +0 -1
  190. package/lib/typescript/src/services/telemetry/deviceMetadata.d.ts.map +0 -1
  191. package/lib/typescript/src/services/telemetry/index.d.ts.map +0 -1
  192. package/lib/typescript/src/services/telemetry/types.d.ts.map +0 -1
  193. package/lib/typescript/src/support/CSATSurvey.d.ts.map +0 -1
  194. package/lib/typescript/src/support/EscalationEventSource.d.ts.map +0 -1
  195. package/lib/typescript/src/support/EscalationSocket.d.ts.map +0 -1
  196. package/lib/typescript/src/support/SupportChatModal.d.ts.map +0 -1
  197. package/lib/typescript/src/support/SupportGreeting.d.ts.map +0 -1
  198. package/lib/typescript/src/support/TicketStore.d.ts.map +0 -1
  199. package/lib/typescript/src/support/escalateTool.d.ts.map +0 -1
  200. package/lib/typescript/src/support/index.d.ts.map +0 -1
  201. package/lib/typescript/src/support/supportPrompt.d.ts.map +0 -1
  202. package/lib/typescript/src/support/types.d.ts.map +0 -1
  203. package/lib/typescript/src/tools/datePickerTool.d.ts.map +0 -1
  204. package/lib/typescript/src/tools/guideTool.d.ts.map +0 -1
  205. package/lib/typescript/src/tools/index.d.ts.map +0 -1
  206. package/lib/typescript/src/tools/keyboardTool.d.ts.map +0 -1
  207. package/lib/typescript/src/tools/longPressTool.d.ts.map +0 -1
  208. package/lib/typescript/src/tools/pickerTool.d.ts.map +0 -1
  209. package/lib/typescript/src/tools/restoreTool.d.ts.map +0 -1
  210. package/lib/typescript/src/tools/scrollTool.d.ts.map +0 -1
  211. package/lib/typescript/src/tools/simplifyTool.d.ts.map +0 -1
  212. package/lib/typescript/src/tools/sliderTool.d.ts.map +0 -1
  213. package/lib/typescript/src/tools/tapTool.d.ts.map +0 -1
  214. package/lib/typescript/src/tools/typeTool.d.ts.map +0 -1
  215. package/lib/typescript/src/tools/types.d.ts.map +0 -1
  216. package/lib/typescript/src/utils/audioUtils.d.ts.map +0 -1
  217. package/lib/typescript/src/utils/logger.d.ts.map +0 -1
  218. package/src/__cli_tmp__.tsx +0 -9
  219. package/src/cli/analyzers/chain-analyzer.ts +0 -183
  220. package/src/cli/extractors/ai-extractor.ts +0 -6
  221. package/src/cli/extractors/ast-extractor.ts +0 -551
  222. package/src/cli/generate-intents.ts +0 -140
  223. package/src/cli/generate-map.ts +0 -121
  224. package/src/cli/generate-swift.ts +0 -116
  225. package/src/cli/scanners/expo-scanner.ts +0 -203
  226. package/src/cli/scanners/rn-scanner.ts +0 -445
  227. package/src/components/AIAgent.tsx +0 -1716
  228. package/src/components/AIZone.tsx +0 -147
  229. package/src/components/AgentChatBar.tsx +0 -1143
  230. package/src/components/AgentErrorBoundary.tsx +0 -78
  231. package/src/components/AgentOverlay.tsx +0 -73
  232. package/src/components/DiscoveryTooltip.tsx +0 -148
  233. package/src/components/HighlightOverlay.tsx +0 -136
  234. package/src/components/Icons.tsx +0 -253
  235. package/src/components/ProactiveHint.tsx +0 -145
  236. package/src/components/cards/InfoCard.tsx +0 -58
  237. package/src/components/cards/ReviewSummary.tsx +0 -76
  238. package/src/config/endpoints.ts +0 -22
  239. package/src/core/ActionRegistry.ts +0 -105
  240. package/src/core/AgentRuntime.ts +0 -1471
  241. package/src/core/FiberTreeWalker.ts +0 -930
  242. package/src/core/IdleDetector.ts +0 -72
  243. package/src/core/MCPBridge.ts +0 -163
  244. package/src/core/ScreenDehydrator.ts +0 -53
  245. package/src/core/ZoneRegistry.ts +0 -44
  246. package/src/core/systemPrompt.ts +0 -431
  247. package/src/core/types.ts +0 -521
  248. package/src/hooks/useAction.ts +0 -182
  249. package/src/index.ts +0 -83
  250. package/src/plugin/withAppIntents.ts +0 -98
  251. package/src/providers/GeminiProvider.ts +0 -357
  252. package/src/providers/OpenAIProvider.ts +0 -379
  253. package/src/providers/ProviderFactory.ts +0 -36
  254. package/src/services/AudioInputService.ts +0 -226
  255. package/src/services/AudioOutputService.ts +0 -236
  256. package/src/services/KnowledgeBaseService.ts +0 -156
  257. package/src/services/VoiceService.ts +0 -451
  258. package/src/services/flags/FlagService.ts +0 -137
  259. package/src/services/telemetry/MobileAI.ts +0 -66
  260. package/src/services/telemetry/PiiScrubber.ts +0 -17
  261. package/src/services/telemetry/TelemetryService.ts +0 -323
  262. package/src/services/telemetry/TouchAutoCapture.ts +0 -165
  263. package/src/services/telemetry/device.ts +0 -93
  264. package/src/services/telemetry/deviceMetadata.ts +0 -13
  265. package/src/services/telemetry/index.ts +0 -13
  266. package/src/services/telemetry/types.ts +0 -75
  267. package/src/support/CSATSurvey.tsx +0 -304
  268. package/src/support/EscalationEventSource.ts +0 -190
  269. package/src/support/EscalationSocket.ts +0 -152
  270. package/src/support/SupportChatModal.tsx +0 -563
  271. package/src/support/SupportGreeting.tsx +0 -161
  272. package/src/support/TicketStore.ts +0 -100
  273. package/src/support/escalateTool.ts +0 -174
  274. package/src/support/index.ts +0 -29
  275. package/src/support/supportPrompt.ts +0 -55
  276. package/src/support/types.ts +0 -155
  277. package/src/tools/datePickerTool.ts +0 -60
  278. package/src/tools/guideTool.ts +0 -76
  279. package/src/tools/index.ts +0 -20
  280. package/src/tools/keyboardTool.ts +0 -30
  281. package/src/tools/longPressTool.ts +0 -61
  282. package/src/tools/pickerTool.ts +0 -115
  283. package/src/tools/restoreTool.ts +0 -33
  284. package/src/tools/scrollTool.ts +0 -156
  285. package/src/tools/simplifyTool.ts +0 -33
  286. package/src/tools/sliderTool.ts +0 -65
  287. package/src/tools/tapTool.ts +0 -93
  288. package/src/tools/typeTool.ts +0 -113
  289. package/src/tools/types.ts +0 -58
  290. package/src/types/jsx.d.ts +0 -20
  291. package/src/utils/audioUtils.ts +0 -54
  292. package/src/utils/logger.ts +0 -38
@@ -1,323 +0,0 @@
1
- /**
2
- * TelemetryService — Batches and sends analytics events to MobileAI Cloud.
3
- *
4
- * Features:
5
- * - Event batching (flush every N seconds or N events)
6
- * - Offline queue with retry on reconnect
7
- * - Lightweight — no native dependencies
8
- * - Opt-in only (requires analyticsKey or analyticsProxyUrl)
9
- */
10
-
11
- import { AppState, Platform } from 'react-native';
12
- import { logger } from '../../utils/logger';
13
- import { ENDPOINTS } from '../../config/endpoints';
14
- import type {
15
- TelemetryEvent,
16
- TelemetryBatch,
17
- TelemetryConfig,
18
- } from './types';
19
- import { scrubPII } from './PiiScrubber';
20
- import { FlagService } from '../flags/FlagService';
21
-
22
- // Optional: AsyncStorage for offline event persistence
23
- // SDK works without it — just loses crash recovery for queued events
24
- let _asyncStorage: any = null;
25
- let _asyncStorageLoaded = false;
26
-
27
- function loadAsyncStorage(): any {
28
- if (_asyncStorageLoaded) return _asyncStorage;
29
- _asyncStorageLoaded = true;
30
- try {
31
- // Suppress the RN red box that AsyncStorage triggers when its native module
32
- // isn't linked ("NativeModule: AsyncStorage is null").
33
- const origError = console.error;
34
- console.error = (...args: unknown[]) => {
35
- const msg = args[0];
36
- if (typeof msg === 'string' && msg.includes('AsyncStorage')) return;
37
- origError.apply(console, args);
38
- };
39
- try {
40
- const mod = require('@react-native-async-storage/async-storage');
41
- _asyncStorage = mod.default ?? mod;
42
- } finally {
43
- console.error = origError;
44
- }
45
- // Verify the native module actually works by checking for the native bridge
46
- if (!_asyncStorage || typeof _asyncStorage.getItem !== 'function') {
47
- _asyncStorage = null;
48
- }
49
- } catch {
50
- // Not installed — offline queue persistence disabled
51
- }
52
- return _asyncStorage;
53
- }
54
-
55
- // ─── Constants ─────────────────────────────────────────────────
56
-
57
- const CLOUD_API_URL = ENDPOINTS.telemetryIngest;
58
- const STORAGE_KEY = '@mobileai/telemetry_queue';
59
- const DEFAULT_FLUSH_INTERVAL_MS = 30_000;
60
- const DEFAULT_MAX_BATCH_SIZE = 50;
61
- const SDK_VERSION = '0.10.0';
62
- const LOG_TAG = 'Telemetry';
63
-
64
- // ─── Utility ───────────────────────────────────────────────────
65
-
66
- function generateSessionId(): string {
67
- return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
68
- }
69
-
70
- import { getDeviceId } from './device';
71
-
72
- // ─── Service ───────────────────────────────────────────────────
73
-
74
- export class TelemetryService {
75
- private queue: TelemetryEvent[] = [];
76
- private config: TelemetryConfig;
77
- private sessionId: string;
78
- private currentScreen = 'Unknown';
79
- private screenFlow: string[] = [];
80
- private flushTimer: ReturnType<typeof setInterval> | null = null;
81
- private isFlushing = false;
82
- private appStateSubscription: ReturnType<typeof AppState.addEventListener> | null = null;
83
-
84
- get screen(): string {
85
- return this.currentScreen;
86
- }
87
-
88
- getScreenFlow(): string[] {
89
- return [...this.screenFlow];
90
- }
91
-
92
- /**
93
- * True while the AI agent is executing a tool (tap, type, navigate, etc.).
94
- * The touch interceptor checks this flag to avoid double-counting AI actions
95
- * as human interactions. Agent steps are already tracked as agent_step events.
96
- */
97
- isAgentActing = false;
98
-
99
- public flags: FlagService;
100
-
101
- /** Set by AgentRuntime before/after each tool execution. */
102
- setAgentActing(active: boolean): void {
103
- this.isAgentActing = active;
104
- }
105
-
106
- constructor(config: TelemetryConfig) {
107
- this.config = config;
108
- this.sessionId = generateSessionId();
109
-
110
- // Extract base URL for flags API (e.g. drop /v1/events)
111
- let baseUrl = new URL(ENDPOINTS.escalation).origin;
112
- try {
113
- if (config.analyticsProxyUrl) {
114
- baseUrl = new URL(config.analyticsProxyUrl).origin;
115
- }
116
- } catch {}
117
- this.flags = new FlagService(baseUrl);
118
- }
119
-
120
- // ─── Lifecycle ──────────────────────────────────────────────
121
-
122
- /** Start the telemetry service (call on mount) */
123
- async start(): Promise<void> {
124
- if (!this.isEnabled()) {
125
- logger.debug(LOG_TAG, 'Disabled — no analyticsKey or proxyUrl');
126
- return;
127
- }
128
-
129
- // Restore queued events from previous session
130
- await this.restoreQueue();
131
-
132
- // Fetch feature flags asynchronously (do not block startup)
133
- if (this.config.analyticsKey) {
134
- this.flags.fetch(this.config.analyticsKey).catch((e) =>
135
- logger.warn(LOG_TAG, `Could not sync flags: ${e.message}`)
136
- );
137
- }
138
-
139
- // Start periodic flush
140
- const interval = this.config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
141
- this.flushTimer = setInterval(() => this.flush(), interval);
142
-
143
- // Flush on app background
144
- this.appStateSubscription = AppState.addEventListener(
145
- 'change',
146
- (state) => {
147
- if (state === 'background' || state === 'inactive') {
148
- this.flush();
149
- }
150
- }
151
- );
152
-
153
- // Track session start
154
- this.track('session_start', {
155
- device: Platform.OS,
156
- os: String(Platform.Version),
157
- sdk_version: SDK_VERSION,
158
- });
159
-
160
- logger.info(LOG_TAG, `Started (session: ${this.sessionId})`);
161
- }
162
-
163
- /** Stop the telemetry service (call on unmount) */
164
- async stop(): Promise<void> {
165
- if (!this.isEnabled()) return;
166
-
167
- // Track session end
168
- this.track('session_end', {
169
- events_count: this.queue.length,
170
- });
171
-
172
- // Final flush
173
- await this.flush();
174
-
175
- // Cleanup
176
- if (this.flushTimer) {
177
- clearInterval(this.flushTimer);
178
- this.flushTimer = null;
179
- }
180
- if (this.appStateSubscription) {
181
- this.appStateSubscription.remove();
182
- this.appStateSubscription = null;
183
- }
184
-
185
- logger.info(LOG_TAG, 'Stopped');
186
- }
187
-
188
- // ─── Public API ─────────────────────────────────────────────
189
-
190
- /** Track an event (auto or custom) */
191
- track(type: string, data: Record<string, unknown> = {}): void {
192
- if (!this.isEnabled()) return;
193
-
194
- // Sanitize any string values in the data payload to remove PII
195
- const sanitizedData = Object.fromEntries(
196
- Object.entries(data).map(([k, v]) => [
197
- k,
198
- typeof v === 'string' ? scrubPII(v) : v,
199
- ])
200
- );
201
-
202
- // Auto-append active feature flags
203
- sanitizedData.$flags = this.flags.getAllFlags();
204
-
205
- const event: TelemetryEvent = {
206
- type,
207
- data: sanitizedData,
208
- timestamp: new Date().toISOString(),
209
- screen: this.currentScreen,
210
- sessionId: this.sessionId,
211
- };
212
-
213
- this.queue.push(event);
214
-
215
- if (this.config.debug) {
216
- logger.debug(LOG_TAG, `→ ${type}`, data);
217
- }
218
-
219
- // Auto-flush if batch is full
220
- const maxSize = this.config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
221
- if (this.queue.length >= maxSize) {
222
- this.flush();
223
- }
224
- }
225
-
226
- /** Update current screen (called by AIAgent on navigation) */
227
- setScreen(screenName: string): void {
228
- if (this.currentScreen !== screenName) {
229
- const prevScreen = this.currentScreen;
230
- this.currentScreen = screenName;
231
- this.screenFlow.push(screenName);
232
-
233
- this.track('screen_view', {
234
- screen: screenName,
235
- prev_screen: prevScreen,
236
- });
237
- }
238
- }
239
-
240
- // ─── Flush ──────────────────────────────────────────────────
241
-
242
- /** Send queued events to the cloud API */
243
- async flush(): Promise<void> {
244
- if (!this.isEnabled() || this.queue.length === 0 || this.isFlushing) return;
245
-
246
- this.isFlushing = true;
247
- const eventsToSend = [...this.queue];
248
- this.queue = [];
249
-
250
- try {
251
- const url = this.config.analyticsProxyUrl ?? CLOUD_API_URL;
252
- const headers: Record<string, string> = {
253
- 'Content-Type': 'application/json',
254
- ...(this.config.analyticsProxyHeaders ?? {}),
255
- };
256
-
257
- const batch: TelemetryBatch = {
258
- analyticsKey: this.config.analyticsKey ?? '',
259
- appId: Platform.OS, // Consumer can override via config later
260
- deviceId: getDeviceId() ?? 'unknown',
261
- sdkVersion: SDK_VERSION,
262
- events: eventsToSend,
263
- };
264
-
265
- const response = await fetch(url, {
266
- method: 'POST',
267
- headers,
268
- body: JSON.stringify(batch),
269
- });
270
-
271
- if (!response.ok) {
272
- throw new Error(`HTTP ${response.status}`);
273
- }
274
-
275
- logger.info(LOG_TAG, `Flushed ${eventsToSend.length} events`);
276
- } catch (error: any) {
277
- // Re-queue failed events for retry
278
- this.queue = [...eventsToSend, ...this.queue];
279
- await this.persistQueue();
280
-
281
- logger.warn(LOG_TAG, `Flush failed (${eventsToSend.length} events re-queued): ${error.message}`);
282
- } finally {
283
- this.isFlushing = false;
284
- }
285
- }
286
-
287
- // ─── Persistence (offline support) ──────────────────────────
288
-
289
- /** Save queued events to AsyncStorage for crash/restart recovery */
290
- private async persistQueue(): Promise<void> {
291
- const storage = loadAsyncStorage();
292
- if (!storage) return;
293
- try {
294
- await storage.setItem(STORAGE_KEY, JSON.stringify(this.queue));
295
- } catch {
296
- // AsyncStorage may not be available in all environments
297
- }
298
- }
299
-
300
- /** Restore queued events from previous session */
301
- private async restoreQueue(): Promise<void> {
302
- const storage = loadAsyncStorage();
303
- if (!storage) return;
304
- try {
305
- const stored = await storage.getItem(STORAGE_KEY);
306
- if (stored) {
307
- const events: TelemetryEvent[] = JSON.parse(stored);
308
- this.queue = [...events, ...this.queue];
309
- await storage.removeItem(STORAGE_KEY);
310
- logger.info(LOG_TAG, `Restored ${events.length} events from previous session`);
311
- }
312
- } catch {
313
- // Silently fail — telemetry is non-critical
314
- }
315
- }
316
-
317
- // ─── Helpers ────────────────────────────────────────────────
318
-
319
- /** Check if telemetry is configured */
320
- private isEnabled(): boolean {
321
- return !!(this.config.analyticsKey || this.config.analyticsProxyUrl);
322
- }
323
- }
@@ -1,165 +0,0 @@
1
- /**
2
- * TouchAutoCapture — Extracts a human-readable label from a React Native
3
- * touch event target by walking up the native view hierarchy.
4
- *
5
- * Used by AIAgent to auto-track every tap in the app without
6
- * any developer code changes (zero-config analytics).
7
- *
8
- * Strategy:
9
- * 1. Read the touched element's accessibilityLabel (best signal).
10
- * 2. If none, use React Native's internal _children to find nested text.
11
- * 3. Fallback to the component's testID.
12
- * 4. Last resort: "Unknown Element".
13
- */
14
-
15
- // React Native imports not needed — we use Fiber internals directly
16
- import type { TelemetryService } from './TelemetryService';
17
-
18
- const recentTaps: { label: string; ts: number }[] = [];
19
- const RAGE_WINDOW_MS = 2000;
20
- const RAGE_THRESHOLD = 3;
21
-
22
- /**
23
- * Checks if the user is repeatedly tapping the same element in frustration.
24
- * If rage click detected, emits 'rage_click' event to telemetry.
25
- */
26
- export function checkRageClick(label: string, telemetry: TelemetryService): void {
27
- const now = Date.now();
28
- recentTaps.push({ label, ts: now });
29
-
30
- // Keep buffer unbounded size of 5
31
- if (recentTaps.length > 5) recentTaps.shift();
32
-
33
- const recent = recentTaps.filter(t => t.label === label && now - t.ts < RAGE_WINDOW_MS);
34
- if (recent.length >= RAGE_THRESHOLD) {
35
- telemetry.track('rage_click', {
36
- label,
37
- count: recent.length,
38
- screen: telemetry.screen
39
- });
40
- recentTaps.length = 0; // reset buffer after emitting to avoid spam
41
- }
42
- }
43
-
44
- /**
45
- * Extract a label from a GestureResponderEvent's native target.
46
- *
47
- * @param nativeEvent - The nativeEvent from onStartShouldSetResponderCapture
48
- * @param rootRef - The root View ref (to resolve relative positions)
49
- * @returns A descriptive label string for the tapped element
50
- */
51
- export function extractTouchLabel(nativeEvent: any): string {
52
- // Try accessible properties first (most reliable)
53
- const target = nativeEvent?.target;
54
-
55
- if (!target) return 'Unknown Element';
56
-
57
- // React Native internal: _internalFiberInstanceHandleDEV or _nativeTag
58
- // We can walk the Fiber tree from the target to find text
59
- try {
60
- // Strategy 1: Walk up the Fiber tree from the touched element
61
- let fiber = getFiberFromNativeTag(target);
62
- if (fiber) {
63
- // Walk up looking for text content or accessibility labels
64
- let current: any = fiber;
65
- let depth = 0;
66
- const MAX_DEPTH = 10;
67
-
68
- while (current && depth < MAX_DEPTH) {
69
- // Check for accessibilityLabel
70
- if (current.memoizedProps?.accessibilityLabel) {
71
- return current.memoizedProps.accessibilityLabel;
72
- }
73
-
74
- // Check for testID
75
- if (current.memoizedProps?.testID) {
76
- return current.memoizedProps.testID;
77
- }
78
-
79
- // Check for title (Button component)
80
- if (current.memoizedProps?.title) {
81
- return current.memoizedProps.title;
82
- }
83
-
84
- // Check for placeholder (TextInput)
85
- if (current.memoizedProps?.placeholder) {
86
- return `Input: ${current.memoizedProps.placeholder}`;
87
- }
88
-
89
- // Check if this is a Text node with children string
90
- if (
91
- typeof current.memoizedProps?.children === 'string' &&
92
- current.memoizedProps.children.trim()
93
- ) {
94
- return current.memoizedProps.children.trim();
95
- }
96
-
97
- // Check for nested text in children array
98
- if (Array.isArray(current.memoizedProps?.children)) {
99
- const textChild = findTextInChildren(current.memoizedProps.children);
100
- if (textChild) return textChild;
101
- }
102
-
103
- current = current.return;
104
- depth++;
105
- }
106
- }
107
- } catch {
108
- // Fiber access failed — fall back gracefully
109
- }
110
-
111
- return 'Unknown Element';
112
- }
113
-
114
- /**
115
- * Find the first string text in a React children tree (recursive, max 3 levels).
116
- */
117
- function findTextInChildren(children: any[], depth = 0): string | null {
118
- if (depth > 3) return null;
119
-
120
- for (const child of children) {
121
- if (typeof child === 'string' && child.trim()) {
122
- return child.trim();
123
- }
124
- if (typeof child === 'number') {
125
- return String(child);
126
- }
127
- // React element with props.children
128
- if (child?.props?.children) {
129
- if (typeof child.props.children === 'string' && child.props.children.trim()) {
130
- return child.props.children.trim();
131
- }
132
- if (Array.isArray(child.props.children)) {
133
- const found = findTextInChildren(child.props.children, depth + 1);
134
- if (found) return found;
135
- }
136
- }
137
- }
138
-
139
- return null;
140
- }
141
-
142
- /**
143
- * Attempt to get the Fiber node from a native tag (React Native internal).
144
- * This uses the same technique as React DevTools and testing libraries.
145
- */
146
- function getFiberFromNativeTag(nativeTag: number): any {
147
- try {
148
- // React Native stores a reference from native tag → Fiber
149
- // via the __internalInstanceHandle on the native node
150
- const instance = (globalThis as any).__REACT_DEVTOOLS_GLOBAL_HOOK__;
151
- if (instance?.renderers) {
152
- for (const [, renderer] of instance.renderers) {
153
- if (renderer?.findFiberByHostInstance) {
154
- // Try to find via the devtools hook
155
- const fiber = renderer.findFiberByHostInstance(nativeTag);
156
- if (fiber) return fiber;
157
- }
158
- }
159
- }
160
- } catch {
161
- // DevTools hook not available — expected in production
162
- }
163
-
164
- return null;
165
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * Persistent device ID — a UUID generated on first launch and stored in AsyncStorage.
3
- * Unique per app install, survives across sessions.
4
- *
5
- * AsyncStorage is an optional peer dependency — if not installed, the ID
6
- * persists only in memory for the current session.
7
- */
8
-
9
- const STORAGE_KEY = '@mobileai:device_id';
10
-
11
- let _cachedId: string | null = null;
12
- let _storageLoaded = false;
13
- let _storage: any = null;
14
-
15
- function loadStorage(): any {
16
- if (_storageLoaded) return _storage;
17
- _storageLoaded = true;
18
- try {
19
- // Suppress the RN red box that AsyncStorage triggers when its native module
20
- // isn't linked ("NativeModule: AsyncStorage is null").
21
- const origError = console.error;
22
- console.error = (...args: unknown[]) => {
23
- const msg = args[0];
24
- if (typeof msg === 'string' && msg.includes('AsyncStorage')) return;
25
- origError.apply(console, args);
26
- };
27
- try {
28
- const mod = require('@react-native-async-storage/async-storage');
29
- const candidate = mod.default ?? mod;
30
- if (candidate && typeof candidate.getItem === 'function') {
31
- _storage = candidate;
32
- }
33
- } finally {
34
- console.error = origError;
35
- }
36
- } catch {
37
- // Not installed — device ID won't persist across restarts
38
- }
39
- return _storage;
40
- }
41
-
42
- function generateUUID(): string {
43
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
44
- return crypto.randomUUID();
45
- }
46
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
47
- const r = (Math.random() * 16) | 0;
48
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
49
- return v.toString(16);
50
- });
51
- }
52
-
53
- /**
54
- * Returns the persistent device ID synchronously (from cache).
55
- * Returns null if not yet initialized.
56
- */
57
- export function getDeviceId(): string | null {
58
- return _cachedId;
59
- }
60
-
61
- /**
62
- * Initializes or retrieves the persistent device ID.
63
- * Call once on app startup. Subsequent getDeviceId() calls are synchronous.
64
- */
65
- export async function initDeviceId(): Promise<string> {
66
- if (_cachedId) return _cachedId;
67
-
68
- const storage = loadStorage();
69
- if (storage) {
70
- try {
71
- const stored: string | null = await storage.getItem(STORAGE_KEY);
72
- if (stored) {
73
- _cachedId = stored;
74
- return stored;
75
- }
76
- } catch {
77
- // Storage read failed — continue with new ID
78
- }
79
- }
80
-
81
- const newId = generateUUID();
82
- _cachedId = newId;
83
-
84
- if (storage) {
85
- try {
86
- await storage.setItem(STORAGE_KEY, newId);
87
- } catch {
88
- // Storage write failed — ID works for this session only
89
- }
90
- }
91
-
92
- return newId;
93
- }
@@ -1,13 +0,0 @@
1
- import { Platform } from 'react-native';
2
-
3
- export interface DeviceMetadata {
4
- platform: string;
5
- osVersion: string;
6
- }
7
-
8
- export function getDeviceMetadata(): DeviceMetadata {
9
- return {
10
- platform: Platform.OS === 'ios' ? 'iOS' : Platform.OS === 'android' ? 'Android' : Platform.OS,
11
- osVersion: String(Platform.Version),
12
- };
13
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Telemetry module barrel export.
3
- */
4
-
5
- export { TelemetryService } from './TelemetryService';
6
- export { MobileAI, bindTelemetryService } from './MobileAI';
7
- export type {
8
- TelemetryEvent,
9
- TelemetryBatch,
10
- TelemetryConfig,
11
- AutoEventType,
12
- EventType,
13
- } from './types';
@@ -1,75 +0,0 @@
1
- /**
2
- * Telemetry types for the MobileAI analytics module.
3
- *
4
- * Events are split into two categories:
5
- * 1. Auto-captured: SDK captures these without consumer code
6
- * 2. Consumer-tracked: via MobileAI.track() API
7
- */
8
-
9
- // ─── Event Types ──────────────────────────────────────────────
10
-
11
- /** Auto-captured event types */
12
- export type AutoEventType =
13
- | 'screen_view'
14
- | 'user_action'
15
- | 'scroll_depth'
16
- | 'idle_detected'
17
- | 'session_start'
18
- | 'session_end'
19
- | 'agent_request'
20
- | 'agent_step'
21
- | 'agent_complete'
22
- | 'escalation'
23
- | 'knowledge_query'
24
- | 'knowledge_miss'
25
- | 'csat_response';
26
-
27
- /** All event types (auto + custom) */
28
- export type EventType = AutoEventType | string;
29
-
30
- // ─── Event Payload ────────────────────────────────────────────
31
-
32
- export interface TelemetryEvent {
33
- /** Event type identifier */
34
- type: EventType;
35
- /** Event-specific data */
36
- data: Record<string, unknown>;
37
- /** ISO 8601 timestamp (auto-set by SDK) */
38
- timestamp: string;
39
- /** Current screen name (auto-attached) */
40
- screen: string;
41
- /** Session ID (auto-generated per app lifecycle) */
42
- sessionId: string;
43
- }
44
-
45
- // ─── Batch Payload (sent to cloud) ────────────────────────────
46
-
47
- export interface TelemetryBatch {
48
- /** Publishable analytics key */
49
- analyticsKey: string;
50
- /** App identifier (bundle ID / package name) */
51
- appId: string;
52
- /** Hashed user/device identifier */
53
- deviceId: string;
54
- /** SDK version */
55
- sdkVersion: string;
56
- /** Batch of events */
57
- events: TelemetryEvent[];
58
- }
59
-
60
- // ─── Configuration ────────────────────────────────────────────
61
-
62
- export interface TelemetryConfig {
63
- /** Publishable analytics key (mobileai_pub_xxx) */
64
- analyticsKey?: string;
65
- /** Proxy URL for enterprise customers (replaces direct cloud API) */
66
- analyticsProxyUrl?: string;
67
- /** Custom headers for proxy requests */
68
- analyticsProxyHeaders?: Record<string, string>;
69
- /** Flush interval in ms (default: 30000) */
70
- flushIntervalMs?: number;
71
- /** Max events before auto-flush (default: 50) */
72
- maxBatchSize?: number;
73
- /** Enable debug logging for telemetry (default: false) */
74
- debug?: boolean;
75
- }