@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,30 +0,0 @@
1
- /**
2
- * Keyboard Tool — Dismiss the on-screen keyboard.
3
- *
4
- * Pattern from:
5
- * - Maestro: hideKeyboard
6
- * - Detox: closeSoftKeyboard
7
- *
8
- * Uses React Native's Keyboard.dismiss() API.
9
- * This is a global action — no element index needed.
10
- */
11
-
12
- import { Keyboard } from 'react-native';
13
- import type { AgentTool } from './types';
14
-
15
- export function createKeyboardTool(): AgentTool {
16
- return {
17
- name: 'dismiss_keyboard',
18
- description: 'Dismiss the on-screen keyboard. Use after typing into a text input when the keyboard is blocking other elements.',
19
- parameters: {},
20
- execute: async () => {
21
- try {
22
- Keyboard.dismiss();
23
- await new Promise(resolve => setTimeout(resolve, 300));
24
- return '✅ Keyboard dismissed.';
25
- } catch (error: any) {
26
- return `❌ Error dismissing keyboard: ${error.message}`;
27
- }
28
- },
29
- };
30
- }
@@ -1,61 +0,0 @@
1
- /**
2
- * Long Press Tool — Extended press interaction via onLongPress.
3
- *
4
- * Pattern from:
5
- * - Detox: longPress(point, duration)
6
- * - Maestro: longPress(point)
7
- *
8
- * In JS fiber level: calls onLongPress prop (available on Pressable/TouchableOpacity).
9
- * Bubbles up fiber tree if direct element doesn't have onLongPress.
10
- */
11
-
12
- import { walkFiberTree } from '../core/FiberTreeWalker';
13
- import type { AgentTool, ToolContext } from './types';
14
-
15
- export function createLongPressTool(context: ToolContext): AgentTool {
16
- return {
17
- name: 'long_press',
18
- description: 'Long-press an interactive element by its index. Use for actions that require a longer touch, such as showing context menus, reordering items, or triggering secondary actions.',
19
- parameters: {
20
- index: { type: 'number', description: 'The index of the element to long-press', required: true },
21
- },
22
- execute: async (args) => {
23
- const { interactives: elements } = walkFiberTree(context.getRootRef(), context.getWalkConfig());
24
- const element = elements.find(el => el.index === args.index);
25
- if (!element) {
26
- return `❌ Element with index ${args.index} not found. Available indexes: ${elements.map(e => e.index).join(', ')}`;
27
- }
28
-
29
- // Strategy 1: Direct onLongPress
30
- if (element.props.onLongPress && typeof element.props.onLongPress === 'function') {
31
- try {
32
- element.props.onLongPress();
33
- await new Promise(resolve => setTimeout(resolve, 500));
34
- return `✅ Long-pressed [${args.index}] "${element.label}"`;
35
- } catch (error: any) {
36
- return `❌ Error long-pressing [${args.index}]: ${error.message}`;
37
- }
38
- }
39
-
40
- // Strategy 2: Bubble up fiber tree
41
- let fiber = element.fiberNode?.return;
42
- let bubbleDepth = 0;
43
- while (fiber && bubbleDepth < 5) {
44
- const parentProps = fiber.memoizedProps || {};
45
- if (parentProps.onLongPress && typeof parentProps.onLongPress === 'function') {
46
- try {
47
- parentProps.onLongPress();
48
- await new Promise(resolve => setTimeout(resolve, 500));
49
- return `✅ Long-pressed parent of [${args.index}] "${element.label}"`;
50
- } catch (error: any) {
51
- return `❌ Error long-pressing parent of [${args.index}]: ${error.message}`;
52
- }
53
- }
54
- fiber = fiber.return;
55
- bubbleDepth++;
56
- }
57
-
58
- return `❌ Element [${args.index}] "${element.label}" has no long-press handler. Try using tap instead.`;
59
- },
60
- };
61
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * Picker Tool — Select values from picker/dropdown components.
3
- *
4
- * Pattern from Detox: setColumnToValue(column, value)
5
- *
6
- * In JS: calls onValueChange(itemValue, itemIndex).
7
- * Reads available options from props.children (Picker.Item) or props.items array.
8
- */
9
-
10
- import { walkFiberTree } from '../core/FiberTreeWalker';
11
- import type { AgentTool, ToolContext } from './types';
12
-
13
- /**
14
- * Extract available options from a picker element's props.
15
- * Handles multiple picker libraries:
16
- * - RN Picker: children are Picker.Item with { label, value } props
17
- * - RNPickerSelect: items prop is array of { label, value }
18
- * - DropDownPicker: items prop is array of { label, value }
19
- */
20
- function extractPickerOptions(element: any): Array<{ label: string; value: any }> {
21
- const props = element.props || {};
22
- const options: Array<{ label: string; value: any }> = [];
23
-
24
- // Pattern 1: items prop (RNPickerSelect, DropDownPicker)
25
- if (Array.isArray(props.items)) {
26
- for (const item of props.items) {
27
- if (item && item.label !== undefined) {
28
- options.push({ label: String(item.label), value: item.value });
29
- }
30
- }
31
- return options;
32
- }
33
-
34
- // Pattern 2: options prop (some custom pickers)
35
- if (Array.isArray(props.options)) {
36
- for (const item of props.options) {
37
- if (typeof item === 'string') {
38
- options.push({ label: item, value: item });
39
- } else if (item && item.label !== undefined) {
40
- options.push({ label: String(item.label), value: item.value });
41
- }
42
- }
43
- return options;
44
- }
45
-
46
- // Pattern 3: Fiber children (RN Picker with Picker.Item children)
47
- const fiberNode = element.fiberNode;
48
- if (fiberNode?.child) {
49
- let child = fiberNode.child;
50
- while (child) {
51
- const childProps = child.memoizedProps || {};
52
- if (childProps.label !== undefined && childProps.value !== undefined) {
53
- options.push({ label: String(childProps.label), value: childProps.value });
54
- }
55
- child = child.sibling;
56
- }
57
- }
58
-
59
- return options;
60
- }
61
-
62
- export function createPickerTool(context: ToolContext): AgentTool {
63
- return {
64
- name: 'select_picker',
65
- description: 'Select a value from a picker/dropdown by its index. Provide the exact value string to select.',
66
- parameters: {
67
- index: { type: 'number', description: 'The index of the picker element', required: true },
68
- value: { type: 'string', description: 'The value to select (must match an available option)', required: true },
69
- },
70
- execute: async (args) => {
71
- const { interactives: elements } = walkFiberTree(context.getRootRef(), context.getWalkConfig());
72
- const element = elements.find(el => el.index === args.index);
73
- if (!element) {
74
- return `❌ Element with index ${args.index} not found.`;
75
- }
76
-
77
- const onValueChange = element.props.onValueChange;
78
- if (!onValueChange || typeof onValueChange !== 'function') {
79
- return `❌ Element [${args.index}] "${element.label}" is not a picker (no onValueChange handler).`;
80
- }
81
-
82
- // Find available options
83
- const options = extractPickerOptions(element);
84
-
85
- if (options.length > 0) {
86
- // Find matching option (case-insensitive)
87
- const match = options.find(
88
- opt => String(opt.value) === args.value ||
89
- opt.label.toLowerCase() === args.value.toLowerCase()
90
- );
91
- if (!match) {
92
- const available = options.map(o => `"${o.label}" (${o.value})`).join(', ');
93
- return `❌ Value "${args.value}" not found in picker. Available: ${available}`;
94
- }
95
- try {
96
- const matchIndex = options.indexOf(match);
97
- onValueChange(match.value, matchIndex);
98
- await new Promise(resolve => setTimeout(resolve, 500));
99
- return `✅ Selected "${match.label}" in picker [${args.index}] "${element.label}"`;
100
- } catch (error: any) {
101
- return `❌ Error selecting picker value: ${error.message}`;
102
- }
103
- }
104
-
105
- // No options found — try direct value pass
106
- try {
107
- onValueChange(args.value, 0);
108
- await new Promise(resolve => setTimeout(resolve, 500));
109
- return `✅ Set picker [${args.index}] "${element.label}" to "${args.value}"`;
110
- } catch (error: any) {
111
- return `❌ Error setting picker value: ${error.message}`;
112
- }
113
- },
114
- };
115
- }
@@ -1,33 +0,0 @@
1
- import type { ToolDefinition } from '../core/types';
2
- import { globalZoneRegistry } from '../core/ZoneRegistry';
3
- import { logger } from '../utils/logger';
4
-
5
- export function createRestoreTool(): ToolDefinition {
6
- return {
7
- name: 'restore_zone',
8
- description: 'Restore a specific Zone to its default state, reversing any previous simplify_zone or inject_card operations.',
9
- parameters: {
10
- zoneId: {
11
- type: 'string',
12
- description: 'The ID of the AIZone to restore',
13
- required: true,
14
- }
15
- },
16
- execute: async (args) => {
17
- const zoneId = String(args.zoneId);
18
-
19
- const zone = globalZoneRegistry.get(zoneId) as any;
20
- if (!zone) {
21
- return `❌ Cannot restore zone "${zoneId}": Zone does not exist.`;
22
- }
23
-
24
- if (zone._controller) {
25
- zone._controller.restore();
26
- logger.info('RestoreTool', `Restored zone: ${zoneId}`);
27
- return `✅ Successfully restored zone "${zoneId}" to its default state.`;
28
- }
29
-
30
- return `❌ Cannot restore zone "${zoneId}": Controller not attached.`;
31
- },
32
- };
33
- }
@@ -1,156 +0,0 @@
1
- /**
2
- * Scroll Tool — Direction-based scrolling with safety checks.
3
- *
4
- * Patterns implemented:
5
- * - Detox: ScrollHelper.perform(direction, amountInDP) — relative scroll
6
- * - Detox: scrollableProbe.atScrollingEdge() — pre-scroll edge detection
7
- * - Detox: ScrollToIndexAction.getConstraints() — PagerView rejection
8
- * - Appium: mobileScrollGesture returns boolean (canScrollMore)
9
- * - Maestro: hierarchy diff post-scroll (no-op detection)
10
- */
11
-
12
- import { findScrollableContainers } from '../core/FiberTreeWalker';
13
- import type { AgentTool, ToolContext } from './types';
14
-
15
- export function createScrollTool(context: ToolContext): AgentTool {
16
- return {
17
- name: 'scroll',
18
- description: 'Scroll the current screen to reveal more content. Use when you need to see items that are not yet visible, e.g. in lazy-loaded lists, long forms, or paginated content. If the screen has multiple scrollable areas, specify containerIndex to target a specific one.',
19
- parameters: {
20
- direction: {
21
- type: 'string',
22
- description: "Scroll direction: 'down' or 'up'",
23
- required: true,
24
- enum: ['down', 'up'],
25
- },
26
- amount: {
27
- type: 'string',
28
- description: "How far to scroll: 'page' (default, ~one screenful), 'toEnd' (jump to bottom), or 'toStart' (jump to top)",
29
- required: false,
30
- enum: ['page', 'toEnd', 'toStart'],
31
- },
32
- containerIndex: {
33
- type: 'number',
34
- description: 'Index of the scrollable container to scroll (0-based). Use when the screen has multiple scrollable areas. Default: 0 (the main/first scrollable area).',
35
- required: false,
36
- },
37
- },
38
- execute: async (args) => {
39
- const screenName = context.getCurrentScreenName();
40
- const containers = findScrollableContainers(context.getRootRef(), screenName);
41
-
42
- if (containers.length === 0) {
43
- return `❌ No scrollable container found on screen "${screenName}". Content may still be loading — wait and try again.`;
44
- }
45
-
46
- const targetIndex = args.containerIndex ?? 0;
47
- const container = containers[targetIndex];
48
- if (!container) {
49
- const available = containers
50
- .filter(c => !c.isPagerLike)
51
- .map(c => `[${c.index}] ${c.label} (${c.componentName})`)
52
- .join(', ');
53
- return `❌ Container index ${targetIndex} not found. Available scrollable containers on "${screenName}": ${available}`;
54
- }
55
-
56
- // PagerView/TabView Rejection (Detox: getConstraints() rejects ViewPager)
57
- if (container.isPagerLike) {
58
- const scrollableAlts = containers
59
- .filter(c => !c.isPagerLike)
60
- .map(c => `[${c.index}] ${c.label} (${c.componentName})`)
61
- .join(', ');
62
- const suggestion = scrollableAlts
63
- ? `Try scrolling a content container instead: ${scrollableAlts}`
64
- : 'Use tap on the tab labels to switch tabs instead of scrolling.';
65
- return `⚠️ Container [${targetIndex}] "${container.label}" is a ${container.componentName} (tab/page container). ` +
66
- `Scrolling pager containers directly causes native crashes. ${suggestion}`;
67
- }
68
-
69
- const direction: string = args.direction || 'down';
70
- const amount: string = args.amount || 'page';
71
- const scrollRef = container.stateNode;
72
-
73
- // Edge Detection (Detox: scrollableProbe.atScrollingEdge())
74
- if (amount === 'page' && typeof scrollRef.scrollToOffset === 'function') {
75
- const metrics = scrollRef._scrollMetrics;
76
- if (metrics) {
77
- const { offset, contentLength, visibleLength } = metrics;
78
- const isAtBottom = offset + visibleLength >= contentLength - 2;
79
- const isAtTop = offset <= 0;
80
-
81
- if (direction === 'down' && isAtBottom) {
82
- return `⚠️ Already at the bottom of ${container.label} (${container.componentName}). No more content to scroll to. All items are visible.`;
83
- }
84
- if (direction === 'up' && isAtTop) {
85
- return `⚠️ Already at the top of ${container.label} (${container.componentName}). Cannot scroll further up.`;
86
- }
87
- }
88
- }
89
-
90
- // Track position for no-op detection (Maestro: hierarchy diff)
91
- const offsetBefore = typeof scrollRef.scrollToOffset === 'function'
92
- ? scrollRef._scrollMetrics?.offset ?? 0
93
- : 0;
94
-
95
- try {
96
- if (amount === 'toEnd') {
97
- if (typeof scrollRef.scrollToEnd === 'function') {
98
- scrollRef.scrollToEnd({ animated: true });
99
- } else if (typeof scrollRef.scrollTo === 'function') {
100
- scrollRef.scrollTo({ y: 999999, animated: true });
101
- }
102
- } else if (amount === 'toStart') {
103
- if (typeof scrollRef.scrollTo === 'function') {
104
- scrollRef.scrollTo({ y: 0, animated: true });
105
- } else if (typeof scrollRef.scrollToOffset === 'function') {
106
- scrollRef.scrollToOffset({ offset: 0, animated: true });
107
- }
108
- } else {
109
- // Direction-based relative scroll (Detox: ScrollHelper, Appium: percent)
110
- const FALLBACK_PAGE_HEIGHT = 800;
111
-
112
- if (typeof scrollRef.scrollToOffset === 'function') {
113
- const metrics = scrollRef._scrollMetrics;
114
- const currentOffset = metrics?.offset ?? 0;
115
- const visibleLength = metrics?.visibleLength ?? FALLBACK_PAGE_HEIGHT;
116
- const scrollAmount = Math.round(visibleLength * 0.8);
117
-
118
- const newOffset = direction === 'down'
119
- ? currentOffset + scrollAmount
120
- : Math.max(0, currentOffset - scrollAmount);
121
-
122
- scrollRef.scrollToOffset({ offset: newOffset, animated: true });
123
- } else if (typeof scrollRef.scrollTo === 'function') {
124
- const currentY = scrollRef._nativeRef?.contentOffset?.y ?? 0;
125
- const scrollAmount = FALLBACK_PAGE_HEIGHT;
126
-
127
- scrollRef.scrollTo({
128
- y: direction === 'down'
129
- ? currentY + scrollAmount
130
- : Math.max(0, currentY - scrollAmount),
131
- animated: true,
132
- });
133
- }
134
- }
135
-
136
- // Wait for scroll animation + onEndReached + lazy load
137
- await new Promise(resolve => setTimeout(resolve, 1500));
138
-
139
- // Post-scroll no-op detection
140
- const offsetAfter = typeof scrollRef.scrollToOffset === 'function'
141
- ? scrollRef._scrollMetrics?.offset ?? 0
142
- : -1;
143
-
144
- if (offsetAfter >= 0 && Math.abs(offsetAfter - offsetBefore) < 2) {
145
- const edge = direction === 'down' ? 'bottom' : 'top';
146
- return `⚠️ Scroll had no effect — already at the ${edge} of ${container.label}. All visible content has been loaded.`;
147
- }
148
-
149
- const amountLabel = amount === 'toEnd' ? 'to end' : amount === 'toStart' ? 'to start' : `${direction} one page`;
150
- return `✅ Scrolled ${amountLabel} in ${container.label} (${container.componentName}). Check the updated screen content for newly loaded items.`;
151
- } catch (error: any) {
152
- return `❌ Scroll failed: ${error.message}`;
153
- }
154
- },
155
- };
156
- }
@@ -1,33 +0,0 @@
1
- import type { ToolDefinition } from '../core/types';
2
- import { globalZoneRegistry } from '../core/ZoneRegistry';
3
- import { logger } from '../utils/logger';
4
-
5
- export function createSimplifyTool(): ToolDefinition {
6
- return {
7
- name: 'simplify_zone',
8
- description: 'Simplify the UI in a specific Zone by telling it to hide elements marked as aiPriority="low". Use this when a screen zone looks cluttered and you want to reduce cognitive load for the user.',
9
- parameters: {
10
- zoneId: {
11
- type: 'string',
12
- description: 'The ID of the AIZone you want to simplify (e.g. from the screen layout context)',
13
- required: true,
14
- }
15
- },
16
- execute: async (args) => {
17
- const zoneId = String(args.zoneId);
18
-
19
- if (!globalZoneRegistry.isActionAllowed(zoneId, 'simplify')) {
20
- return `❌ Cannot simplify zone "${zoneId}": Zone does not exist or allowSimplify is false.`;
21
- }
22
-
23
- const zone = globalZoneRegistry.get(zoneId) as any;
24
- if (zone && zone._controller) {
25
- zone._controller.simplify();
26
- logger.info('SimplifyTool', `Simplified zone: ${zoneId}`);
27
- return `✅ Successfully requested simplification for zone "${zoneId}".`;
28
- }
29
-
30
- return `❌ Cannot simplify zone "${zoneId}": Controller not attached. Is the zone currently rendered on screen?`;
31
- },
32
- };
33
- }
@@ -1,65 +0,0 @@
1
- /**
2
- * Slider Tool — Adjust slider/seekbar values.
3
- *
4
- * Pattern from Detox:
5
- * - adjustSliderToPosition(normalizedPosition) — 0.0 to 1.0
6
- * - UISlider+DetoxUtils: value = normalized * (max - min) + min
7
- *
8
- * In JS: calls onValueChange(actualValue) then onSlidingComplete(actualValue).
9
- * Reads min/max from props (minimumValue/maximumValue).
10
- */
11
-
12
- import { walkFiberTree } from '../core/FiberTreeWalker';
13
- import type { AgentTool, ToolContext } from './types';
14
-
15
- export function createSliderTool(context: ToolContext): AgentTool {
16
- return {
17
- name: 'adjust_slider',
18
- description: 'Adjust a slider to a specific position. Use for sliders, seek bars, and range selectors. Value is normalized 0.0 (minimum) to 1.0 (maximum).',
19
- parameters: {
20
- index: { type: 'number', description: 'The index of the slider element', required: true },
21
- value: { type: 'number', description: 'Target position from 0.0 (min) to 1.0 (max)', required: true },
22
- },
23
- execute: async (args) => {
24
- const { interactives: elements } = walkFiberTree(context.getRootRef(), context.getWalkConfig());
25
- const element = elements.find(el => el.index === args.index);
26
- if (!element) {
27
- return `❌ Element with index ${args.index} not found.`;
28
- }
29
-
30
- // Validate normalized value
31
- const normalizedValue = Number(args.value);
32
- if (!Number.isFinite(normalizedValue) || normalizedValue < 0 || normalizedValue > 1) {
33
- return `❌ Slider value must be between 0.0 and 1.0, got ${args.value}`;
34
- }
35
-
36
- // Find onValueChange (Slider's primary callback)
37
- const onValueChange = element.props.onValueChange;
38
- if (!onValueChange || typeof onValueChange !== 'function') {
39
- return `❌ Element [${args.index}] "${element.label}" is not a slider (no onValueChange handler).`;
40
- }
41
-
42
- // Calculate actual value from normalized position
43
- // Pattern from Detox UISlider+DetoxUtils:
44
- // actualValue = normalized * (max - min) + min
45
- const min = element.props.minimumValue ?? 0;
46
- const max = element.props.maximumValue ?? 1;
47
- const actualValue = normalizedValue * (max - min) + min;
48
-
49
- try {
50
- // Call onValueChange (continuous feedback)
51
- onValueChange(actualValue);
52
-
53
- // Call onSlidingComplete if available (final value callback)
54
- if (element.props.onSlidingComplete && typeof element.props.onSlidingComplete === 'function') {
55
- element.props.onSlidingComplete(actualValue);
56
- }
57
-
58
- await new Promise(resolve => setTimeout(resolve, 500));
59
- return `✅ Adjusted slider [${args.index}] "${element.label}" to ${Math.round(normalizedValue * 100)}% (value: ${actualValue.toFixed(2)})`;
60
- } catch (error: any) {
61
- return `❌ Error adjusting slider: ${error.message}`;
62
- }
63
- },
64
- };
65
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * Tap Tool — Universal element interaction via onPress.
3
- *
4
- * Strategies (in priority order):
5
- * 1. Switch → onValueChange (toggle)
6
- * 2. Direct onPress on element
7
- * 3. Bubble up fiber tree to find parent onPress (max 5 levels)
8
- *
9
- * Includes Maestro-style tap verification:
10
- * - Captures element count + screen name before tap
11
- * - Compares after tap to detect dead taps
12
- */
13
-
14
- import { walkFiberTree } from '../core/FiberTreeWalker';
15
- import type { AgentTool, ToolContext } from './types';
16
-
17
- export function createTapTool(context: ToolContext): AgentTool {
18
- return {
19
- name: 'tap',
20
- description: 'Tap an interactive element by its index. Works universally on buttons, switches, and custom components.',
21
- parameters: {
22
- index: { type: 'number', description: 'The index of the element to tap', required: true },
23
- },
24
- execute: async (args) => {
25
- const walkResult = walkFiberTree(context.getRootRef(), context.getWalkConfig());
26
- const elements = walkResult.interactives;
27
- const element = elements.find(el => el.index === args.index);
28
- if (!element) {
29
- return `❌ Element with index ${args.index} not found. Available indexes: ${elements.map(e => e.index).join(', ')}`;
30
- }
31
-
32
- // Pre-tap snapshot for change detection (Pattern from Maestro: hierarchyBasedTap)
33
- const elementCountBefore = elements.length;
34
- const screenBefore = context.getCurrentScreenName();
35
-
36
- // Strategy 1: Switch → onValueChange
37
- if (element.type === 'switch' && element.props.onValueChange) {
38
- try {
39
- element.props.onValueChange(!element.props.value);
40
- await new Promise(resolve => setTimeout(resolve, 500));
41
- return `✅ Toggled [${args.index}] "${element.label}" to ${!element.props.value}`;
42
- } catch (error: any) {
43
- return `❌ Error toggling [${args.index}]: ${error.message}`;
44
- }
45
- }
46
-
47
- // Strategy 2: Direct onPress
48
- if (element.props.onPress) {
49
- try {
50
- element.props.onPress();
51
- await new Promise(resolve => setTimeout(resolve, 500));
52
-
53
- // Post-tap observation (Maestro pattern: compare hierarchy after tap)
54
- const postWalk = walkFiberTree(context.getRootRef(), context.getWalkConfig());
55
- const screenAfter = context.getCurrentScreenName();
56
- const elementCountAfter = postWalk.interactives.length;
57
-
58
- if (screenAfter !== screenBefore) {
59
- return `✅ Tapped [${args.index}] "${element.label}" → navigated to "${screenAfter}"`;
60
- }
61
- if (Math.abs(elementCountAfter - elementCountBefore) > 0) {
62
- return `✅ Tapped [${args.index}] "${element.label}" → screen updated (${elementCountBefore} → ${elementCountAfter} elements)`;
63
- }
64
- // Many valid taps (add-to-cart, favorites, API calls) update state
65
- // without changing visible UI elements. Report success and move on.
66
- return `✅ Tapped [${args.index}] "${element.label}" → action executed successfully. Proceed to your next step.`;
67
- } catch (error: any) {
68
- return `❌ Error tapping [${args.index}]: ${error.message}`;
69
- }
70
- }
71
-
72
- // Strategy 3: Bubble up fiber tree (like RNTL's findEventHandler)
73
- let fiber = element.fiberNode?.return;
74
- let bubbleDepth = 0;
75
- while (fiber && bubbleDepth < 5) {
76
- const parentProps = fiber.memoizedProps || {};
77
- if (parentProps.onPress && typeof parentProps.onPress === 'function') {
78
- try {
79
- parentProps.onPress();
80
- await new Promise(resolve => setTimeout(resolve, 500));
81
- return `✅ Tapped parent of [${args.index}] "${element.label}"`;
82
- } catch (error: any) {
83
- return `❌ Error tapping parent of [${args.index}]: ${error.message}`;
84
- }
85
- }
86
- fiber = fiber.return;
87
- bubbleDepth++;
88
- }
89
-
90
- return `❌ Element [${args.index}] "${element.label}" has no tap handler (no onPress or onValueChange found).`;
91
- },
92
- };
93
- }