@mobileai/react-native 0.9.27 → 0.9.29

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 (65) hide show
  1. package/README.md +28 -16
  2. package/android/build.gradle +17 -0
  3. package/android/src/main/java/com/mobileai/overlay/FloatingOverlayDialogRootViewGroup.kt +243 -0
  4. package/android/src/main/java/com/mobileai/overlay/FloatingOverlayView.kt +281 -87
  5. package/android/src/newarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +52 -17
  6. package/android/src/oldarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +49 -2
  7. package/bin/generate-map.cjs +45 -6
  8. package/ios/MobileAIFloatingOverlayComponentView.h +8 -0
  9. package/ios/MobileAIFloatingOverlayComponentView.mm +12 -41
  10. package/ios/Podfile +63 -0
  11. package/ios/Podfile.lock +2290 -0
  12. package/ios/Podfile.properties.json +4 -0
  13. package/ios/mobileaireactnative/AppDelegate.swift +69 -0
  14. package/ios/mobileaireactnative/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
  15. package/ios/mobileaireactnative/Images.xcassets/Contents.json +6 -0
  16. package/ios/mobileaireactnative/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +21 -0
  17. package/ios/mobileaireactnative/Images.xcassets/SplashScreenLegacy.imageset/SplashScreenLegacy.png +0 -0
  18. package/ios/mobileaireactnative/Info.plist +55 -0
  19. package/ios/mobileaireactnative/PrivacyInfo.xcprivacy +48 -0
  20. package/ios/mobileaireactnative/SplashScreen.storyboard +47 -0
  21. package/ios/mobileaireactnative/Supporting/Expo.plist +6 -0
  22. package/ios/mobileaireactnative/mobileaireactnative-Bridging-Header.h +3 -0
  23. package/ios/mobileaireactnative.xcodeproj/project.pbxproj +547 -0
  24. package/ios/mobileaireactnative.xcodeproj/xcshareddata/xcschemes/mobileaireactnative.xcscheme +88 -0
  25. package/ios/mobileaireactnative.xcworkspace/contents.xcworkspacedata +10 -0
  26. package/lib/module/components/AIAgent.js +501 -191
  27. package/lib/module/components/AgentChatBar.js +250 -59
  28. package/lib/module/components/FloatingOverlayWrapper.js +68 -32
  29. package/lib/module/config/endpoints.js +22 -1
  30. package/lib/module/core/AgentRuntime.js +110 -8
  31. package/lib/module/core/FiberTreeWalker.js +211 -10
  32. package/lib/module/core/OutcomeVerifier.js +149 -0
  33. package/lib/module/core/systemPrompt.js +96 -25
  34. package/lib/module/providers/GeminiProvider.js +9 -3
  35. package/lib/module/services/telemetry/TelemetryService.js +21 -2
  36. package/lib/module/services/telemetry/TouchAutoCapture.js +235 -38
  37. package/lib/module/services/telemetry/analyticsLabeling.js +187 -0
  38. package/lib/module/specs/FloatingOverlayNativeComponent.ts +7 -1
  39. package/lib/module/support/supportPrompt.js +22 -7
  40. package/lib/module/support/supportStyle.js +55 -0
  41. package/lib/module/support/types.js +2 -0
  42. package/lib/module/tools/typeTool.js +20 -0
  43. package/lib/module/utils/humanizeScreenName.js +49 -0
  44. package/lib/typescript/src/components/AIAgent.d.ts +6 -2
  45. package/lib/typescript/src/components/AgentChatBar.d.ts +15 -1
  46. package/lib/typescript/src/components/FloatingOverlayWrapper.d.ts +22 -10
  47. package/lib/typescript/src/config/endpoints.d.ts +4 -0
  48. package/lib/typescript/src/core/AgentRuntime.d.ts +12 -3
  49. package/lib/typescript/src/core/FiberTreeWalker.d.ts +12 -1
  50. package/lib/typescript/src/core/OutcomeVerifier.d.ts +46 -0
  51. package/lib/typescript/src/core/systemPrompt.d.ts +3 -10
  52. package/lib/typescript/src/core/types.d.ts +63 -0
  53. package/lib/typescript/src/index.d.ts +1 -0
  54. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts +7 -1
  55. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts +6 -1
  56. package/lib/typescript/src/services/telemetry/analyticsLabeling.d.ts +20 -0
  57. package/lib/typescript/src/services/telemetry/types.d.ts +1 -1
  58. package/lib/typescript/src/specs/FloatingOverlayNativeComponent.d.ts +5 -0
  59. package/lib/typescript/src/support/index.d.ts +1 -0
  60. package/lib/typescript/src/support/supportStyle.d.ts +9 -0
  61. package/lib/typescript/src/support/types.d.ts +3 -0
  62. package/lib/typescript/src/utils/humanizeScreenName.d.ts +6 -0
  63. package/package.json +10 -10
  64. package/src/specs/FloatingOverlayNativeComponent.ts +7 -1
  65. package/ios/MobileAIPilotIntents.swift +0 -51
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+
3
+ const GENERIC_LABELS = new Set(['button', 'buttons', 'component', 'components', 'container', 'containers', 'content', 'cta', 'item', 'items', 'label', 'labels', 'root', 'row', 'rows', 'screen', 'screens', 'text', 'texts', 'title', 'titles', 'unknown', 'value', 'values', 'view', 'views', 'wrapper', 'wrappers']);
4
+ const GENERIC_IDENTIFIER_TOKENS = new Set(['btn', 'button', 'card', 'cell', 'component', 'container', 'content', 'cta', 'icon', 'input', 'item', 'label', 'node', 'pressable', 'root', 'row', 'screen', 'target', 'text', 'tile', 'toggle', 'view', 'wrapper']);
5
+ const INTERNAL_NAME_PATTERNS = [/^RCT[A-Z]/, /^React/, /^TextImpl/i, /^Android/i, /^UI[A-Z]/, /^RN[A-Z]/, /^Virtualized/i, /^ScrollResponder$/i, /^Animated(Component|.*Wrapper)?$/i, /^Touchable[A-Z]/, /^Pressable$/i, /^View$/i, /^Text$/i, /^Modal$/i, /Legacy/i, /Wrapper/i, /Context$/i, /Provider$/i];
6
+ function normalizeWhitespace(value) {
7
+ if (!value) return '';
8
+ return String(value).replace(/\s+/g, ' ').trim();
9
+ }
10
+ function toTitleCase(value) {
11
+ return value.split(/\s+/).filter(Boolean).map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
12
+ }
13
+ function humanizeIdentifier(value) {
14
+ const humanized = value.replace(/^icon:/i, '').replace(/[_./-]+/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\b\d+\b/g, ' ').replace(/\s+/g, ' ').trim();
15
+ return toTitleCase(humanized);
16
+ }
17
+ function stripDecorators(value) {
18
+ return value.replace(/^\[[^\]]+\]\s*/g, '').replace(/^["'`]+|["'`]+$/g, '').trim();
19
+ }
20
+ function looksInternal(value) {
21
+ return INTERNAL_NAME_PATTERNS.some(pattern => pattern.test(value));
22
+ }
23
+ function isLowSignalValue(value) {
24
+ const lowered = value.toLowerCase();
25
+ return GENERIC_LABELS.has(lowered);
26
+ }
27
+ function sanitizeLabelValue(rawValue, source) {
28
+ let normalized = normalizeWhitespace(rawValue);
29
+ if (!normalized) return null;
30
+ normalized = stripDecorators(normalized);
31
+ if (!normalized) return null;
32
+ if (source === 'test-id' || source === 'icon' || source === 'context') {
33
+ normalized = humanizeIdentifier(normalized);
34
+ }
35
+ if (!normalized) return null;
36
+ if (normalized.length > 80) return null;
37
+ if (looksInternal(normalized)) return null;
38
+ const words = normalized.split(/\s+/).filter(Boolean);
39
+ if (words.length === 0) return null;
40
+ if (words.length === 1) {
41
+ const token = words[0].toLowerCase();
42
+ if (GENERIC_IDENTIFIER_TOKENS.has(token) || GENERIC_LABELS.has(token)) {
43
+ return null;
44
+ }
45
+ }
46
+ if (isLowSignalValue(normalized)) return null;
47
+ return normalized;
48
+ }
49
+ function scoreAnalyticsLabel(label, source, isInteractiveContext = false) {
50
+ let score = 0;
51
+ const words = label.split(/\s+/).filter(Boolean);
52
+ if (source === 'accessibility') score += 95;
53
+ if (source === 'deep-text') score += 78;
54
+ if (source === 'sibling-text') score += 64;
55
+ if (source === 'title') score += 56;
56
+ if (source === 'placeholder') score += 42;
57
+ if (source === 'context') score += 12;
58
+ if (source === 'icon') score -= 8;
59
+ if (source === 'test-id') score -= 14;
60
+ if (isInteractiveContext) score += 18;
61
+ if (words.length >= 2 && words.length <= 6) score += 20;else if (words.length === 1) score += 4;
62
+ if (label.length >= 4 && label.length <= 36) score += 18;else if (label.length > 56) score -= 20;
63
+ if (/^[A-Z]/.test(label)) score += 8;
64
+ if (/[A-Za-z]/.test(label) && !/[_./]/.test(label)) score += 10;
65
+ return score;
66
+ }
67
+ export function getFallbackAnalyticsLabel(elementKind) {
68
+ switch (elementKind) {
69
+ case 'button':
70
+ return 'Primary action';
71
+ case 'text_input':
72
+ return 'Text input';
73
+ case 'toggle':
74
+ return 'Toggle';
75
+ case 'picker':
76
+ return 'Picker';
77
+ case 'slider':
78
+ return 'Slider';
79
+ case 'link':
80
+ return 'Link';
81
+ case 'tab':
82
+ return 'Tab';
83
+ case 'list_item':
84
+ return 'List item';
85
+ case 'image':
86
+ return 'Image';
87
+ case 'icon':
88
+ return 'Icon';
89
+ case 'text':
90
+ return 'Text';
91
+ case 'card':
92
+ return 'Card';
93
+ case 'modal':
94
+ return 'Modal';
95
+ case 'sheet':
96
+ return 'Bottom sheet';
97
+ case 'scroll_area':
98
+ return 'Scrollable area';
99
+ default:
100
+ return null;
101
+ }
102
+ }
103
+ export function getAnalyticsElementKind(elementType) {
104
+ switch (elementType) {
105
+ case 'pressable':
106
+ case 'radio':
107
+ case 'button':
108
+ case 'checkbox':
109
+ return 'button';
110
+ case 'link':
111
+ return 'link';
112
+ case 'tab':
113
+ case 'tabbar':
114
+ return 'tab';
115
+ case 'listitem':
116
+ case 'list-item':
117
+ case 'menuitem':
118
+ return 'list_item';
119
+ case 'text-input':
120
+ case 'text_input':
121
+ case 'textinput':
122
+ return 'text_input';
123
+ case 'switch':
124
+ case 'toggle':
125
+ return 'toggle';
126
+ case 'slider':
127
+ return 'slider';
128
+ case 'picker':
129
+ return 'picker';
130
+ case 'date-picker':
131
+ case 'select':
132
+ case 'dropdown':
133
+ return 'picker';
134
+ case 'image':
135
+ case 'imagebutton':
136
+ return 'image';
137
+ case 'icon':
138
+ return 'icon';
139
+ case 'text':
140
+ case 'label':
141
+ case 'header':
142
+ return 'text';
143
+ case 'card':
144
+ return 'card';
145
+ case 'modal':
146
+ return 'modal';
147
+ case 'sheet':
148
+ case 'bottomsheet':
149
+ return 'sheet';
150
+ case 'scrollable':
151
+ case 'scrollview':
152
+ case 'flatlist':
153
+ case 'sectionlist':
154
+ case 'adjustable':
155
+ return 'scroll_area';
156
+ default:
157
+ return 'unknown';
158
+ }
159
+ }
160
+ export function chooseBestAnalyticsTarget(candidates, elementKind) {
161
+ let best;
162
+ for (const candidate of candidates) {
163
+ const label = sanitizeLabelValue(candidate.text, candidate.source);
164
+ if (!label) continue;
165
+ const score = scoreAnalyticsLabel(label, candidate.source, candidate.isInteractiveContext === true);
166
+ if (!best || score > best.score) {
167
+ best = {
168
+ label,
169
+ score
170
+ };
171
+ }
172
+ }
173
+ if (best) {
174
+ const labelConfidence = best.score >= 100 ? 'high' : 'low';
175
+ return {
176
+ label: best.label,
177
+ elementKind,
178
+ labelConfidence
179
+ };
180
+ }
181
+ return {
182
+ label: getFallbackAnalyticsLabel(elementKind),
183
+ elementKind,
184
+ labelConfidence: 'low'
185
+ };
186
+ }
187
+ //# sourceMappingURL=analyticsLabeling.js.map
@@ -11,9 +11,15 @@
11
11
  */
12
12
 
13
13
  import type { ViewProps } from 'react-native';
14
+ import type { Int32 } from 'react-native/Libraries/Types/CodegenTypes';
14
15
  import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
15
16
 
16
- export interface NativeProps extends ViewProps {}
17
+ export interface NativeProps extends ViewProps {
18
+ windowX?: Int32;
19
+ windowY?: Int32;
20
+ windowWidth?: Int32;
21
+ windowHeight?: Int32;
22
+ }
17
23
 
18
24
  // Codegen reads this export to generate the native component interfaces.
19
25
  export default codegenNativeComponent<NativeProps>('MobileAIFloatingOverlay');
@@ -6,12 +6,16 @@
6
6
  * Uses POSITIVE framing (what TO DO) instead of negative rules (per user's prompt engineering rules).
7
7
  */
8
8
 
9
+ import { buildSupportStylePrompt, resolveSupportStyle } from "./supportStyle.js";
10
+
9
11
  /**
10
12
  * Build the support mode system prompt addition.
11
13
  * This gets appended to the main system prompt when support mode is active.
12
14
  */
13
15
  export function buildSupportPrompt(config) {
14
16
  const parts = [];
17
+ const supportStyle = config.persona?.preset ?? 'warm-concise';
18
+ const stylePreset = resolveSupportStyle(supportStyle);
15
19
 
16
20
  // Core support persona
17
21
  parts.push(`
@@ -20,18 +24,26 @@ export function buildSupportPrompt(config) {
20
24
  You are a helpful customer support assistant representing the company. Your primary goal is to RESOLVE the user's issue through empathetic conversation. App navigation is a tool you USE when needed, not the first thing you propose.
21
25
 
22
26
  ### Identity & Context
23
- Adopt the persona of a dedicated human customer support team member. Speak on behalf of the company as an organization with human operational timelines.
27
+ Speak like a calm, caring human teammate. Sound emotionally safe, patient, and kind in every reply.
28
+
29
+ Treat any question about "you" or "when you will reply" as referring to the company's real support process, but explain that in warm, human language rather than corporate language. Reassure the user naturally, and make them feel cared for while you work on the issue.
24
30
 
25
- Base all discussions regarding processing, reviews, resolutions, and response expectations on standard operational business timelines. Treat the conversational context holistically—assume any user questions about "you" or "when you will reply" refer to the company's human support staff processing their real-world request. Express empathy naturally, and assure the user that the operational team is handling their ticket promptly.
31
+ ### Gentle Customer Care
32
+ - Lead with warmth before action.
33
+ - Make the user feel supported, not processed.
34
+ - Even when you need details or need to say no, keep your wording soft and respectful.
35
+ - Avoid cold, legalistic, commanding, or overly procedural phrasing.
36
+ - If something failed, acknowledge the frustration first, then guide the user gently toward the next step.
26
37
 
27
38
  ### Support Resolution Protocol (HEARD)
28
39
  Follow this sequence. Exhaust each level before moving to the next:
29
40
 
30
- 1. HEAR: Listen actively. Paraphrase the problem back to confirm you understand. Ask specific
31
- clarifying questions (which order? when? what happened exactly?).
41
+ 1. HEAR: Listen actively. Paraphrase the problem back to confirm you understand. Ask gentle,
42
+ specific clarifying questions (for example: which order, when it happened, and what went wrong).
32
43
 
33
44
  2. EMPATHIZE: Acknowledge the user's feelings with sincerity. Use their name if available.
34
- Say "I understand how frustrating this must be" not "I see you have an issue."
45
+ Use a genuine, varied phrase for example: "I hear you", "That makes total sense", "I'm sorry about that".
46
+ Avoid scripted lines like "I understand how frustrating this must be" — they sound hollow.
35
47
  Take responsibility where appropriate.
36
48
 
37
49
  3. ANSWER: Search the knowledge base (query_knowledge) for relevant policies, FAQs, and procedures.
@@ -49,6 +61,7 @@ Follow this sequence. Exhaust each level before moving to the next:
49
61
  5. DIAGNOSE: After resolution, briefly identify the root cause if visible
50
62
  (e.g. "It looks like the delivery partner marked it as delivered prematurely").
51
63
  Ask the user if the issue is fully resolved before calling done().`);
64
+ parts.push(buildSupportStylePrompt(supportStyle));
52
65
  parts.push(`
53
66
  ### Consent and Liability Guard
54
67
  - Treat money movement, subscription cancellation, deletion, final submission, and account/security changes as high-risk actions.
@@ -68,7 +81,7 @@ Follow this sequence. Exhaust each level before moving to the next:
68
81
  ### Progress Communication
69
82
  When executing a multi-step resolution, you must communicate your progress to keep the user informed.
70
83
  - Do NOT execute more than 2 tools in silence.
71
- - Use the 'ask_user' tool to say phrases like "I am checking your account details now..." or "Just a moment while I pull up that information."
84
+ - Use the 'ask_user' tool to say phrases like "Let me check that for you now." or "Just a moment while I pull that up."
72
85
  - Never leave the user waiting in silence during complex operations.`);
73
86
 
74
87
  // Agent Persona
@@ -80,9 +93,11 @@ When executing a multi-step resolution, you must communicate your progress to ke
80
93
  } = config.persona;
81
94
  let personaStr = `\n### AI Persona & Tone\n`;
82
95
  if (agentName) personaStr += `- Your name is ${agentName}. Introduce yourself if appropriate.\n`;
83
- if (tone) personaStr += `- Maintain a ${tone} tone throughout the conversation.\n`;
96
+ personaStr += `- Maintain a ${tone || stylePreset.tone} tone throughout the conversation.\n`;
84
97
  if (signOff) personaStr += `- When resolving an issue, sign off with: "${signOff}".\n`;
85
98
  parts.push(personaStr);
99
+ } else {
100
+ parts.push(`\n### AI Persona & Tone\n- Maintain a ${stylePreset.tone} tone throughout the conversation.\n`);
86
101
  }
87
102
 
88
103
  // Custom system context from the consumer
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ const PRESETS = {
4
+ /**
5
+ * Default — calm, human, to the point.
6
+ * Sounds like a competent teammate who genuinely cares, not a scripted bot.
7
+ */
8
+ 'warm-concise': {
9
+ tone: 'calm, warm, and direct',
10
+ prompt: `
11
+ ### Support Style: Warm Concise
12
+ - Sound like a calm human who is good at their job and genuinely wants to help.
13
+ - Acknowledge the situation once — then focus on solving it. Do not repeat the same empathy phrase twice in a conversation.
14
+ - Keep each message short and scannable (1-3 sentences). Users are on mobile.
15
+ - Use natural language: say "Got it" not "I understand your concern"; say "Let me check" not "I will certainly look into that for you".
16
+ - When something went wrong, own it simply and move on: "That shouldn't have happened — let me fix it."
17
+ - Sound confident and in control. Users trust agents who know what to do.
18
+ - Vary your acknowledgment phrases naturally: "I hear you", "Got it", "That makes sense", "Let's sort this out", "On it" — never repeat the same one twice in a row.`
19
+ },
20
+ /**
21
+ * WOW Service — warm, proactive, surprise-and-delight energy.
22
+ * Best for consumer apps where the brand identity is warm and friendly.
23
+ */
24
+ 'wow-service': {
25
+ tone: 'warm, proactive, and genuinely service-first',
26
+ prompt: `
27
+ ### Support Style: WOW Service
28
+ - Deliver genuinely memorable service — through helpfulness and warmth, not adjectives.
29
+ - Be human and real: a little personality is good, but stay grounded. Never be performatively cheerful.
30
+ - Own mistakes fast and recover without excuses: "That's on us — here's what I'll do."
31
+ - Look for small ways to go above and beyond after resolving the core issue.
32
+ - Never joke through a serious problem. Match the user's energy — if they're frustrated, be calm and direct, not upbeat.`
33
+ },
34
+ /**
35
+ * Neutral Professional — composed, clear, efficient.
36
+ * Best for fintech, healthcare, or enterprise apps.
37
+ */
38
+ 'neutral-professional': {
39
+ tone: 'clear, composed, and respectfully direct',
40
+ prompt: `
41
+ ### Support Style: Neutral Professional
42
+ - Prioritize clarity and efficiency above all. Every sentence should move the conversation forward.
43
+ - Stay warm but understated — acknowledge the issue, then get straight to the solution.
44
+ - Use plain, jargon-free language. Short sentences. No exclamation marks.
45
+ - Be transparent about what you know, what you're checking, and what the next step is.
46
+ - Avoid casual phrases or emotional flourish — composed professionalism builds trust here.`
47
+ }
48
+ };
49
+ export function resolveSupportStyle(style) {
50
+ return PRESETS[style ?? 'warm-concise'];
51
+ }
52
+ export function buildSupportStylePrompt(style) {
53
+ return resolveSupportStyle(style).prompt;
54
+ }
55
+ //# sourceMappingURL=supportStyle.js.map
@@ -1,2 +1,4 @@
1
1
  "use strict";
2
+
3
+ export {};
2
4
  //# sourceMappingURL=types.js.map
@@ -85,6 +85,21 @@ export function createTypeTool(context) {
85
85
  }
86
86
  }
