@mobileai/react-native 0.9.17 → 0.9.18

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 (213) hide show
  1. package/package.json +2 -5
  2. package/lib/module/__cli_tmp__.js.map +0 -1
  3. package/lib/module/components/AIAgent.js.map +0 -1
  4. package/lib/module/components/AIZone.js.map +0 -1
  5. package/lib/module/components/AgentChatBar.js.map +0 -1
  6. package/lib/module/components/AgentErrorBoundary.js.map +0 -1
  7. package/lib/module/components/AgentOverlay.js.map +0 -1
  8. package/lib/module/components/DiscoveryTooltip.js.map +0 -1
  9. package/lib/module/components/HighlightOverlay.js.map +0 -1
  10. package/lib/module/components/Icons.js.map +0 -1
  11. package/lib/module/components/ProactiveHint.js.map +0 -1
  12. package/lib/module/components/cards/InfoCard.js.map +0 -1
  13. package/lib/module/components/cards/ReviewSummary.js.map +0 -1
  14. package/lib/module/config/endpoints.js.map +0 -1
  15. package/lib/module/core/ActionRegistry.js.map +0 -1
  16. package/lib/module/core/AgentRuntime.js.map +0 -1
  17. package/lib/module/core/FiberTreeWalker.js.map +0 -1
  18. package/lib/module/core/IdleDetector.js.map +0 -1
  19. package/lib/module/core/MCPBridge.js.map +0 -1
  20. package/lib/module/core/ScreenDehydrator.js.map +0 -1
  21. package/lib/module/core/ZoneRegistry.js.map +0 -1
  22. package/lib/module/core/systemPrompt.js.map +0 -1
  23. package/lib/module/core/types.js.map +0 -1
  24. package/lib/module/hooks/useAction.js.map +0 -1
  25. package/lib/module/index.js.map +0 -1
  26. package/lib/module/plugin/withAppIntents.js.map +0 -1
  27. package/lib/module/providers/GeminiProvider.js.map +0 -1
  28. package/lib/module/providers/OpenAIProvider.js.map +0 -1
  29. package/lib/module/providers/ProviderFactory.js.map +0 -1
  30. package/lib/module/services/AudioInputService.js.map +0 -1
  31. package/lib/module/services/AudioOutputService.js.map +0 -1
  32. package/lib/module/services/KnowledgeBaseService.js.map +0 -1
  33. package/lib/module/services/VoiceService.js.map +0 -1
  34. package/lib/module/services/flags/FlagService.js.map +0 -1
  35. package/lib/module/services/telemetry/MobileAI.js.map +0 -1
  36. package/lib/module/services/telemetry/PiiScrubber.js.map +0 -1
  37. package/lib/module/services/telemetry/TelemetryService.js.map +0 -1
  38. package/lib/module/services/telemetry/TouchAutoCapture.js.map +0 -1
  39. package/lib/module/services/telemetry/device.js.map +0 -1
  40. package/lib/module/services/telemetry/deviceMetadata.js.map +0 -1
  41. package/lib/module/services/telemetry/index.js.map +0 -1
  42. package/lib/module/services/telemetry/types.js.map +0 -1
  43. package/lib/module/support/CSATSurvey.js.map +0 -1
  44. package/lib/module/support/EscalationEventSource.js.map +0 -1
  45. package/lib/module/support/EscalationSocket.js.map +0 -1
  46. package/lib/module/support/SupportChatModal.js.map +0 -1
  47. package/lib/module/support/SupportGreeting.js.map +0 -1
  48. package/lib/module/support/TicketStore.js.map +0 -1
  49. package/lib/module/support/escalateTool.js.map +0 -1
  50. package/lib/module/support/index.js.map +0 -1
  51. package/lib/module/support/supportPrompt.js.map +0 -1
  52. package/lib/module/support/types.js.map +0 -1
  53. package/lib/module/tools/datePickerTool.js.map +0 -1
  54. package/lib/module/tools/guideTool.js.map +0 -1
  55. package/lib/module/tools/index.js.map +0 -1
  56. package/lib/module/tools/keyboardTool.js.map +0 -1
  57. package/lib/module/tools/longPressTool.js.map +0 -1
  58. package/lib/module/tools/pickerTool.js.map +0 -1
  59. package/lib/module/tools/restoreTool.js.map +0 -1
  60. package/lib/module/tools/scrollTool.js.map +0 -1
  61. package/lib/module/tools/simplifyTool.js.map +0 -1
  62. package/lib/module/tools/sliderTool.js.map +0 -1
  63. package/lib/module/tools/tapTool.js.map +0 -1
  64. package/lib/module/tools/typeTool.js.map +0 -1
  65. package/lib/module/tools/types.js.map +0 -1
  66. package/lib/module/types/jsx.d.js.map +0 -1
  67. package/lib/module/utils/audioUtils.js.map +0 -1
  68. package/lib/module/utils/logger.js.map +0 -1
  69. package/lib/typescript/babel.config.d.ts.map +0 -1
  70. package/lib/typescript/bin/generate-map.d.cts.map +0 -1
  71. package/lib/typescript/eslint.config.d.mts.map +0 -1
  72. package/lib/typescript/generate-map.d.ts.map +0 -1
  73. package/lib/typescript/src/__cli_tmp__.d.ts.map +0 -1
  74. package/lib/typescript/src/components/AIAgent.d.ts.map +0 -1
  75. package/lib/typescript/src/components/AIZone.d.ts.map +0 -1
  76. package/lib/typescript/src/components/AgentChatBar.d.ts.map +0 -1
  77. package/lib/typescript/src/components/AgentErrorBoundary.d.ts.map +0 -1
  78. package/lib/typescript/src/components/AgentOverlay.d.ts.map +0 -1
  79. package/lib/typescript/src/components/DiscoveryTooltip.d.ts.map +0 -1
  80. package/lib/typescript/src/components/HighlightOverlay.d.ts.map +0 -1
  81. package/lib/typescript/src/components/Icons.d.ts.map +0 -1
  82. package/lib/typescript/src/components/ProactiveHint.d.ts.map +0 -1
  83. package/lib/typescript/src/components/cards/InfoCard.d.ts.map +0 -1
  84. package/lib/typescript/src/components/cards/ReviewSummary.d.ts.map +0 -1
  85. package/lib/typescript/src/config/endpoints.d.ts.map +0 -1
  86. package/lib/typescript/src/core/ActionRegistry.d.ts.map +0 -1
  87. package/lib/typescript/src/core/AgentRuntime.d.ts.map +0 -1
  88. package/lib/typescript/src/core/FiberTreeWalker.d.ts.map +0 -1
  89. package/lib/typescript/src/core/IdleDetector.d.ts.map +0 -1
  90. package/lib/typescript/src/core/MCPBridge.d.ts.map +0 -1
  91. package/lib/typescript/src/core/ScreenDehydrator.d.ts.map +0 -1
  92. package/lib/typescript/src/core/ZoneRegistry.d.ts.map +0 -1
  93. package/lib/typescript/src/core/systemPrompt.d.ts.map +0 -1
  94. package/lib/typescript/src/core/types.d.ts.map +0 -1
  95. package/lib/typescript/src/hooks/useAction.d.ts.map +0 -1
  96. package/lib/typescript/src/index.d.ts.map +0 -1
  97. package/lib/typescript/src/plugin/withAppIntents.d.ts.map +0 -1
  98. package/lib/typescript/src/providers/GeminiProvider.d.ts.map +0 -1
  99. package/lib/typescript/src/providers/OpenAIProvider.d.ts.map +0 -1
  100. package/lib/typescript/src/providers/ProviderFactory.d.ts.map +0 -1
  101. package/lib/typescript/src/services/AudioInputService.d.ts.map +0 -1
  102. package/lib/typescript/src/services/AudioOutputService.d.ts.map +0 -1
  103. package/lib/typescript/src/services/KnowledgeBaseService.d.ts.map +0 -1
  104. package/lib/typescript/src/services/VoiceService.d.ts.map +0 -1
  105. package/lib/typescript/src/services/flags/FlagService.d.ts.map +0 -1
  106. package/lib/typescript/src/services/telemetry/MobileAI.d.ts.map +0 -1
  107. package/lib/typescript/src/services/telemetry/PiiScrubber.d.ts.map +0 -1
  108. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts.map +0 -1
  109. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts.map +0 -1
  110. package/lib/typescript/src/services/telemetry/device.d.ts.map +0 -1
  111. package/lib/typescript/src/services/telemetry/deviceMetadata.d.ts.map +0 -1
  112. package/lib/typescript/src/services/telemetry/index.d.ts.map +0 -1
  113. package/lib/typescript/src/services/telemetry/types.d.ts.map +0 -1
  114. package/lib/typescript/src/support/CSATSurvey.d.ts.map +0 -1
  115. package/lib/typescript/src/support/EscalationEventSource.d.ts.map +0 -1
  116. package/lib/typescript/src/support/EscalationSocket.d.ts.map +0 -1
  117. package/lib/typescript/src/support/SupportChatModal.d.ts.map +0 -1
  118. package/lib/typescript/src/support/SupportGreeting.d.ts.map +0 -1
  119. package/lib/typescript/src/support/TicketStore.d.ts.map +0 -1
  120. package/lib/typescript/src/support/escalateTool.d.ts.map +0 -1
  121. package/lib/typescript/src/support/index.d.ts.map +0 -1
  122. package/lib/typescript/src/support/supportPrompt.d.ts.map +0 -1
  123. package/lib/typescript/src/support/types.d.ts.map +0 -1
  124. package/lib/typescript/src/tools/datePickerTool.d.ts.map +0 -1
  125. package/lib/typescript/src/tools/guideTool.d.ts.map +0 -1
  126. package/lib/typescript/src/tools/index.d.ts.map +0 -1
  127. package/lib/typescript/src/tools/keyboardTool.d.ts.map +0 -1
  128. package/lib/typescript/src/tools/longPressTool.d.ts.map +0 -1
  129. package/lib/typescript/src/tools/pickerTool.d.ts.map +0 -1
  130. package/lib/typescript/src/tools/restoreTool.d.ts.map +0 -1
  131. package/lib/typescript/src/tools/scrollTool.d.ts.map +0 -1
  132. package/lib/typescript/src/tools/simplifyTool.d.ts.map +0 -1
  133. package/lib/typescript/src/tools/sliderTool.d.ts.map +0 -1
  134. package/lib/typescript/src/tools/tapTool.d.ts.map +0 -1
  135. package/lib/typescript/src/tools/typeTool.d.ts.map +0 -1
  136. package/lib/typescript/src/tools/types.d.ts.map +0 -1
  137. package/lib/typescript/src/utils/audioUtils.d.ts.map +0 -1
  138. package/lib/typescript/src/utils/logger.d.ts.map +0 -1
  139. package/src/__cli_tmp__.tsx +0 -9
  140. package/src/cli/analyzers/chain-analyzer.ts +0 -183
  141. package/src/cli/extractors/ai-extractor.ts +0 -6
  142. package/src/cli/extractors/ast-extractor.ts +0 -551
  143. package/src/cli/generate-intents.ts +0 -140
  144. package/src/cli/generate-map.ts +0 -121
  145. package/src/cli/generate-swift.ts +0 -116
  146. package/src/cli/scanners/expo-scanner.ts +0 -203
  147. package/src/cli/scanners/rn-scanner.ts +0 -445
  148. package/src/components/AIAgent.tsx +0 -1716
  149. package/src/components/AIZone.tsx +0 -147
  150. package/src/components/AgentChatBar.tsx +0 -1143
  151. package/src/components/AgentErrorBoundary.tsx +0 -78
  152. package/src/components/AgentOverlay.tsx +0 -73
  153. package/src/components/DiscoveryTooltip.tsx +0 -148
  154. package/src/components/HighlightOverlay.tsx +0 -136
  155. package/src/components/Icons.tsx +0 -253
  156. package/src/components/ProactiveHint.tsx +0 -145
  157. package/src/components/cards/InfoCard.tsx +0 -58
  158. package/src/components/cards/ReviewSummary.tsx +0 -76
  159. package/src/config/endpoints.ts +0 -22
  160. package/src/core/ActionRegistry.ts +0 -105
  161. package/src/core/AgentRuntime.ts +0 -1471
  162. package/src/core/FiberTreeWalker.ts +0 -930
  163. package/src/core/IdleDetector.ts +0 -72
  164. package/src/core/MCPBridge.ts +0 -163
  165. package/src/core/ScreenDehydrator.ts +0 -53
  166. package/src/core/ZoneRegistry.ts +0 -44
  167. package/src/core/systemPrompt.ts +0 -431
  168. package/src/core/types.ts +0 -521
  169. package/src/hooks/useAction.ts +0 -182
  170. package/src/index.ts +0 -83
  171. package/src/plugin/withAppIntents.ts +0 -98
  172. package/src/providers/GeminiProvider.ts +0 -357
  173. package/src/providers/OpenAIProvider.ts +0 -379
  174. package/src/providers/ProviderFactory.ts +0 -36
  175. package/src/services/AudioInputService.ts +0 -226
  176. package/src/services/AudioOutputService.ts +0 -236
  177. package/src/services/KnowledgeBaseService.ts +0 -156
  178. package/src/services/VoiceService.ts +0 -451
  179. package/src/services/flags/FlagService.ts +0 -137
  180. package/src/services/telemetry/MobileAI.ts +0 -66
  181. package/src/services/telemetry/PiiScrubber.ts +0 -17
  182. package/src/services/telemetry/TelemetryService.ts +0 -323
  183. package/src/services/telemetry/TouchAutoCapture.ts +0 -165
  184. package/src/services/telemetry/device.ts +0 -93
  185. package/src/services/telemetry/deviceMetadata.ts +0 -13
  186. package/src/services/telemetry/index.ts +0 -13
  187. package/src/services/telemetry/types.ts +0 -75
  188. package/src/support/CSATSurvey.tsx +0 -304
  189. package/src/support/EscalationEventSource.ts +0 -190
  190. package/src/support/EscalationSocket.ts +0 -152
  191. package/src/support/SupportChatModal.tsx +0 -563
  192. package/src/support/SupportGreeting.tsx +0 -161
  193. package/src/support/TicketStore.ts +0 -100
  194. package/src/support/escalateTool.ts +0 -174
  195. package/src/support/index.ts +0 -29
  196. package/src/support/supportPrompt.ts +0 -55
  197. package/src/support/types.ts +0 -155
  198. package/src/tools/datePickerTool.ts +0 -60
  199. package/src/tools/guideTool.ts +0 -76
  200. package/src/tools/index.ts +0 -20
  201. package/src/tools/keyboardTool.ts +0 -30
  202. package/src/tools/longPressTool.ts +0 -61
  203. package/src/tools/pickerTool.ts +0 -115
  204. package/src/tools/restoreTool.ts +0 -33
  205. package/src/tools/scrollTool.ts +0 -156
  206. package/src/tools/simplifyTool.ts +0 -33
  207. package/src/tools/sliderTool.ts +0 -65
  208. package/src/tools/tapTool.ts +0 -93
  209. package/src/tools/typeTool.ts +0 -113
  210. package/src/tools/types.ts +0 -58
  211. package/src/types/jsx.d.ts +0 -20
  212. package/src/utils/audioUtils.ts +0 -54
  213. 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
- }