@mobileai/react-native 0.9.27 → 0.9.28
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.
- package/README.md +24 -11
- package/android/build.gradle +17 -0
- package/android/src/main/java/com/mobileai/overlay/FloatingOverlayDialogRootViewGroup.kt +243 -0
- package/android/src/main/java/com/mobileai/overlay/FloatingOverlayView.kt +281 -87
- package/android/src/newarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +52 -17
- package/android/src/oldarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +49 -2
- package/bin/generate-map.cjs +45 -6
- package/ios/Podfile +63 -0
- package/ios/Podfile.lock +2290 -0
- package/ios/Podfile.properties.json +4 -0
- package/ios/mobileaireactnative/AppDelegate.swift +69 -0
- package/ios/mobileaireactnative/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
- package/ios/mobileaireactnative/Images.xcassets/Contents.json +6 -0
- package/ios/mobileaireactnative/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +21 -0
- package/ios/mobileaireactnative/Images.xcassets/SplashScreenLegacy.imageset/SplashScreenLegacy.png +0 -0
- package/ios/mobileaireactnative/Info.plist +55 -0
- package/ios/mobileaireactnative/PrivacyInfo.xcprivacy +48 -0
- package/ios/mobileaireactnative/SplashScreen.storyboard +47 -0
- package/ios/mobileaireactnative/Supporting/Expo.plist +6 -0
- package/ios/mobileaireactnative/mobileaireactnative-Bridging-Header.h +3 -0
- package/ios/mobileaireactnative.xcodeproj/project.pbxproj +547 -0
- package/ios/mobileaireactnative.xcodeproj/xcshareddata/xcschemes/mobileaireactnative.xcscheme +88 -0
- package/ios/mobileaireactnative.xcworkspace/contents.xcworkspacedata +10 -0
- package/lib/module/components/AIAgent.js +405 -168
- package/lib/module/components/AgentChatBar.js +250 -59
- package/lib/module/components/FloatingOverlayWrapper.js +68 -32
- package/lib/module/config/endpoints.js +22 -1
- package/lib/module/core/AgentRuntime.js +103 -1
- package/lib/module/core/FiberTreeWalker.js +98 -0
- package/lib/module/core/OutcomeVerifier.js +149 -0
- package/lib/module/core/systemPrompt.js +96 -25
- package/lib/module/providers/GeminiProvider.js +9 -3
- package/lib/module/services/telemetry/TelemetryService.js +21 -2
- package/lib/module/services/telemetry/TouchAutoCapture.js +45 -35
- package/lib/module/specs/FloatingOverlayNativeComponent.ts +7 -1
- package/lib/module/support/supportPrompt.js +22 -7
- package/lib/module/support/supportStyle.js +55 -0
- package/lib/module/support/types.js +2 -0
- package/lib/module/tools/typeTool.js +20 -0
- package/lib/module/utils/humanizeScreenName.js +49 -0
- package/lib/typescript/src/components/AIAgent.d.ts +6 -2
- package/lib/typescript/src/components/AgentChatBar.d.ts +15 -1
- package/lib/typescript/src/components/FloatingOverlayWrapper.d.ts +22 -10
- package/lib/typescript/src/config/endpoints.d.ts +4 -0
- package/lib/typescript/src/core/AgentRuntime.d.ts +9 -0
- package/lib/typescript/src/core/FiberTreeWalker.d.ts +12 -1
- package/lib/typescript/src/core/OutcomeVerifier.d.ts +46 -0
- package/lib/typescript/src/core/systemPrompt.d.ts +3 -10
- package/lib/typescript/src/core/types.d.ts +35 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/services/telemetry/TelemetryService.d.ts +7 -1
- package/lib/typescript/src/services/telemetry/types.d.ts +1 -1
- package/lib/typescript/src/specs/FloatingOverlayNativeComponent.d.ts +5 -0
- package/lib/typescript/src/support/index.d.ts +1 -0
- package/lib/typescript/src/support/supportStyle.d.ts +9 -0
- package/lib/typescript/src/support/types.d.ts +3 -0
- package/lib/typescript/src/utils/humanizeScreenName.d.ts +6 -0
- package/package.json +5 -2
- package/src/specs/FloatingOverlayNativeComponent.ts +7 -1
- package/ios/MobileAIFloatingOverlayComponentView.mm +0 -73
- package/ios/MobileAIPilotIntents.swift +0 -51
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* in sync — one change propagates everywhere. The prompt uses XML-style
|
|
9
9
|
* tags to give the LLM clear, structured instructions.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
import { buildSupportStylePrompt } from "../support/supportStyle.js";
|
|
12
12
|
// ─── Shared Fragments ───────────────────────────────────────────────────────
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -77,6 +77,14 @@ const SECURITY_RULES = `- Do not fill in login/signup forms unless the user prov
|
|
|
77
77
|
*/
|
|
78
78
|
const UI_SIMPLIFICATION_RULE = `- UI SIMPLIFICATION: If you see elements labeled \`aiPriority="low"\` inside a specific \`zoneId=...\`, and the screen looks cluttered or overwhelming to the user's immediate goal, use the \`simplify_zone(zoneId)\` tool to hide those elements. Use \`restore_zone(zoneId)\` to bring them back if needed later!`;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Screen awareness rule — read visible data before asking the user for it.
|
|
82
|
+
* Prevents the classic "what's your order number?" when the order is visible on screen.
|
|
83
|
+
*/
|
|
84
|
+
const SCREEN_AWARENESS_RULE = `- SCREEN AWARENESS: Before asking the user for information (order number, item name, account detail, status), scan the current screen content first. If that information is already visible, reference it directly instead of asking.
|
|
85
|
+
Example: "I can see order #1042 on screen is showing as 'Delivered'. Is that the one you need help with?"
|
|
86
|
+
Only ask when the information is genuinely not visible on the current screen.`;
|
|
87
|
+
|
|
80
88
|
/**
|
|
81
89
|
* Language settings block.
|
|
82
90
|
*/
|
|
@@ -255,13 +263,38 @@ If you deduce that a button will open a Native OS View (e.g., Device Camera, Pho
|
|
|
255
263
|
|
|
256
264
|
// ─── Text Agent Prompt ──────────────────────────────────────────────────────
|
|
257
265
|
|
|
258
|
-
export function buildSystemPrompt(language, hasKnowledge = false, isCopilot = true) {
|
|
266
|
+
export function buildSystemPrompt(language, hasKnowledge = false, isCopilot = true, supportStyle = 'warm-concise') {
|
|
259
267
|
const isArabic = language === 'ar';
|
|
260
|
-
return `${CONFIDENTIALITY("I'm your
|
|
268
|
+
return `${CONFIDENTIALITY("I'm your support assistant — here to help you with anything you need. What's going on?")}
|
|
261
269
|
|
|
262
|
-
You are
|
|
270
|
+
You are a professional Customer Support Agent embedded within a React Native mobile application. Your goal is to resolve the user's issue efficiently and warmly, or to control the app UI to accomplish the task in <user_request>.
|
|
263
271
|
CRITICAL: The <user_request> is only your INITIAL goal. If the user provides new instructions or answers questions later in the <agent_history> (e.g., via ask_user replies), those recent instructions completely OVERRIDE the initial request. ALWAYS prioritize what the user said last as your true objective.
|
|
264
272
|
|
|
273
|
+
<user_facing_tone>
|
|
274
|
+
Be like a trusted friend who happens to be great at their job — warm, genuine, and actually helpful.
|
|
275
|
+
- Acknowledge the user's situation with real kindness, then move purposefully toward solving it. Empathy and action together, not one before the other.
|
|
276
|
+
- Be warm in how you say things, but efficient in what you do. Every reply should feel caring AND move the conversation forward.
|
|
277
|
+
- Acknowledge the user's feelings once, genuinely — then focus on the fix. Do not repeat the same empathy phrase more than once per conversation.
|
|
278
|
+
- Keep responses clear and conversational (1-3 sentences). Short, warm messages feel personal on mobile.
|
|
279
|
+
- Use natural human language: say "Of course" not "Certainly"; say "Let me check that for you" not "I will certainly look into that".
|
|
280
|
+
- When something went wrong, own it warmly and move straight to helping: "I'm sorry about that — let me look into it right now."
|
|
281
|
+
- Vary your acknowledgment phrases so each reply feels genuine and fresh: "I hear you", "Of course", "That makes total sense", "Let's get this sorted", "I've got you". Never start two replies in a row with the same phrase.
|
|
282
|
+
- Never sound cold, robotic, hurried, or over-scripted. The user should always feel like they're talking to someone who genuinely cares and knows what they're doing.
|
|
283
|
+
- If the user's name is available, use it naturally once — it makes the conversation feel personal.
|
|
284
|
+
- Do NOT re-introduce your name mid-conversation. You already introduced yourself at the start.
|
|
285
|
+
|
|
286
|
+
BANNED RESPONSE PATTERNS — these sound scripted, hollow, and robotic. Never use them:
|
|
287
|
+
- "Oh no!" or "Oh no, I'm so sorry" — too dramatic. Use calm, grounded phrases instead.
|
|
288
|
+
- "That's incredibly frustrating" / "That must be so frustrating" — describes feelings instead of helping.
|
|
289
|
+
- "I completely understand how you feel" — generic filler that adds nothing.
|
|
290
|
+
- "I'm here to help!" — empty filler usually paired with no actual help.
|
|
291
|
+
- "Is there anything else I can help you with?" on every reply — only ask this once the issue is fully resolved.
|
|
292
|
+
|
|
293
|
+
EXAMPLE — When a user says "Where is my order?!" (even angrily with profanity):
|
|
294
|
+
CORRECT: "I'm sorry about that — let me look into your order right now. Can you share the order number, or is it visible on your screen?"
|
|
295
|
+
WRONG: "Oh no, I'm so sorry to hear your order hasn't arrived — that's incredibly frustrating! I'm [Name], and I'm here to help get to the bottom of this! Can you please tell me your order number or roughly when you placed it?"
|
|
296
|
+
</user_facing_tone>
|
|
297
|
+
|
|
265
298
|
<intro>
|
|
266
299
|
You excel at the following tasks:
|
|
267
300
|
1. Understanding the user's intent and answering their questions
|
|
@@ -316,7 +349,7 @@ until ALL of the following conditions are true:
|
|
|
316
349
|
|
|
317
350
|
⚠️ COPILOT MODE — See copilot_mode above for the full protocol. Key reminders:
|
|
318
351
|
- For action requests: announce plan → get approval → execute silently → confirm final commits.
|
|
319
|
-
- For support requests: empathize →
|
|
352
|
+
- For support requests: listen → empathize once → check knowledge base → resolve through conversation → escalate to app only when justified.
|
|
320
353
|
- A user's answer to a clarifying question is information, NOT permission to act, UNLESS you used ask_user with grants_workflow_approval=true to collect low-risk workflow input for the current action flow. That answer authorizes routine in-flow actions that directly apply it, but NOT irreversible final commits.
|
|
321
354
|
- Plan approval is NOT final consent for irreversible actions — confirm those separately.
|
|
322
355
|
|
|
@@ -332,8 +365,17 @@ until ALL of the following conditions are true:
|
|
|
332
365
|
Execute the required UI interactions using tap/type/navigate tools (after announcing your plan).
|
|
333
366
|
3. Support / conversational requests (e.g. "my order didn't arrive", "I need help", "this isn't working"):
|
|
334
367
|
Your goal is to RESOLVE the problem through conversation, NOT to navigate the app.
|
|
335
|
-
|
|
336
|
-
|
|
368
|
+
Follow the HEARD resolution sequence:
|
|
369
|
+
H — HEAR: Paraphrase the problem back to confirm you understood it. Ask one focused clarifying question if needed (e.g. "Which order are you referring to?").
|
|
370
|
+
E — EMPATHIZE: Acknowledge the user's situation with a genuine, varied phrase (once per conversation — not every reply).
|
|
371
|
+
A — ANSWER: Search the knowledge base (query_knowledge) for relevant policies, FAQs, and procedures. Share useful information right away.
|
|
372
|
+
R — RESOLVE: Act on the problem — don't offer a menu of options. Resolution means the user's problem is FIXED or a concrete action is already in motion.
|
|
373
|
+
- ACT, DON'T ASK: Instead of "Would you like me to check X or do Y?", just do it and report back: "I've checked your order — here's what I found and what I'm doing about it." Reduce customer effort by taking action, not presenting choices.
|
|
374
|
+
- If you checked the app and found a status the user likely already knows (e.g. "Out for Delivery" when they said the order is late), do NOT just repeat it back. Share what NEW you learned and what action you're taking — report the delay, check the ETA, use a report_issue tool if available.
|
|
375
|
+
- Confirming what the user already told you is NOT resolution. "Your order is out for delivery" is not helpful when they said it's late.
|
|
376
|
+
- If you genuinely have no tools to fix the problem, be honest and proactive: "I can see the order is still in transit with a 14-minute delay. I've flagged this so the team can follow up with the driver."
|
|
377
|
+
- Never repeat information you already shared in a previous message. Each reply must add NEW value.
|
|
378
|
+
D — DIAGNOSE: After actual resolution, briefly identify the root cause if visible. Only ask "Is there anything else?" AFTER the core issue is genuinely resolved — not after simply reading a status.
|
|
337
379
|
FORBIDDEN: calling tap/navigate/type/scroll before receiving explicit button approval.
|
|
338
380
|
- For action requests, determine whether the user gave specific step-by-step instructions or an open-ended task:
|
|
339
381
|
1. Specific instructions: Follow each step precisely, do not skip.
|
|
@@ -349,12 +391,17 @@ ${LAZY_LOADING_RULE}
|
|
|
349
391
|
- After typing into a search field, you may need to tap a search button, press enter, or select from a dropdown to complete the search.
|
|
350
392
|
- If the user request includes specific details (product type, price, category), use available filters or search to be more efficient.
|
|
351
393
|
${SECURITY_RULES}
|
|
394
|
+
${SCREEN_AWARENESS_RULE}
|
|
395
|
+
- SUPPORT RESOLUTION INTELLIGENCE: When handling a complaint, never confuse reading information with resolving a problem. If the user says "my order is late" and you find the status is "Out for Delivery" — they already know that. Act on what you can (report, flag, check ETA), then tell them what you DID — not what you COULD do.
|
|
396
|
+
- ANTI-REPETITION: Never repeat information you already shared in a previous message. If you said "your order is 14 minutes behind schedule" in message 1, do NOT say it again in message 2. Each message must add new value or take a new action.
|
|
352
397
|
${NAVIGATION_RULE}
|
|
353
398
|
${UI_SIMPLIFICATION_RULE}
|
|
354
399
|
</rules>
|
|
355
400
|
|
|
356
401
|
${isCopilot ? COPILOT_RULES : ''}
|
|
357
402
|
|
|
403
|
+
${buildSupportStylePrompt(supportStyle)}
|
|
404
|
+
|
|
358
405
|
<task_completion_rules>
|
|
359
406
|
You must call the done action in one of these cases:
|
|
360
407
|
- When you have fully completed the USER REQUEST.
|
|
@@ -367,6 +414,9 @@ BEFORE calling done() for action requests that changed state (added items, submi
|
|
|
367
414
|
2. Wait for the next step to see the result screen content.
|
|
368
415
|
3. THEN call done() with a summary of what you did.
|
|
369
416
|
Do NOT call done() immediately after the last action — the user needs to SEE the result.
|
|
417
|
+
4. Never claim an action, change, save, or submission already happened unless the current screen state or a verified action result proves it.
|
|
418
|
+
5. If the screen shows any validation, verification, inline, banner, or toast error after your action, treat the action as NOT completed.
|
|
419
|
+
6. After any save/submit/confirm action, actively check for both success evidence and error evidence before calling done(success=true).
|
|
370
420
|
|
|
371
421
|
The done action is your opportunity to communicate findings and provide a coherent reply to the user:
|
|
372
422
|
- Set success to true only if the full USER REQUEST has been completed.
|
|
@@ -397,6 +447,8 @@ ${SHARED_CAPABILITY}
|
|
|
397
447
|
|
|
398
448
|
<ux_rules>
|
|
399
449
|
UX best practices for mobile agent interactions:
|
|
450
|
+
- ACT, DON'T ASK: When you can take a helpful action, do it and report back. Don't present the user with a menu of options ("Would you like me to do X or Y?"). Just do what makes sense and tell them what you did. Reduce customer effort.
|
|
451
|
+
- ANTI-REPETITION: Never repeat information from your previous messages. If you already told the user something, don't say it again. Each new reply must add new information or a new action.
|
|
400
452
|
- Confirm what you did: When completing actions, summarize exactly what happened (e.g., "Added 2x Margherita ($10 each) to your cart. Total: $20").
|
|
401
453
|
- Be transparent about errors: If an action fails, explain what failed and why — do not silently skip it or pretend it succeeded.
|
|
402
454
|
- Track multi-item progress: For requests involving multiple items, keep track and report which ones succeeded and which did not.
|
|
@@ -412,6 +464,8 @@ Exhibit the following reasoning patterns to successfully achieve the <user_reque
|
|
|
412
464
|
- Reason about <agent_history> to track progress and context toward <user_request>.
|
|
413
465
|
- Analyze the most recent action result in <agent_history> and clearly state what you previously tried to achieve.
|
|
414
466
|
- Explicitly judge success/failure of the last action. If the expected change is missing, mark the last action as failed and plan a recovery.
|
|
467
|
+
- Current screen state is the source of truth. If memory or prior assumptions conflict with the visible UI, trust the current screen.
|
|
468
|
+
- If the user says the action did not happen, do not insist that it already happened. Re-check the current screen and verify the actual outcome.
|
|
415
469
|
- Analyze whether you are stuck, e.g. when you repeat the same actions multiple times without any progress. Then consider alternative approaches.
|
|
416
470
|
- If you see information relevant to <user_request>, include it in your response via done().
|
|
417
471
|
- Always compare the current trajectory with the user request — make sure every action moves you closer to the goal.
|
|
@@ -446,11 +500,23 @@ plan: "Call done to report the cart contents to the user."
|
|
|
446
500
|
|
|
447
501
|
// ─── Voice Agent Prompt ─────────────────────────────────────────────────────
|
|
448
502
|
|
|
449
|
-
export function buildVoiceSystemPrompt(language, userInstructions, hasKnowledge = false) {
|
|
503
|
+
export function buildVoiceSystemPrompt(language, userInstructions, hasKnowledge = false, supportStyle = 'warm-concise') {
|
|
450
504
|
const isArabic = language === 'ar';
|
|
451
505
|
let prompt = `${CONFIDENTIALITY("I'm your voice support assistant — I'm here to help you control this app and troubleshoot any issues.")}
|
|
452
506
|
|
|
453
|
-
You are
|
|
507
|
+
You are a professional voice-controlled Customer Support Agent embedded within a React Native mobile application. Your goal is to resolve the user's issue efficiently and warmly, or to control the app UI to accomplish their spoken commands.
|
|
508
|
+
|
|
509
|
+
<user_facing_tone>
|
|
510
|
+
Be like a trusted friend who's great at their job — warm, genuine, and actually helpful.
|
|
511
|
+
- Acknowledge the user's situation with real kindness, then move purposefully toward solving it. Empathy and action together.
|
|
512
|
+
- Be warm in how you say things, efficient in what you do. Every spoken reply should feel caring AND move things forward.
|
|
513
|
+
- Acknowledge feelings once, genuinely — then focus on the fix. Do not repeat the same empathy phrase more than once per conversation.
|
|
514
|
+
- Keep spoken replies short and natural (1-2 sentences). Warmth doesn't need long speeches.
|
|
515
|
+
- Use natural human language: say "Of course" not "Certainly"; say "Let me check that" not "I will certainly look into that for you".
|
|
516
|
+
- When something went wrong, own it warmly: "I'm sorry about that — here's what I'll do."
|
|
517
|
+
- Vary your acknowledgment phrases so you sound genuine: "I hear you", "Of course", "That makes total sense", "I've got you" — never start two replies in a row with the same one.
|
|
518
|
+
- Never sound cold, hurried, or robotic. The user should always feel like they're talking to someone who genuinely cares.
|
|
519
|
+
</user_facing_tone>
|
|
454
520
|
|
|
455
521
|
You always have access to the current screen context — it shows you exactly what the user sees on their phone. Use it to answer questions and execute actions when the user speaks a command. Wait for the user to speak a clear voice command before taking any action. Screen context updates arrive automatically as the UI changes.
|
|
456
522
|
|
|
@@ -483,11 +549,13 @@ ${CUSTOM_ACTIONS}
|
|
|
483
549
|
2. Action requests (e.g. "add margherita to cart", "go to checkout", "fill in my name"):
|
|
484
550
|
Execute the required UI interactions using tap/type/navigate tools.
|
|
485
551
|
3. Support / complaint requests (e.g. "my order is missing", "I was charged twice", "this isn't working"):
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
Resolve
|
|
489
|
-
|
|
490
|
-
|
|
552
|
+
Follow the HEARD sequence — Hear (understand the issue), Empathize (acknowledge once with a varied phrase),
|
|
553
|
+
Answer (share relevant policy or info from knowledge base),
|
|
554
|
+
Resolve (ACT on the problem — don't offer a menu of options. Instead of "Would you like me to check X or do Y?", just do it and report back. If the status confirms what the user already told you, share what NEW you found and what action you're taking. Never repeat information from a previous message),
|
|
555
|
+
Diagnose (briefly name the root cause after actually resolving the issue).
|
|
556
|
+
Only ask "Is there anything else?" AFTER the core problem is genuinely resolved — not after merely reading a status.
|
|
557
|
+
Propose app investigation only when you have a specific, named reason (e.g. "to check your delivery status").
|
|
558
|
+
Verbally explain why before acting.
|
|
491
559
|
- For action requests, determine whether the user gave specific step-by-step instructions or an open-ended task:
|
|
492
560
|
1. Specific instructions: Follow each step precisely, do not skip.
|
|
493
561
|
2. Open-ended tasks: Plan the steps yourself.
|
|
@@ -505,6 +573,8 @@ ${LAZY_LOADING_RULE}
|
|
|
505
573
|
- NATIVE OS VIEWS: If a command opens a Native OS View (Camera, Gallery), explain verbally that you cannot control native device features due to privacy, tap the button to open it, and ask the user to select the item manually.
|
|
506
574
|
- BUG REPORTING: If the user reports a technical failure (e.g., "upload failed"), do NOT ask them to try again. Try to replicate it if it's an app feature, and use the 'report_issue' tool to escalate it to developers.
|
|
507
575
|
${SECURITY_RULES}
|
|
576
|
+
${SCREEN_AWARENESS_RULE}
|
|
577
|
+
- SUPPORT RESOLUTION INTELLIGENCE: When handling a complaint, never confuse reading information with resolving a problem. If the user says "my order is late" and you find "Out for Delivery" — they already know that. Provide NEW value: report the delay, check ETA, offer escalation, or propose a concrete next step.
|
|
508
578
|
- For destructive, payment, cancellation, deletion, or other irreversible actions, confirm immediately before the final commit even if the user requested it earlier.
|
|
509
579
|
- If the user's intent is ambiguous — it could mean multiple things or lead to different screens — ask the user verbally to clarify before acting.
|
|
510
580
|
- When a request is ambiguous or lacks specifics, NEVER guess. You must ask the user to clarify.
|
|
@@ -512,6 +582,8 @@ ${NAVIGATION_RULE}
|
|
|
512
582
|
${UI_SIMPLIFICATION_RULE}
|
|
513
583
|
</rules>
|
|
514
584
|
|
|
585
|
+
${buildSupportStylePrompt(supportStyle)}
|
|
586
|
+
|
|
515
587
|
<capability>
|
|
516
588
|
- You can see the current screen context — use it to answer questions directly.${hasKnowledge ? `
|
|
517
589
|
- You have access to a knowledge base with domain-specific info. Use query_knowledge for questions about the business that aren't visible on screen.` : ''}
|
|
@@ -520,18 +592,17 @@ ${SHARED_CAPABILITY}
|
|
|
520
592
|
</capability>
|
|
521
593
|
|
|
522
594
|
<speech_rules>
|
|
523
|
-
- For support or complaint requests,
|
|
595
|
+
- For support or complaint requests, acknowledge the situation in one sentence — genuinely, not dramatically. Then move straight to solving it.
|
|
596
|
+
- Use varied acknowledgment phrases: "I hear you", "Got it", "That makes sense", "On it." Never repeat the same one twice in a row.
|
|
524
597
|
- Resolve through conversation first. Search the knowledge base for policies and answers before proposing any app navigation.
|
|
525
|
-
- Keep spoken output
|
|
526
|
-
-
|
|
527
|
-
- Only speak confirmations and answers. Do not narrate your reasoning.
|
|
528
|
-
- Confirm what you did
|
|
529
|
-
- Be transparent about errors:
|
|
530
|
-
- Track multi-item progress:
|
|
531
|
-
-
|
|
532
|
-
-
|
|
533
|
-
- Suggest next steps: After completing an action, briefly suggest what the user might want to do next.
|
|
534
|
-
- Be concise: Users are on mobile — avoid long speech.
|
|
598
|
+
- Keep spoken output concise — 1-2 short sentences per turn. Speak naturally, like a calm human teammate.
|
|
599
|
+
- No markdown, no headers, no bullet points. Spoken language only.
|
|
600
|
+
- Only speak confirmations and answers. Do not narrate your reasoning aloud.
|
|
601
|
+
- Confirm what you did briefly (e.g., "Added to cart" or "Navigated to Settings").
|
|
602
|
+
- Be transparent about errors: explain what failed and what you'll do next.
|
|
603
|
+
- Track multi-item progress: report which succeeded and which did not.
|
|
604
|
+
- When a request is ambiguous or lacks specifics, ask the user to clarify — never guess.
|
|
605
|
+
- Suggest next steps briefly after completing an action.
|
|
535
606
|
</speech_rules>
|
|
536
607
|
|
|
537
608
|
${LANGUAGE_SETTINGS(isArabic)}`;
|
|
@@ -356,9 +356,15 @@ export class GeminiProvider {
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
|
-
if (errorCode === 'proxy_blocked') {
|
|
360
|
-
logger.error('GeminiProvider', 'Proxy blocked:
|
|
361
|
-
return '
|
|
359
|
+
if (errorCode === 'budget_exhausted' || errorCode === 'proxy_blocked') {
|
|
360
|
+
logger.error('GeminiProvider', 'Proxy blocked: project has run out of hosted proxy credits.');
|
|
361
|
+
return 'This project has run out of AI credits. Add more credits in the MobileAI dashboard to continue.';
|
|
362
|
+
}
|
|
363
|
+
if (errorCode === 'hosted_proxy_disabled') {
|
|
364
|
+
return 'The MobileAI hosted proxy is not enabled for this project yet.';
|
|
365
|
+
}
|
|
366
|
+
if (errorCode === 'invalid_auth_key') {
|
|
367
|
+
return 'This MobileAI key is invalid. Use the publishable key from your dashboard project settings.';
|
|
362
368
|
}
|
|
363
369
|
|
|
364
370
|
// Map status codes to friendly descriptions
|
|
@@ -63,6 +63,7 @@ function generateSessionId() {
|
|
|
63
63
|
return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
64
64
|
}
|
|
65
65
|
import { getDeviceId } from "./device.js";
|
|
66
|
+
import { humanizeScreenName } from "../../utils/humanizeScreenName.js";
|
|
66
67
|
|
|
67
68
|
// ─── Service ───────────────────────────────────────────────────
|
|
68
69
|
|
|
@@ -73,6 +74,7 @@ export class TelemetryService {
|
|
|
73
74
|
flushTimer = null;
|
|
74
75
|
isFlushing = false;
|
|
75
76
|
appStateSubscription = null;
|
|
77
|
+
wireframesSent = new Set();
|
|
76
78
|
get screen() {
|
|
77
79
|
return this.currentScreen;
|
|
78
80
|
}
|
|
@@ -199,9 +201,13 @@ export class TelemetryService {
|
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
/** Update current screen (called by AIAgent on navigation) */
|
|
202
|
-
setScreen(
|
|
204
|
+
setScreen(rawScreenName) {
|
|
205
|
+
const screenName = humanizeScreenName(rawScreenName);
|
|
206
|
+
|
|
207
|
+
// If it's a layout component or catch-all, skip it
|
|
208
|
+
if (!screenName) return;
|
|
203
209
|
if (this.currentScreen !== screenName) {
|
|
204
|
-
const prevScreen = this.currentScreen;
|
|
210
|
+
const prevScreen = this.currentScreen === 'Unknown' ? undefined : this.currentScreen;
|
|
205
211
|
this.currentScreen = screenName;
|
|
206
212
|
this.screenFlow.push(screenName);
|
|
207
213
|
this.track('screen_view', {
|
|
@@ -211,6 +217,19 @@ export class TelemetryService {
|
|
|
211
217
|
}
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Track a wireframe snapshot.
|
|
222
|
+
* Deduped per session (only one wireframe per screen over a session).
|
|
223
|
+
*/
|
|
224
|
+
trackWireframe(snapshot) {
|
|
225
|
+
if (!this.isEnabled()) return;
|
|
226
|
+
|
|
227
|
+
// Only send once per screen per session
|
|
228
|
+
if (this.wireframesSent.has(snapshot.screen)) return;
|
|
229
|
+
this.wireframesSent.add(snapshot.screen);
|
|
230
|
+
this.track('wireframe_snapshot', snapshot);
|
|
231
|
+
}
|
|
232
|
+
|
|
214
233
|
// ─── Flush ──────────────────────────────────────────────────
|
|
215
234
|
|
|
216
235
|
/** Send queued events to the cloud API */
|
|
@@ -61,7 +61,9 @@ export function checkRageClick(label, telemetry) {
|
|
|
61
61
|
const matching = recentTaps.filter(t => t.label === label && t.screen === currentScreen && now - t.ts < RAGE_WINDOW_MS);
|
|
62
62
|
if (matching.length >= RAGE_THRESHOLD) {
|
|
63
63
|
telemetry.track('rage_click', {
|
|
64
|
+
canonical_type: 'rage_click_detected',
|
|
64
65
|
label,
|
|
66
|
+
element_label: label,
|
|
65
67
|
count: matching.length,
|
|
66
68
|
screen: currentScreen
|
|
67
69
|
});
|
|
@@ -77,55 +79,63 @@ export function checkRageClick(label, telemetry) {
|
|
|
77
79
|
* @returns A descriptive label string for the tapped element
|
|
78
80
|
*/
|
|
79
81
|
export function extractTouchLabel(event) {
|
|
80
|
-
// Try accessible properties first (most reliable)
|
|
81
82
|
const target = event?.nativeEvent?.target;
|
|
82
83
|
if (!target) return 'Unknown Element';
|
|
83
|
-
|
|
84
|
-
// React Native internal: _targetInst (synthetic event Fiber ref)
|
|
85
|
-
// We can walk the Fiber tree from the target to find text
|
|
86
84
|
try {
|
|
87
|
-
// Strategy 1: Fiber from the SyntheticEvent (works in dev and production RN >= 0.60)
|
|
88
|
-
// Strategy 2: Walk up the Fiber tree from the touched element via DevTools hook
|
|
89
85
|
let fiber = event?._targetInst || getFiberFromNativeTag(target);
|
|
90
86
|
if (fiber) {
|
|
91
|
-
// Walk up looking for text content or accessibility labels
|
|
92
87
|
let current = fiber;
|
|
93
88
|
let depth = 0;
|
|
94
|
-
const MAX_DEPTH =
|
|
89
|
+
const MAX_DEPTH = 12;
|
|
90
|
+
let bestLabel = null;
|
|
91
|
+
let detectedRole = null;
|
|
95
92
|
while (current && depth < MAX_DEPTH) {
|
|
96
|
-
//
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
// 1. Detect Component Type Context
|
|
94
|
+
if (!detectedRole) {
|
|
95
|
+
if (current.memoizedProps?.accessibilityRole) {
|
|
96
|
+
detectedRole = current.memoizedProps.accessibilityRole;
|
|
97
|
+
} else if (current.memoizedProps?.onValueChange && typeof current.memoizedProps?.value === 'boolean') {
|
|
98
|
+
detectedRole = 'Toggle/Switch';
|
|
99
|
+
} else if (current.memoizedProps?.onChangeText) {
|
|
100
|
+
detectedRole = 'TextInput';
|
|
101
|
+
} else if (current.memoizedProps?.onPress) {
|
|
102
|
+
detectedRole = 'Button';
|
|
103
|
+
} else if (current.type?.name || current.type?.displayName) {
|
|
104
|
+
const name = current.type.name || current.type.displayName;
|
|
105
|
+
if (typeof name === 'string' && name.length > 2 && !name.toLowerCase().includes('wrapper') && !name.startsWith('RCT')) {
|
|
106
|
+
detectedRole = name;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
//
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const textChild = findTextInChildren(current.memoizedProps.children);
|
|
124
|
-
if (textChild) return textChild;
|
|
111
|
+
// 2. Detect String Label Output
|
|
112
|
+
if (!bestLabel) {
|
|
113
|
+
if (current.memoizedProps?.accessibilityLabel) {
|
|
114
|
+
bestLabel = current.memoizedProps.accessibilityLabel;
|
|
115
|
+
} else if (current.memoizedProps?.testID) {
|
|
116
|
+
bestLabel = current.memoizedProps.testID;
|
|
117
|
+
} else if (current.memoizedProps?.title) {
|
|
118
|
+
bestLabel = current.memoizedProps.title;
|
|
119
|
+
} else if (current.memoizedProps?.placeholder) {
|
|
120
|
+
bestLabel = current.memoizedProps.placeholder;
|
|
121
|
+
} else if (typeof current.memoizedProps?.children === 'string' && current.memoizedProps.children.trim()) {
|
|
122
|
+
bestLabel = current.memoizedProps.children.trim();
|
|
123
|
+
} else if (Array.isArray(current.memoizedProps?.children)) {
|
|
124
|
+
bestLabel = findTextInChildren(current.memoizedProps.children);
|
|
125
|
+
} else if (current.memoizedProps?.children && typeof current.memoizedProps.children === 'object') {
|
|
126
|
+
bestLabel = findTextInChildren([current.memoizedProps.children]);
|
|
127
|
+
}
|
|
125
128
|
}
|
|
126
129
|
current = current.return;
|
|
127
130
|
depth++;
|
|
128
131
|
}
|
|
132
|
+
if (bestLabel) {
|
|
133
|
+
if (detectedRole && detectedRole.toLowerCase() !== 'text' && detectedRole.toLowerCase() !== 'view') {
|
|
134
|
+
const formattedRole = detectedRole.charAt(0).toUpperCase() + detectedRole.slice(1);
|
|
135
|
+
return `[${formattedRole}] ${bestLabel}`;
|
|
136
|
+
}
|
|
137
|
+
return bestLabel;
|
|
138
|
+
}
|
|
129
139
|
}
|
|
130
140
|
} catch {
|
|
131
141
|
// Fiber access failed — fall back gracefully
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
31
|
-
clarifying questions (which order
|
|
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
|
-
|
|
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 "
|
|
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
|
-
|
|
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
|
|
@@ -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) {
|