@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
package/src/index.ts DELETED
@@ -1,83 +0,0 @@
1
- /**
2
- * @mobileai/react-native
3
- *
4
- * Zero-wrapper AI agent for React Native.
5
- * Auto-detects interactive elements via React Fiber tree traversal.
6
- */
7
-
8
- // ─── Components ──────────────────────────────────────────────
9
- export { AIAgent } from './components/AIAgent';
10
- export { AIZone } from './components/AIZone';
11
- // Built-in card templates for AIZone injection
12
- // Note: displayName is set explicitly on each — required for minification-safe template lookup.
13
- export { InfoCard } from './components/cards/InfoCard';
14
- export { ReviewSummary } from './components/cards/ReviewSummary';
15
-
16
- // ─── Providers ───────────────────────────────────────────────
17
- export { GeminiProvider } from './providers/GeminiProvider';
18
- export { OpenAIProvider } from './providers/OpenAIProvider';
19
- export { createProvider } from './providers/ProviderFactory';
20
-
21
- // ─── Hooks ───────────────────────────────────────────────────
22
- export { useAction, useAI } from './hooks/useAction';
23
-
24
- // ─── Services ────────────────────────────────────────────────
25
- export { VoiceService } from './services/VoiceService';
26
- export { AudioInputService } from './services/AudioInputService';
27
- export { AudioOutputService } from './services/AudioOutputService';
28
- export { KnowledgeBaseService } from './services/KnowledgeBaseService';
29
-
30
- // ─── Analytics ───────────────────────────────────────────────
31
- export { MobileAI } from './services/telemetry';
32
-
33
- // ─── Utilities ───────────────────────────────────────────────
34
- export { logger } from './utils/logger';
35
-
36
- // ─── Types ───────────────────────────────────────────────────
37
- export type {
38
- AgentConfig,
39
- AgentMode,
40
- ExecutionResult,
41
- InteractiveElement,
42
- DehydratedScreen,
43
- ToolDefinition,
44
- ActionDefinition,
45
- TokenUsage,
46
- KnowledgeEntry,
47
- KnowledgeRetriever,
48
- KnowledgeBaseConfig,
49
- ChatBarTheme,
50
- AIMessage,
51
- AIProviderName,
52
- ScreenMap,
53
- ScreenMapEntry,
54
- InteractionMode,
55
- } from './core/types';
56
-
57
- export type {
58
- VoiceServiceConfig,
59
- VoiceServiceCallbacks,
60
- VoiceStatus,
61
- } from './services/VoiceService';
62
-
63
- export type {
64
- TelemetryConfig,
65
- TelemetryEvent,
66
- } from './services/telemetry';
67
-
68
- // ─── Support Mode ────────────────────────────────────────────
69
- // SupportGreeting, CSATSurvey, buildSupportPrompt work standalone (no backend)
70
- // createEscalateTool works with provider='custom' (no backend)
71
- // EscalationSocket and provider='mobileai' require api.mobileai.dev
72
- export { SupportGreeting, CSATSurvey, buildSupportPrompt, createEscalateTool, EscalationSocket } from './support';
73
-
74
- export type {
75
- SupportModeConfig,
76
- QuickReply,
77
- EscalationConfig,
78
- EscalationContext,
79
- CSATConfig,
80
- CSATRating,
81
- BusinessHoursConfig,
82
- SupportTicket,
83
- } from './support';
@@ -1,98 +0,0 @@
1
- import type { ConfigPlugin } from 'expo/config-plugins';
2
- import * as path from 'path';
3
- import * as fs from 'fs';
4
- import { extractIntentsFromAST } from '../cli/generate-intents';
5
- import { generateSwiftCode } from '../cli/generate-swift';
6
-
7
- interface PluginOptions {
8
- /** The source directory to scan for useAction calls. Defaults to 'src' */
9
- scanDirectory?: string;
10
- /** App scheme for deep links. Defaults to the scheme in app.json */
11
- appScheme?: string;
12
- }
13
-
14
- const withAppIntents: ConfigPlugin<PluginOptions | void> = (config, options) => {
15
- let withXcodeProject: ((config: any, action: (config: any) => any) => any) | undefined;
16
- try {
17
- ({ withXcodeProject } = require('expo/config-plugins'));
18
- } catch {
19
- console.warn(
20
- '[MobileAI] `withAppIntents` requires `expo/config-plugins`. ' +
21
- 'Skipping App Intents generation because Expo config plugins are not available.'
22
- );
23
- return config;
24
- }
25
-
26
- if (!withXcodeProject) {
27
- return config;
28
- }
29
-
30
- const applyWithXcodeProject = withXcodeProject;
31
-
32
- return applyWithXcodeProject(config, async (config) => {
33
- const project = config.modResults;
34
- const projectName = config.modRequest.projectName || config.name;
35
- const projectRoot = config.modRequest.projectRoot;
36
-
37
- const scanDir = (options as PluginOptions)?.scanDirectory || 'src';
38
- const appScheme = (options as PluginOptions)?.appScheme || (Array.isArray(config.scheme) ? config.scheme[0] : config.scheme) || 'mobileai';
39
-
40
- try {
41
- // 1. Scan and Extract
42
- const scanPath = path.resolve(projectRoot, scanDir);
43
- console.log(`\n🤖 [MobileAI] Scanning ${scanPath} for AI Actions...`);
44
- const intents = extractIntentsFromAST(scanPath);
45
-
46
- console.log(`🤖 [MobileAI] Found ${intents.length} actions.`);
47
-
48
- // 2. Generate Swift Code
49
- // We write a temporary manifest to disk to use the CLI function,
50
- // or we can just adapt generateSwiftCode to take the object directly,
51
- // but the CLI expects a file path. Let's write a temporary file.
52
- const tmpManifestPath = path.join(projectRoot, '.mobileai-intent-manifest.tmp.json');
53
- fs.writeFileSync(tmpManifestPath, JSON.stringify(intents, null, 2));
54
-
55
- const swiftCode = generateSwiftCode(tmpManifestPath, appScheme);
56
-
57
- // Clean up tmp manifest
58
- if (fs.existsSync(tmpManifestPath)) {
59
- fs.unlinkSync(tmpManifestPath);
60
- }
61
-
62
- // 3. Write Swift File to iOS Project Directory
63
- const targetFilePath = path.join(projectRoot, 'ios', projectName, 'MobileAIAppIntents.swift');
64
-
65
- // Ensure directory exists
66
- const targetDir = path.dirname(targetFilePath);
67
- if (!fs.existsSync(targetDir)) {
68
- fs.mkdirSync(targetDir, { recursive: true });
69
- }
70
-
71
- fs.writeFileSync(targetFilePath, swiftCode);
72
- console.log(`🤖 [MobileAI] Generated ${targetFilePath}`);
73
-
74
- // 4. Link in Xcode
75
- const groupKey = project.findPBXGroupKey({ name: projectName });
76
- if (!groupKey) {
77
- console.warn(`🤖 [MobileAI] Warning: Could not find main PBXGroup for ${projectName}. You may need to manually add MobileAIAppIntents.swift to Xcode.`);
78
- return config;
79
- }
80
-
81
- // Check if already added
82
- const relativeFilePath = `${projectName}/MobileAIAppIntents.swift`;
83
- const fileAdded = project.hasFile(relativeFilePath);
84
-
85
- if (!fileAdded) {
86
- project.addSourceFile(relativeFilePath, null, groupKey);
87
- console.log(`🤖 [MobileAI] Linked MobileAIAppIntents.swift to Xcode project.`);
88
- }
89
-
90
- } catch (error) {
91
- console.error('🤖 [MobileAI] AppIntents generation failed:', error);
92
- }
93
-
94
- return config;
95
- });
96
- };
97
-
98
- export default withAppIntents;
@@ -1,357 +0,0 @@
1
- /**
2
- * GeminiProvider — Gemini API integration via @google/genai SDK.
3
- *
4
- * Uses the official Google GenAI SDK for:
5
- * - generateContent with structured function calling (agent_step)
6
- * - inlineData for vision (base64 screenshots)
7
- * - System instructions
8
- *
9
- * Implements the AIProvider interface so it can be swapped
10
- * with OpenAIProvider, AnthropicProvider, etc.
11
- */
12
-
13
- import { GoogleGenAI, FunctionCallingConfigMode, Type } from '@google/genai';
14
- import { logger } from '../utils/logger';
15
- import type { AIProvider, ToolDefinition, AgentStep, ProviderResult, AgentReasoning, TokenUsage } from '../core/types';
16
-
17
- // ─── Constants ─────────────────────────────────────────────────
18
-
19
- const AGENT_STEP_FN = 'agent_step';
20
-
21
- // Reasoning fields always present in the agent_step schema
22
- const REASONING_FIELDS = ['previous_goal_eval', 'memory', 'plan'] as const;
23
-
24
- // ─── Provider ──────────────────────────────────────────────────
25
-
26
- export class GeminiProvider implements AIProvider {
27
- private ai: GoogleGenAI;
28
- private model: string;
29
-
30
- constructor(
31
- apiKey?: string,
32
- model: string = 'gemini-2.5-flash',
33
- proxyUrl?: string,
34
- proxyHeaders?: Record<string, string>
35
- ) {
36
- const config: any = {};
37
-
38
- if (proxyUrl) {
39
- config.apiKey = 'proxy-key'; // Dummy key to bypass local validation
40
- config.httpOptions = {
41
- baseUrl: proxyUrl,
42
- headers: proxyHeaders || {},
43
- };
44
- } else if (apiKey) {
45
- config.apiKey = apiKey;
46
- } else {
47
- throw new Error('[mobileai] You must provide either "apiKey" or "proxyUrl" to AIAgent.');
48
- }
49
-
50
- this.ai = new GoogleGenAI(config);
51
- this.model = model;
52
- }
53
-
54
- async generateContent(
55
- systemPrompt: string,
56
- userMessage: string,
57
- tools: ToolDefinition[],
58
- history: AgentStep[],
59
- screenshot?: string,
60
- ): Promise<ProviderResult> {
61
-
62
- logger.info('GeminiProvider', `Sending request. Model: ${this.model}, Tools: ${tools.length}${screenshot ? ', with screenshot' : ''}`);
63
-
64
- // Build single agent_step function declaration
65
- const agentStepDeclaration = this.buildAgentStepDeclaration(tools);
66
-
67
- // Build contents (user message + optional screenshot)
68
- const contents = this.buildContents(userMessage, history, screenshot);
69
-
70
- const startTime = Date.now();
71
-
72
- try {
73
- const response = await this.ai.models.generateContent({
74
- model: this.model,
75
- contents,
76
- config: {
77
- systemInstruction: systemPrompt,
78
- tools: [{ functionDeclarations: [agentStepDeclaration] }],
79
- toolConfig: {
80
- functionCallingConfig: {
81
- mode: FunctionCallingConfigMode.ANY,
82
- allowedFunctionNames: [AGENT_STEP_FN],
83
- },
84
- },
85
- temperature: 0.2,
86
- maxOutputTokens: 2048,
87
- },
88
- });
89
-
90
- const elapsed = Date.now() - startTime;
91
- logger.info('GeminiProvider', `Response received in ${elapsed}ms`);
92
-
93
- // Extract token usage from SDK response
94
- const tokenUsage = this.extractTokenUsage(response);
95
- if (tokenUsage) {
96
- logger.info('GeminiProvider', `Tokens: ${tokenUsage.promptTokens} in / ${tokenUsage.completionTokens} out / $${tokenUsage.estimatedCostUSD.toFixed(6)}`);
97
- }
98
-
99
- const result = this.parseAgentStepResponse(response, tools);
100
- result.tokenUsage = tokenUsage;
101
- return result;
102
- } catch (error: any) {
103
- logger.error('GeminiProvider', 'Request failed:', error.message);
104
-
105
- if (error.status) {
106
- throw new Error(this.formatProviderError(error.status, error.message));
107
- }
108
- throw error;
109
- }
110
- }
111
-
112
- // ─── Build agent_step Declaration ──────────────────────────
113
-
114
- /**
115
- * Builds a single `agent_step` function declaration that combines:
116
- * - Structured reasoning fields (previous_goal_eval, memory, plan)
117
- * - action_name (enum of all available tool names)
118
- * - All tool parameter fields as flat top-level properties
119
- *
120
- * Flat schema avoids Gemini's "deeply nested schema" rejection in ANY mode.
121
- */
122
- private buildAgentStepDeclaration(tools: ToolDefinition[]): any {
123
- const toolNames = tools.map(t => t.name);
124
-
125
- // Collect all unique parameter fields across all tools
126
- const actionProperties: Record<string, any> = {};
127
- for (const tool of tools) {
128
- for (const [paramName, param] of Object.entries(tool.parameters)) {
129
- if (actionProperties[paramName]) continue;
130
- actionProperties[paramName] = {
131
- type: this.mapParamType(param.type),
132
- description: param.description,
133
- ...(param.enum ? { enum: param.enum } : {}),
134
- };
135
- }
136
- }
137
-
138
- // Build tool descriptions for the action_name enum
139
- const toolDescriptions = tools
140
- .map(t => {
141
- const params = Object.keys(t.parameters).join(', ');
142
- return `- ${t.name}(${params}): ${t.description}`;
143
- })
144
- .join('\n');
145
-
146
- return {
147
- name: AGENT_STEP_FN,
148
- description: `Execute one agent step. Choose an action and provide reasoning.\n\nAvailable actions:\n${toolDescriptions}`,
149
- parameters: {
150
- type: Type.OBJECT,
151
- properties: {
152
- previous_goal_eval: {
153
- type: Type.STRING,
154
- description: 'One-sentence assessment of your last action. State success, failure, or uncertain. Skip on first step.',
155
- },
156
- memory: {
157
- type: Type.STRING,
158
- description: 'Key facts to remember for future steps: progress made, items found, counters, field values already collected.',
159
- },
160
- plan: {
161
- type: Type.STRING,
162
- description: 'Your immediate next goal — what action you will take and why.',
163
- },
164
- action_name: {
165
- type: Type.STRING,
166
- description: 'Which action to execute.',
167
- enum: toolNames,
168
- },
169
- ...actionProperties,
170
- },
171
- required: ['plan', 'action_name'],
172
- },
173
- };
174
- }
175
-
176
- private mapParamType(type: string): string {
177
- switch (type) {
178
- case 'number': return Type.NUMBER;
179
- case 'integer': return Type.INTEGER;
180
- case 'boolean': return Type.BOOLEAN;
181
- case 'string':
182
- default: return Type.STRING;
183
- }
184
- }
185
-
186
- // ─── Build Contents ────────────────────────────────────────
187
-
188
- /**
189
- * Builds contents for the generateContent call.
190
- * Single-turn: user message + optional screenshot as inlineData.
191
- */
192
- private buildContents(userMessage: string, _history: AgentStep[], screenshot?: string): any[] {
193
- const parts: any[] = [{ text: userMessage }];
194
-
195
- // Append screenshot as inlineData for Gemini vision
196
- if (screenshot) {
197
- parts.push({
198
- inlineData: {
199
- mimeType: 'image/jpeg',
200
- data: screenshot,
201
- },
202
- });
203
- }
204
-
205
- return [{ role: 'user', parts }];
206
- }
207
-
208
- // ─── Parse Response ────────────────────────────────────────
209
-
210
- /**
211
- * Parses the SDK response expecting a single agent_step function call.
212
- * Extracts structured reasoning + action.
213
- */
214
- private parseAgentStepResponse(response: any, tools: ToolDefinition[]): ProviderResult {
215
- const candidates = response.candidates || [];
216
-
217
- if (candidates.length === 0) {
218
- logger.warn('GeminiProvider', 'No candidates in response');
219
- return {
220
- toolCalls: [{ name: 'done', args: { text: 'No response generated.', success: false } }],
221
- reasoning: { previousGoalEval: '', memory: '', plan: '' },
222
- text: 'No response generated.',
223
- };
224
- }
225
-
226
- const candidate = candidates[0];
227
- const parts = candidate.content?.parts || [];
228
-
229
- // Find the function call part
230
- const fnCallPart = parts.find((p: any) => p.functionCall);
231
- const textPart = parts.find((p: any) => p.text);
232
-
233
- if (!fnCallPart?.functionCall) {
234
- logger.warn('GeminiProvider', 'No function call in response. Text:', textPart?.text);
235
- return {
236
- toolCalls: [{ name: 'done', args: { text: textPart?.text || 'No action taken.', success: false } }],
237
- reasoning: { previousGoalEval: '', memory: '', plan: '' },
238
- text: textPart?.text,
239
- };
240
- }
241
-
242
- const args = fnCallPart.functionCall.args || {};
243
-
244
- // Extract reasoning fields
245
- const reasoning: AgentReasoning = {
246
- previousGoalEval: args.previous_goal_eval || '',
247
- memory: args.memory || '',
248
- plan: args.plan || '',
249
- };
250
-
251
- // Extract action
252
- const actionName = args.action_name;
253
- if (!actionName) {
254
- logger.warn('GeminiProvider', 'No action_name in agent_step. Falling back to done.');
255
- return {
256
- toolCalls: [{ name: 'done', args: { text: 'Agent did not choose an action.', success: false } }],
257
- reasoning,
258
- text: textPart?.text,
259
- };
260
- }
261
-
262
- // Build action args: extract only the params that belong to the matched tool
263
- const actionArgs: Record<string, any> = {};
264
- const reservedKeys = new Set([...REASONING_FIELDS, 'action_name']);
265
-
266
- const matchedTool = tools.find(t => t.name === actionName);
267
- if (matchedTool) {
268
- for (const paramName of Object.keys(matchedTool.parameters)) {
269
- if (args[paramName] !== undefined) {
270
- actionArgs[paramName] = args[paramName];
271
- }
272
- }
273
- } else {
274
- for (const [key, value] of Object.entries(args)) {
275
- if (!reservedKeys.has(key)) {
276
- actionArgs[key] = value;
277
- }
278
- }
279
- }
280
-
281
- logger.info('GeminiProvider', `Parsed: action=${actionName}, plan="${reasoning.plan}"`);
282
-
283
- return {
284
- toolCalls: [{ name: actionName, args: actionArgs }],
285
- reasoning,
286
- text: textPart?.text,
287
- };
288
- }
289
-
290
- // ─── Token Usage Extraction ─────────────────────────────────
291
-
292
- /**
293
- * Extracts token usage from SDK response and calculates estimated cost.
294
- *
295
- * Pricing (Gemini 2.5 Flash):
296
- * - Input: $0.30 / 1M tokens
297
- * - Output: $2.50 / 1M tokens
298
- */
299
- private extractTokenUsage(response: any): TokenUsage | undefined {
300
- const meta = response?.usageMetadata;
301
- if (!meta) return undefined;
302
-
303
- const promptTokens = meta.promptTokenCount ?? 0;
304
- const completionTokens = meta.candidatesTokenCount ?? 0;
305
- const totalTokens = meta.totalTokenCount ?? (promptTokens + completionTokens);
306
-
307
- // Cost estimation based on Gemini 2.5 Flash pricing
308
- const INPUT_COST_PER_M = 0.30;
309
- const OUTPUT_COST_PER_M = 2.50;
310
-
311
- const estimatedCostUSD =
312
- (promptTokens / 1_000_000) * INPUT_COST_PER_M +
313
- (completionTokens / 1_000_000) * OUTPUT_COST_PER_M;
314
-
315
- return { promptTokens, completionTokens, totalTokens, estimatedCostUSD };
316
- }
317
-
318
- // ─── Error Formatting ──────────────────────────────────────
319
-
320
- /**
321
- * Converts raw API errors into clean, user-friendly messages.
322
- * Parses JSON error bodies and maps HTTP codes to plain language.
323
- */
324
- private formatProviderError(status: number, rawMessage: string): string {
325
- // Try to extract the human-readable message from JSON body
326
- let humanMessage = '';
327
- try {
328
- const parsed = JSON.parse(rawMessage);
329
- humanMessage = parsed?.error?.message || parsed?.message || '';
330
- } catch {
331
- // rawMessage may contain JSON embedded in a string like "503: {json}"
332
- const jsonMatch = rawMessage.match(/\{[\s\S]*\}/);
333
- if (jsonMatch) {
334
- try {
335
- const parsed = JSON.parse(jsonMatch[0]);
336
- humanMessage = parsed?.error?.message || parsed?.message || '';
337
- } catch { /* ignore */ }
338
- }
339
- }
340
-
341
- // Map status codes to friendly descriptions
342
- switch (status) {
343
- case 429:
344
- return humanMessage || 'Too many requests. Please wait a moment and try again.';
345
- case 503:
346
- return humanMessage || 'The AI service is temporarily unavailable. Please try again shortly.';
347
- case 500:
348
- return humanMessage || 'The AI service encountered an internal error. Please try again.';
349
- case 401:
350
- return 'Authentication failed. Please check your API key.';
351
- case 403:
352
- return 'Access denied. Your API key may not have the required permissions.';
353
- default:
354
- return humanMessage || `Something went wrong (${status}). Please try again.`;
355
- }
356
- }
357
- }