87
87
 
88
+ // ── Strategy 1b: inner onChangeText (for wrapped 3rd-party inputs) ────
89
+ if (fiberNode) {
90
+ const innerTextInputFiber = findFiberNode(fiberNode, n => typeof getProps(n)?.onChangeText === 'function', 15);
91
+ if (innerTextInputFiber) {
92
+ const innerProps = getProps(innerTextInputFiber);
93
+ try {
94
+ innerProps.onChangeText(args.text);
95
+ await new Promise(resolve => setTimeout(resolve, 300));
96
+ return `✅ Typed "${args.text}" into wrapped component [${args.index}] "${label}"`;
97
+ } catch (err) {
98
+ console.warn(`[Agent] Inner onChangeText failed for element ${args.index}:`, err);
99
+ }
100
+ }
101
+ }
102
+
88
103
  // ── Strategy 2: uncontrolled — find native onChange + setNativeProps ──
89
104
  // For TextInputs with defaultValue only.
90
105
  // We need BOTH:
@@ -106,10 +121,12 @@ export function createTypeTool(context) {
106
121
  if (onChange || nativeInstance) {
107
122
  try {
108
123
  // Step 1: Update visual text in native view
124
+ let visualUpdated = false;
109
125
  if (nativeInstance && typeof nativeInstance.setNativeProps === 'function') {
110
126
  nativeInstance.setNativeProps({
111
127
  text: args.text
112
128
  });
129
+ visualUpdated = true;
113
130
  }
114
131
 
115
132
  // Step 2: Notify React's internal onChange so lastNativeText stays in sync
@@ -122,6 +139,9 @@ export function createTypeTool(context) {
122
139
  }
123
140
  });
124
141
  }
142
+ if (!visualUpdated) {
143
+ return `❌ Type failed: Cannot locate native host node to explicitly inject visual text into uncontrolled element [${args.index}].`;
144
+ }
125
145
  await new Promise(resolve => setTimeout(resolve, 300));
126
146
  return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
127
147
  } catch (err) {
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Transforms raw navigation route names into human-readable labels.
5
+ * Designed to handle both Expo Router (file-based) and React Navigation conventions.
6
+ */
7
+ export function humanizeScreenName(route) {
8
+ if (!route) return '';
9
+ let name = route;
10
+
11
+ // 1. Strip Expo Router groups: e.g., "(tabs)/index" -> "index"
12
+ // Keep replacing in case of nested groups like "(app)/(tabs)/home"
13
+ name = name.replace(/\([^)]+\)\//g, '');
14
+
15
+ // 2. Skip internal layout and catch-all routes
16
+ if (name.includes('_layout') || name.includes('[...')) {
17
+ return '';
18
+ }
19
+
20
+ // 3. Handle nested indexes: "settings/index" -> "settings"
21
+ if (name.endsWith('/index')) {
22
+ name = name.replace(/\/index$/, '');
23
+ }
24
+
25
+ // 4. Special case root index
26
+ if (name === 'index') {
27
+ return 'Home';
28
+ }
29
+
30
+ // 5. Strip dynamic brackets: "[id]" -> "id"
31
+ name = name.replace(/\[([^\]]+)\]/g, '$1');
32
+
33
+ // Strip leading/trailing slashes just in case
34
+ name = name.replace(/^\/|\/$/g, '');
35
+
36
+ // 6. Split on kebab-case, snake_case, slash, and camelCase boundaries
37
+ // e.g., "product-details" -> "product details"
38
+ // e.g., "order_history" -> "order history"
39
+ // e.g., "UserProfile" -> "User Profile"
40
+ // e.g., "settings/profile" -> "settings profile"
41
+ name = name.replace(/[-_/]/g, ' ')
42
+ // Insert a space before all caps (but not at the start) to separate camelCase/PascalCase
43
+ .replace(/([a-z])([A-Z])/g, '$1 $2');
44
+
45
+ // 7. Title-case each word and clean extra spaces
46
+ name = name.split(/\s+/).filter(Boolean).map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
47
+ return name;
48
+ }
49
+ //# sourceMappingURL=humanizeScreenName.js.map
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import React from 'react';
11
11
  import type { AIConsentConfig } from './AIConsentDialog';
12
- import type { ExecutionResult, ToolDefinition, AgentStep, TokenUsage, KnowledgeBaseConfig, ChatBarTheme, AIProviderName, ScreenMap, ProactiveHelpConfig, InteractionMode, CustomerSuccessConfig, OnboardingConfig } from '../core/types';
12
+ import type { ExecutionResult, ToolDefinition, AgentStep, TokenUsage, KnowledgeBaseConfig, ChatBarTheme, AIProviderName, ScreenMap, ProactiveHelpConfig, InteractionMode, CustomerSuccessConfig, OnboardingConfig, VerifierConfig, SupportStyle } from '../core/types';
13
13
  interface AIAgentProps {
14
14
  /**
15
15
  * API key (for local prototyping only).
@@ -41,6 +41,10 @@ interface AIAgentProps {
41
41
  voiceProxyHeaders?: Record<string, string>;
42
42
  /** LLM model name (provider-specific) */
43
43
  model?: string;
44
+ /** Support personality preset. Default: 'warm-concise'. */
45
+ supportStyle?: SupportStyle;
46
+ /** Optional outcome verifier configuration for critical actions. */
47
+ verifier?: VerifierConfig;
44
48
  /** Navigation container ref (from useNavigationContainerRef) */
45
49
  navRef?: any;
46
50
  /** Max agent steps per request */
@@ -216,6 +220,6 @@ interface AIAgentProps {
216
220
  */
217
221
  consent?: AIConsentConfig;
218
222
  }
219
- export declare function AIAgent({ apiKey, proxyUrl, proxyHeaders, voiceProxyUrl, voiceProxyHeaders, provider: providerName, model, navRef, maxSteps, showChatBar, children, onResult, interactiveBlacklist, interactiveWhitelist, onBeforeStep, onAfterStep, onBeforeTask, onAfterTask, transformScreenContent, customTools, instructions, stepDelay, mcpServerUrl, router, pathname, enableVoice, onTokenUsage, debug, knowledgeBase, knowledgeMaxTokens, enableUIControl, accentColor, theme, screenMap, useScreenMap, maxTokenBudget, maxCostUSD, analyticsKey, analyticsProxyUrl, analyticsProxyHeaders, proactiveHelp, userContext, pushToken, pushTokenType, interactionMode, showDiscoveryTooltip: showDiscoveryTooltipProp, discoveryTooltipMessage, customerSuccess, onboarding, consent, }: AIAgentProps): import("react/jsx-runtime").JSX.Element;
223
+ export declare function AIAgent({ apiKey, proxyUrl, proxyHeaders, voiceProxyUrl, voiceProxyHeaders, provider: providerName, model, supportStyle, verifier, navRef, maxSteps, showChatBar, children, onResult, interactiveBlacklist, interactiveWhitelist, onBeforeStep, onAfterStep, onBeforeTask, onAfterTask, transformScreenContent, customTools, instructions, stepDelay, mcpServerUrl, router, pathname, enableVoice, onTokenUsage, debug, knowledgeBase, knowledgeMaxTokens, enableUIControl, accentColor, theme, screenMap, useScreenMap, maxTokenBudget, maxCostUSD, analyticsKey, analyticsProxyUrl, analyticsProxyHeaders, proactiveHelp, userContext, pushToken, pushTokenType, interactionMode, showDiscoveryTooltip: showDiscoveryTooltipProp, discoveryTooltipMessage, customerSuccess, onboarding, consent, }: AIAgentProps): import("react/jsx-runtime").JSX.Element;
220
224
  export {};
221
225
  //# sourceMappingURL=AIAgent.d.ts.map
@@ -7,6 +7,7 @@ import type { ExecutionResult, AgentMode, ChatBarTheme, AIMessage, ConversationS
7
7
  import type { SupportTicket } from '../support/types';
8
8
  interface AgentChatBarProps {
9
9
  onSend: (message: string) => void;
10
+ onCancel?: () => void;
10
11
  isThinking: boolean;
11
12
  statusText?: string;
12
13
  lastResult: ExecutionResult | null;
@@ -67,7 +68,20 @@ interface AgentChatBarProps {
67
68
  onNewConversation?: () => void;
68
69
  pendingApprovalQuestion?: string | null;
69
70
  onPendingApprovalAction?: (action: 'approve' | 'reject') => void;
71
+ renderMode?: 'default' | 'android-native-window';
72
+ onWindowMetricsChange?: (metrics: {
73
+ x: number;
74
+ y: number;
75
+ width: number;
76
+ height: number;
77
+ }) => void;
78
+ windowMetrics?: {
79
+ x: number;
80
+ y: number;
81
+ width: number;
82
+ height: number;
83
+ } | null;
70
84
  }
71
- export declare function AgentChatBar({ onSend, isThinking, statusText, lastResult, language, availableModes, mode, onModeChange, onMicToggle, onSpeakerToggle, isMicActive, isSpeakerMuted, isAISpeaking, isVoiceConnected, onStopSession, theme, tickets, selectedTicketId, onTicketSelect, autoExpandTrigger, unreadCounts, totalUnread, showDiscoveryTooltip, discoveryTooltipMessage, onTooltipDismiss, chatMessages, conversations, isLoadingHistory, onConversationSelect, onNewConversation, pendingApprovalQuestion, onPendingApprovalAction, }: AgentChatBarProps): import("react/jsx-runtime").JSX.Element;
85
+ export declare function AgentChatBar({ onSend, onCancel, isThinking, statusText, lastResult, language, availableModes, mode, onModeChange, onMicToggle, onSpeakerToggle, isMicActive, isSpeakerMuted, isAISpeaking, isVoiceConnected, onStopSession, theme, tickets, selectedTicketId, onTicketSelect, autoExpandTrigger, unreadCounts, totalUnread, showDiscoveryTooltip, discoveryTooltipMessage, onTooltipDismiss, chatMessages, conversations, isLoadingHistory, onConversationSelect, onNewConversation, pendingApprovalQuestion, onPendingApprovalAction, renderMode, onWindowMetricsChange, windowMetrics, }: AgentChatBarProps): import("react/jsx-runtime").JSX.Element;
72
86
  export {};
73
87
  //# sourceMappingURL=AgentChatBar.d.ts.map
@@ -9,13 +9,10 @@
9
9
  * Renders ABOVE all native Modals, system alerts, and navigation chrome.
10
10
  * 2. Falls back to plain View if react-native-screens is not installed.
11
11
  *
12
- * Android (both Old and New Architecture):
13
- * 1. Native `MobileAIFloatingOverlay` ViewManager (bundled in this library).
14
- * Creates a Dialog window with TYPE_APPLICATION_PANEL (z=1000),
15
- * above normal app Dialog windows (TYPE_APPLICATION, z=2).
16
- * No SYSTEM_ALERT_WINDOW permission needed — scoped to app's own window.
17
- * 2. Falls back to plain View if the app hasn't been rebuilt after install
18
- * (graceful degradation with DEV warning).
12
+ * Android:
13
+ * 1. Uses a native panel dialog window when explicit bounds are provided.
14
+ * This keeps the floating agent compact and above native modal surfaces.
15
+ * 2. Falls back to a plain View otherwise.
19
16
  *
20
17
  * Usage:
21
18
  * <FloatingOverlayWrapper fallbackStyle={styles.floatingLayer}>
@@ -28,24 +25,39 @@
28
25
  * Note: FullWindowOverlay on iOS does NOT officially accept style props in its TS definition,
29
26
  * but passing StyleSheet.absoluteFill is often necessary to prevent dimensions collapsing conditionally.
30
27
  */
28
+ import React from 'react';
31
29
  /**
32
30
  * True when a native elevated overlay is available on the current platform.
33
31
  * Used by AIConsentDialog to decide whether to render as View vs Modal.
34
32
  *
35
33
  * iOS + react-native-screens installed → true
36
- * Android + native rebuild done → true
37
34
  * Everything else (fallback) → false
38
35
  */
39
36
  export declare const isNativeOverlayActive: boolean;
40
37
  interface FloatingOverlayWrapperProps {
41
38
  children: React.ReactNode;
39
+ androidWindowMetrics?: {
40
+ x: number;
41
+ y: number;
42
+ width: number;
43
+ height: number;
44
+ } | null;
45
+ onAndroidWindowDragEnd?: (metrics: {
46
+ x: number;
47
+ y: number;
48
+ width: number;
49
+ height: number;
50
+ }) => void;
42
51
  /**
43
52
  * Style applied to the View wrapper when no native overlay is available.
44
53
  * Ignored on iOS (FullWindowOverlay creates its own UIWindow) and
45
- * Android (native module creates its own Dialog window).
54
+ * Android (native module creates its own panel dialog window).
46
55
  */
47
56
  fallbackStyle?: any;
48
57
  }
49
- export declare function FloatingOverlayWrapper({ children, fallbackStyle, }: FloatingOverlayWrapperProps): React.ReactElement;
58
+ export interface FloatingOverlayWrapperHandle {
59
+ setAndroidWindowMetrics: (metrics: NonNullable<FloatingOverlayWrapperProps['androidWindowMetrics']>) => void;
60
+ }
61
+ export declare const FloatingOverlayWrapper: React.ForwardRefExoticComponent<FloatingOverlayWrapperProps & React.RefAttributes<FloatingOverlayWrapperHandle>>;
50
62
  export {};
51
63
  //# sourceMappingURL=FloatingOverlayWrapper.d.ts.map
@@ -8,6 +8,10 @@
8
8
  * to route telemetry through your own backend without touching this file.
9
9
  */
10
10
  export declare const ENDPOINTS: {
11
+ /** Hosted MobileAI text proxy — used by default when analyticsKey is set */
12
+ readonly hostedTextProxy: `${string}/api/v1/hosted-proxy/text`;
13
+ /** Hosted MobileAI voice proxy — used by default when analyticsKey is set */
14
+ readonly hostedVoiceProxy: `${string}/ws/hosted-proxy/voice`;
11
15
  /** Telemetry event ingest — receives batched SDK events */
12
16
  readonly telemetryIngest: `${string}/api/v1/events`;
13
17
  /** Feature flag sync — fetches remote flags for this analyticsKey */
@@ -23,6 +23,10 @@ export declare class AgentRuntime {
23
23
  private uiControlOverride?;
24
24
  private lastDehydratedRoot;
25
25
  private currentTraceId;
26
+ private currentUserGoal;
27
+ private verifierProvider;
28
+ private outcomeVerifier;
29
+ private pendingCriticalVerification;
26
30
  private originalErrorHandler;
27
31
  private lastSuppressedError;
28
32
  private graceTimer;
@@ -38,6 +42,11 @@ export declare class AgentRuntime {
38
42
  private formatInteractiveForDebug;
39
43
  private debugScreenSnapshot;
40
44
  constructor(provider: AIProvider, config: AgentConfig, rootRef: any, navRef: any);
45
+ private getVerifier;
46
+ private createCurrentVerificationSnapshot;
47
+ private updateCriticalVerification;
48
+ private maybeStartCriticalVerification;
49
+ private shouldBlockSuccessCompletion;
41
50
  private registerBuiltInTools;
42
51
  /**
43
52
  * Register only knowledge-assistant tools (no UI control).
@@ -73,9 +82,9 @@ export declare class AgentRuntime {
73
82
  /** Maps a tool call to a user-friendly status label for the loading overlay. */
74
83
  private getToolStatusLabel;
75
84
  /**
76
- * Captures the current screen as a base64 JPEG for Gemini vision.
77
- * Uses react-native-view-shot as an optional peer dependency.
78
- * Returns null if the library is not installed (graceful fallback).
85
+ * Captures the root component as a base64 JPEG for vision tools.
86
+ * Uses react-native-view-shot as a required peer dependency.
87
+ * Returns null only when capture is temporarily unavailable.
79
88
  */
80
89
  private captureScreenshot;
81
90
  /**
@@ -6,7 +6,7 @@
6
6
  * interactive elements by their type and props (onPress, onChangeText, etc.).
7
7
  *
8
8
  */
9
- import type { InteractiveElement } from './types';
9
+ import type { InteractiveElement, WireframeSnapshot } from './types';
10
10
  export interface WalkConfig {
11
11
  /** React refs of elements to exclude */
12
12
  interactiveBlacklist?: React.RefObject<any>[];
@@ -64,4 +64,15 @@ export interface ScrollableContainer {
64
64
  * For ScrollView: the stateNode IS the native scroll view directly.
65
65
  */
66
66
  export declare function findScrollableContainers(rootRef: any, screenName?: string): ScrollableContainer[];
67
+ /**
68
+ * Capture a privacy-safe wireframe of the current screen.
69
+ *
70
+ * Performance guarantees:
71
+ * - Capped at WIREFRAME_MAX_ELEMENTS (50) — enough for wireframe context
72
+ * - Measures in batches of WIREFRAME_BATCH_SIZE (10), yielding a frame
73
+ * between batches so the bridge stays free for user interactions
74
+ * - The caller (AIAgent) defers this via InteractionManager so it
75
+ * never competes with screen transitions or gestures
76
+ */
77
+ export declare function captureWireframe(rootRef: React.RefObject<any>, config?: WalkConfig): Promise<WireframeSnapshot | null>;
67
78
  //# sourceMappingURL=FiberTreeWalker.d.ts.map