{"_rev":"257-b1f22f8129afcaade3f370118b9f26be","time":{"created":"2026-08-12T08:05:46.562Z","modified":"2026-08-12T08:05:47.590Z","0.0.2":"2026-03-20T21:24:40.914Z","0.2.0":"2026-03-21T16:47:08.186Z","0.3.0":"2026-03-21T16:49:22.197Z","0.4.0":"2026-03-21T17:39:47.633Z","0.4.1":"2026-03-21T17:42:40.294Z","0.4.2":"2026-03-21T17:46:09.153Z","0.4.3":"2026-03-21T23:08:50.815Z","0.4.4":"2026-03-21T23:15:53.378Z","0.4.5":"2026-03-21T23:18:17.692Z","0.4.6":"2026-03-22T00:16:46.798Z","0.5.0":"2026-03-22T18:21:23.841Z","0.5.1":"2026-03-22T18:24:30.331Z","0.5.2":"2026-03-22T18:35:26.234Z","0.5.3":"2026-03-22T18:58:59.147Z","0.5.4":"2026-03-22T19:05:49.089Z","0.5.5":"2026-03-22T19:08:41.512Z","0.5.6":"2026-03-22T19:52:33.974Z","0.5.7":"2026-03-22T20:09:44.202Z","0.5.8":"2026-03-22T20:11:07.576Z","0.5.9":"2026-03-22T21:41:57.383Z","0.5.10":"2026-03-22T21:52:11.074Z","0.5.11":"2026-03-22T21:54:05.082Z","0.5.12":"2026-03-22T22:07:34.220Z","0.5.13":"2026-03-22T22:16:49.269Z","0.5.14":"2026-03-22T22:17:33.653Z","0.5.15":"2026-03-22T22:27:31.504Z","0.5.16":"2026-03-22T22:52:00.833Z","0.5.17":"2026-03-22T22:55:37.823Z","0.5.18":"2026-03-22T22:58:28.573Z","0.5.19":"2026-03-22T22:59:51.310Z","0.5.20":"2026-03-22T23:01:14.045Z","0.5.21":"2026-03-23T11:00:06.854Z","0.5.22":"2026-03-23T14:25:07.274Z","0.6.0":"2026-03-23T15:11:54.509Z","0.6.1":"2026-03-23T15:13:58.086Z","0.6.2":"2026-03-23T15:15:56.221Z","0.6.3":"2026-03-23T15:23:48.327Z","0.7.1":"2026-03-24T10:14:48.197Z","0.7.2":"2026-03-24T13:08:49.923Z","0.7.3":"2026-03-24T16:57:23.381Z","0.7.4":"2026-03-24T17:02:56.119Z","0.7.5":"2026-03-24T17:07:05.165Z","0.7.6":"2026-03-24T17:23:23.269Z","0.7.7":"2026-03-24T18:53:06.589Z","0.7.8":"2026-03-24T18:59:46.781Z","0.7.9":"2026-03-24T19:41:01.139Z","0.7.10":"2026-03-24T20:06:16.688Z","0.7.11":"2026-03-24T20:11:38.951Z","0.7.12":"2026-03-24T20:31:49.092Z","0.7.13":"2026-03-24T20:38:42.570Z","0.7.14":"2026-03-24T20:42:52.020Z","0.7.15":"2026-03-24T20:51:51.319Z","0.7.16":"2026-03-24T20:56:00.738Z","0.7.17":"2026-03-24T21:14:47.482Z","0.7.18":"2026-03-24T21:24:26.483Z","0.7.19":"2026-03-24T21:38:07.929Z","0.7.20":"2026-03-24T21:53:46.212Z","0.7.22":"2026-03-24T22:24:19.385Z","0.7.23":"2026-03-24T22:55:36.255Z","0.7.24":"2026-03-24T23:08:34.203Z","0.7.25":"2026-03-24T23:23:32.431Z","0.7.26":"2026-03-24T23:33:50.678Z","0.7.27":"2026-03-24T23:38:52.779Z","0.7.28":"2026-03-24T23:40:31.581Z","0.8.0":"2026-03-24T23:41:48.825Z","0.8.1":"2026-03-24T23:46:07.504Z","0.8.2":"2026-03-25T09:19:00.836Z","0.8.3":"2026-03-25T15:47:13.853Z","0.8.4":"2026-03-25T16:23:49.148Z","0.8.5":"2026-03-25T16:28:28.618Z","0.8.6":"2026-03-25T16:33:02.716Z","0.8.7":"2026-03-25T16:41:32.113Z","0.8.8":"2026-03-25T16:44:53.316Z","0.8.9-beta.0":"2026-03-25T16:52:15.904Z","0.8.9-beta.1":"2026-03-25T17:01:04.125Z","0.8.9":"2026-03-25T17:04:51.806Z","0.8.10":"2026-03-25T17:15:28.251Z","0.8.11":"2026-03-25T17:17:08.447Z","0.8.12":"2026-03-25T17:22:03.964Z","0.8.13":"2026-03-25T17:25:22.646Z","0.8.13-beta.0":"2026-03-25T17:26:13.622Z","0.8.13-beta.1":"2026-03-25T17:28:38.596Z","0.8.13-beta.2":"2026-03-25T17:31:02.179Z","0.8.13-beta.3":"2026-03-25T17:39:30.304Z","0.8.14":"2026-03-25T19:59:18.671Z","0.8.15":"2026-03-25T20:05:19.315Z","0.8.16":"2026-03-25T20:11:49.173Z","0.9.0":"2026-03-25T20:18:23.319Z","0.9.9":"2026-03-26T21:33:45.702Z","0.9.1":"2026-03-26T21:35:30.951Z","0.9.2":"2026-03-26T21:41:05.737Z","0.9.3":"2026-03-26T22:10:42.626Z","0.9.4":"2026-03-26T22:17:44.838Z","0.9.5":"2026-03-26T22:25:18.366Z","0.9.10":"2026-03-28T10:25:28.407Z","0.9.11":"2026-03-29T23:21:21.941Z","0.9.12":"2026-03-30T16:24:20.749Z","0.9.13":"2026-03-30T17:36:59.366Z","0.9.14":"2026-03-30T17:40:36.310Z","0.9.15":"2026-03-30T17:46:54.095Z","0.9.16":"2026-03-30T19:05:01.666Z","0.9.17":"2026-03-30T21:00:54.013Z","0.9.18":"2026-03-31T13:15:39.957Z","0.9.19":"2026-04-09T12:09:56.046Z","0.9.20":"2026-04-09T13:01:05.292Z","0.9.21":"2026-04-09T13:11:00.704Z","0.9.22":"2026-04-09T13:31:11.574Z","0.9.23":"2026-04-09T13:55:33.357Z","0.9.24":"2026-04-09T13:58:08.649Z","0.9.25":"2026-04-09T14:15:59.355Z","0.9.26":"2026-04-09T14:17:31.028Z","0.9.27":"2026-04-09T21:17:08.627Z","0.9.28":"2026-04-11T19:45:01.248Z","0.9.29":"2026-04-15T20:20:07.543Z","0.9.30":"2026-04-17T13:17:49.659Z","0.9.31":"2026-04-17T15:54:15.556Z","0.9.32":"2026-04-17T18:49:33.235Z","0.9.33":"2026-04-18T10:01:42.807Z","0.9.34":"2026-04-18T10:16:18.740Z","0.9.35":"2026-04-18T20:28:56.402Z","0.9.36":"2026-04-18T21:24:35.220Z","0.9.37":"2026-04-19T07:28:28.444Z","0.9.39":"2026-04-25T12:03:17.134Z","0.9.40":"2026-04-25T12:11:19.904Z","0.9.41":"2026-04-25T12:15:06.152Z","0.9.44":"2026-04-28T18:41:12.469Z","0.9.45":"2026-04-28T18:46:43.538Z","0.9.46":"2026-04-28T18:47:17.351Z","0.9.47":"2026-04-28T18:47:45.046Z","0.9.48":"2026-04-28T18:48:10.775Z","0.9.49":"2026-04-28T18:48:37.093Z","0.9.50":"2026-04-28T18:49:06.997Z","0.9.51":"2026-04-28T21:19:14.554Z","0.9.52":"2026-04-28T21:25:49.306Z","0.9.53":"2026-04-29T10:11:26.320Z","0.9.54":"2026-04-29T10:37:57.581Z","0.9.55":"2026-04-29T10:40:47.119Z","0.9.56":"2026-04-29T10:50:38.887Z","0.9.57":"2026-05-01T14:43:37.397Z","0.9.58":"2026-05-02T09:32:30.881Z","0.9.59":"2026-05-02T15:07:01.103Z","0.9.60":"2026-05-02T15:12:55.980Z","0.9.61":"2026-05-02T21:32:02.588Z","0.9.62":"2026-05-03T09:16:46.416Z","0.9.63":"2026-05-03T10:56:06.038Z","0.9.64":"2026-05-03T19:06:00.547Z","0.9.65":"2026-05-03T19:27:37.117Z","0.9.66":"2026-05-03T19:37:48.094Z","0.9.67":"2026-05-07T14:16:04.298Z","0.9.68":"2026-05-09T11:39:24.682Z","0.9.69":"2026-05-09T13:13:42.394Z","0.9.70":"2026-05-10T20:57:29.379Z","0.9.71":"2026-05-11T06:54:36.402Z","0.9.72":"2026-05-11T07:51:07.304Z","0.9.73":"2026-05-14T11:12:31.956Z","0.9.74":"2026-05-21T18:29:51.864Z","0.9.75":"2026-05-21T18:36:41.707Z","0.9.76":"2026-05-21T18:40:16.649Z","0.9.77":"2026-05-21T18:42:30.456Z","0.9.78":"2026-05-21T18:45:18.367Z","0.9.79":"2026-05-21T18:48:50.600Z","0.9.80":"2026-05-21T18:53:17.446Z","0.9.82":"2026-05-22T10:04:44.000Z","0.9.83":"2026-05-22T10:10:51.246Z","0.9.84":"2026-05-22T13:20:48.767Z","0.9.85":"2026-05-23T07:29:42.390Z","0.9.90":"2026-05-23T15:34:47.655Z","0.9.91":"2026-06-09T20:12:11.480Z","0.9.92":"2026-08-12T08:05:47.378Z"},"_id":"react-native-agentic-ai","name":"react-native-agentic-ai","dist-tags":{"latest":"0.9.92"},"versions":{"0.9.92":{"name":"react-native-agentic-ai","version":"0.9.92","description":"Add an in-app AI support agent to React Native apps that understands UI, navigates screens, fills forms, and escalates to humans.","main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"types":"./lib/typescript/src/index.d.ts","default":"./lib/module/index.js"},"./withAppIntents":{"types":"./lib/typescript/src/plugin/withAppIntents.d.ts","default":"./lib/module/plugin/withAppIntents.js"},"./generate-map":"./generate-map.js","./package.json":"./package.json"},"bin":{"react-native-ai-agent":"bin/generate-map.cjs"},"scripts":{"example":"yarn workspace @twomilia/react-native-example","clean":"del-cli lib","prepare":"bob build && rm -rf lib/module/cli lib/typescript/src/cli","typecheck":"tsc -p tsconfig.build.json","lint":"eslint \"**/*.{js,ts,tsx}\"","test":"jest","test:coverage":"jest --coverage","publish:dual":"bash scripts/publish-dual.sh","publish:dual:dry":"bash scripts/publish-dual.sh --dry-run","generate-map":"ts-node src/cli/generate-map.ts"},"keywords":["react-native","expo","ai","ai-agent","agentic-ai","llm","agent","autonomous-agent","gemini","voice-ai","voice-agent","mcp","model-context-protocol","ai-testing","mobile-ai","ui-automation","react-native-ai","ai-assistant","knowledge-base","ai-sdk","natural-language","screen-reader","accessibility","human-in-the-loop"],"repository":{"type":"git","url":"git+https://github.com/mohamed2m2018/react-native-agentic-ai.git"},"author":{"name":"Mohamed Salah","email":"mohamed2m2018@gmail.com","url":"https://www.linkedin.com/in/muhammad-salah-eldin/"},"license":"SEE LICENSE IN LICENSE","bugs":{"url":"https://github.com/mohamed2m2018/react-native-agentic-ai/issues"},"homepage":"https://github.com/mohamed2m2018/react-native-agentic-ai#readme","publishConfig":{"registry":"https://registry.npmjs.org/"},"dependencies":{"@google/genai":"^1.0.0","@babel/parser":"^7.29.2","@babel/traverse":"^7.29.0","@babel/types":"^7.29.0","react-native-view-shot":"4.0.3"},"devDependencies":{"@eslint/compat":"^1.3.2","@eslint/eslintrc":"^3.3.1","@eslint/js":"^9.35.0","@react-native/babel-preset":"0.83.0","@react-native/eslint-config":"0.83.0","@testing-library/react-native":"^13.3.3","@types/jest":"^29.5.14","@types/react":"^19.1.12","@types/react-native":"^0.72.8","del-cli":"^6.0.0","dotenv":"^17.3.1","eslint":"^9.35.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","expo":"~55.0.8","jest":"^29.7.0","prettier":"^3.5.0","react":"19.2.0","react-native":"0.83.2","react-native-audio-api":"*","react-native-builder-bob":"^0.40.18","react-test-renderer":"^19.2.0","turbo":"^2.5.6","typescript":"^5.9.2"},"peerDependencies":{"@react-native-async-storage/async-storage":"*","expo-image-picker":"*","expo-speech-recognition":"*","react":">=18.0.0","react-native":">=0.73.0","react-native-audio-api":"*","react-native-screens":"*"},"peerDependenciesMeta":{"@react-native-async-storage/async-storage":{"optional":true},"react-native-audio-api":{"optional":true},"react-native-screens":{"optional":true},"expo-image-picker":{"optional":true},"expo-speech-recognition":{"optional":true}},"codegenConfig":{"name":"RNMobileAIOverlaySpec","type":"components","jsSrcsDir":"src/specs","ios":{"componentProvider":{"MobileAIFloatingOverlay":"MobileAIFloatingOverlayComponentView"}},"android":{"javaPackageName":"com.mobileai.overlay"}},"workspaces":["example-react-navigation"],"react-native-builder-bob":{"source":"src","output":"lib","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"prettier":{"quoteProps":"consistent","singleQuote":true,"tabWidth":2,"trailingComma":"es5","useTabs":false},"jest":{"preset":"react-native","setupFilesAfterEnv":["<rootDir>/src/__tests__/setup.ts"],"testMatch":["**/?(*.)+(spec|test).[jt]s?(x)"],"modulePathIgnorePatterns":["<rootDir>/example-react-navigation/node_modules","<rootDir>/lib/"],"collectCoverageFrom":["src/**/*.{ts,tsx}","!src/**/*.d.ts","!src/__tests__/**","!src/index.ts"]},"create-react-native-library":{"type":"library","languages":"js","tools":["eslint","jest"],"version":"0.57.2"},"gitHead":"d42c6de425ffe893718e1e96c93712c9cdaf4b7e","_id":"react-native-agentic-ai@0.9.92","_nodeVersion":"25.4.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-oTKl3/4emHpR/B+Szft+LQXZzkqP+CtD4h1uot6JNnFIiGTiogAjJaW+7O9SvX4/ciKUvaGgs2kduFUN26btZA==","shasum":"c1b85db8b903769b10eb2c8c14c45161cc7ebc08","tarball":"https://registry.npmjs.org/react-native-agentic-ai/-/react-native-agentic-ai-0.9.92.tgz","fileCount":247,"unpackedSize":1320383,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDHaHIbyRzWCTgjPpf8U3TA7lO10tJxwh4PMcNk8RnTeQIgPhKwekc1maGPEvpJ4JzOBN0ILvlleMp6s4eBJiY6qu0="}]},"_npmUser":{"name":"mohamed2m2018","email":"mohamed2m2018@gmail.com"},"directories":{},"maintainers":[{"name":"mohamed2m2018","email":"mohamed2m2018@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-agentic-ai_0.9.92_1786521947068_0.6680324265839923"},"_hasShrinkwrap":false}},"maintainers":[{"name":"mohamed2m2018","email":"mohamed2m2018@gmail.com"}],"description":"Add an in-app AI support agent to React Native apps that understands UI, navigates screens, fills forms, and escalates to humans.","homepage":"https://github.com/mohamed2m2018/react-native-agentic-ai#readme","keywords":["react-native","expo","ai","ai-agent","agentic-ai","llm","agent","autonomous-agent","gemini","voice-ai","voice-agent","mcp","model-context-protocol","ai-testing","mobile-ai","ui-automation","react-native-ai","ai-assistant","knowledge-base","ai-sdk","natural-language","screen-reader","accessibility","human-in-the-loop"],"repository":{"type":"git","url":"git+https://github.com/mohamed2m2018/react-native-agentic-ai.git"},"author":{"name":"Mohamed Salah","email":"mohamed2m2018@gmail.com","url":"https://www.linkedin.com/in/muhammad-salah-eldin/"},"bugs":{"url":"https://github.com/mohamed2m2018/react-native-agentic-ai/issues"},"license":"SEE LICENSE IN LICENSE","readme":"# Twomilia React Native\n\nAn in-app AI assistant for React Native that reads your app's UI automatically — no wrappers required. It can guide users, perform approved actions, answer questions using your knowledge base, and hand off to human support.\n\n[![npm version](https://img.shields.io/npm/v/@twomilia/react-native.svg)](https://www.npmjs.com/package/@twomilia/react-native)\n[![React Native](https://img.shields.io/badge/react--native-≥0.73-blue.svg)](https://reactnative.dev/)\n[![License](https://img.shields.io/badge/license-EULA-lightgrey.svg)](./LICENSE)\n[![Security](https://img.shields.io/badge/security-policy-blue.svg)](./SECURITY.md)\n[![Roadmap](https://img.shields.io/badge/roadmap-public-orange.svg)](./ROADMAP.md)\n[![Changelog](https://img.shields.io/badge/changelog-keep--a--changelog-blueviolet.svg)](./CHANGELOG.md)\n\n![Twomilia demo](./assets/demo.gif)\n\n## Contents\n\n- [How It Works](#how-it-works)\n- [Quick Start](#quick-start)\n- [Platform Setup](#platform-setup)\n- [Optional Dependencies](#optional-dependencies)\n- [Screen Map](#screen-map)\n- [Provider Options](#provider-options)\n- [Core Concepts](#core-concepts)\n- [Guardrails](#guardrails)\n- [Support Mode](#support-mode)\n- [Common Recipes](#common-recipes)\n- [TypeScript](#typescript)\n- [More Docs](#more-docs)\n- [Requirements](#requirements)\n- [Troubleshooting](#troubleshooting)\n- [Security](./SECURITY.md)\n- [Roadmap](./ROADMAP.md)\n- [Changelog](./CHANGELOG.md)\n- [License](#license)\n\n---\n\n## How It Works\n\nTwomilia uses **React Fiber tree traversal** to find every interactive element on the current screen at runtime — no component wrappers, no prop drilling, no code changes to your existing screens.\n\n```\nUser message → Agent Runtime → Fiber tree snapshot → LLM → Tool call → UI action / answer\n                                      ↑\n                          Optional: screen map + knowledge base\n```\n\nThe `<AIAgent>` component sits at your app root. When the user sends a message:\n\n1. The runtime walks the live React Fiber tree to discover interactive elements (buttons, inputs, pickers, etc.) and their accessibility labels.\n2. It builds a structured screen snapshot and sends it to the LLM along with your instructions, knowledge base, and registered data sources.\n3. The LLM calls tools (`tap`, `type`, `scroll`, `navigate`, `query_knowledge`, your custom actions, etc.).\n4. The runtime executes each tool call, respects guardrails, and streams results back to the chat UI.\n\nThe assistant sees exactly what is mounted — nothing more, nothing less. Use `aiIgnore` on sensitive elements to hide them from the snapshot, or `transformScreenContent` to mask values before the LLM call.\n\n---\n\n## Quick Start\n\n### 1. Install\n\n```bash\nnpm install @twomilia/react-native\n```\n\n### 2. Wrap Your App\n\n**React Navigation**\n\n```tsx\nimport { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';\nimport { AIAgent } from '@twomilia/react-native';\n\nexport default function App() {\n  const navRef = useNavigationContainerRef();\n\n  return (\n    <AIAgent\n      analyticsKey=\"twomilia_pub_xxxxxxxx\"\n      navRef={navRef}\n    >\n      <NavigationContainer ref={navRef}>\n        {/* your screens */}\n      </NavigationContainer>\n    </AIAgent>\n  );\n}\n```\n\n**Expo Router**\n\n```tsx\nimport { Slot, useNavigationContainerRef } from 'expo-router';\nimport { AIAgent } from '@twomilia/react-native';\n\nexport default function RootLayout() {\n  const navRef = useNavigationContainerRef();\n\n  return (\n    <AIAgent\n      analyticsKey=\"twomilia_pub_xxxxxxxx\"\n      navRef={navRef}\n    >\n      <Slot />\n    </AIAgent>\n  );\n}\n```\n\n### 3. Run a Native Build\n\nThe SDK uses native modules. You cannot test it in Expo Go.\n\n```bash\n# Expo\nnpx expo run:ios\nnpx expo run:android\n\n# React Native CLI\nnpx react-native run-ios\nnpx react-native run-android\n```\n\nThat's it — a floating chat button appears in your app.\n\n---\n\n## Platform Setup\n\n### iOS\n\nAfter adding the package, run CocoaPods:\n\n```bash\nnpx pod-install\n# or\ncd ios && pod install\n```\n\n### Android\n\nNo extra steps. The SDK ships its own `AndroidManifest.xml` with required permissions.\n\n### Expo Managed Workflow\n\nExpo managed workflow is **not supported** because the SDK requires native modules. Use a development build or prebuild:\n\n```bash\nnpx expo prebuild\nnpx expo run:ios   # or run:android\n```\n\n### Architecture Support\n\nThe SDK supports both the Old Architecture (Paper) and the New Architecture (Fabric). No extra config is needed — the correct native code is selected automatically at build time based on your app's architecture setting.\n\n---\n\n## Optional Dependencies\n\nThese packages are technically optional — the SDK won't crash without them — but they are important for a complete experience. For full functionality, install all of them.\n\n| Package | Needed for |\n| --- | --- |\n| `react-native-screens` | Better navigation support in navigation-heavy apps |\n| `@react-native-async-storage/async-storage` | Persisting user consent across sessions |\n| `react-native-audio-api` | Voice input (microphone capture) |\n| `expo-speech-recognition` | Voice input on Expo |\n| `expo-image-picker` | Image attachments in chat |\n\n```bash\n# Install all optional features\nnpm install react-native-screens @react-native-async-storage/async-storage\nnpm install react-native-audio-api expo-speech-recognition expo-image-picker\n\n# iOS pods after adding native deps\nnpx pod-install\n```\n\n---\n\n## Screen Map\n\nA screen map gives the AI knowledge of all your screens, their content, and navigation chains — so it can route users to the right place even without them being there first.\n\nThe SDK works without it using the live Fiber tree alone, but generating a screen map is **strongly recommended**. It significantly improves navigation accuracy and reduces the number of LLM calls needed to find the right screen, leading to faster and cheaper agent interactions.\n\n### Generate Once\n\n```bash\nnpx react-native-ai-agent generate-map\n```\n\nThis creates `ai-screen-map.json` in your project root. Commit it to source control.\n\n### Auto-Generate on Metro Start\n\n```js\n// metro.config.js\nconst { getDefaultConfig } = require('expo/metro-config');\n\nrequire('@twomilia/react-native/generate-map').autoGenerate(__dirname);\n\nmodule.exports = getDefaultConfig(__dirname);\n```\n\n### Pass It to AIAgent\n\n```tsx\nimport screenMap from './ai-screen-map.json';\n\n<AIAgent screenMap={screenMap} navRef={navRef}>\n  {children}\n</AIAgent>\n```\n\n---\n\n## Provider Options\n\n### Twomilia Cloud (recommended for production)\n\n`analyticsKey` routes AI calls through the Twomilia hosted proxy. It also enables analytics, the knowledge base dashboard, support ticket inbox, and conversation history — without exposing an API key in your app bundle.\n\n```tsx\n<AIAgent analyticsKey=\"twomilia_pub_xxxxxxxx\" navRef={navRef}>\n  {children}\n</AIAgent>\n```\n\nGet your key at [twomilia.com](https://twomilia.com).\n\n### Your Own Backend Proxy (production without Twomilia Cloud)\n\nRoute requests through your server. Your server adds the real API key before forwarding to Gemini or OpenAI.\n\n```tsx\n<AIAgent\n  provider=\"openai\"\n  proxyUrl=\"https://api.example.com/twomilia/chat\"\n  proxyHeaders={{ Authorization: `Bearer ${sessionToken}` }}\n  navRef={navRef}\n>\n  {children}\n</AIAgent>\n```\n\nFor voice mode, you can use a separate WebSocket endpoint:\n\n```tsx\n<AIAgent\n  proxyUrl=\"https://api.example.com/twomilia/chat\"\n  voiceProxyUrl=\"wss://api.example.com/twomilia/voice\"\n  voiceProxyHeaders={{ Authorization: `Bearer ${sessionToken}` }}\n  navRef={navRef}\n>\n  {children}\n</AIAgent>\n```\n\n### Direct API Key (local prototyping only)\n\n```tsx\n<AIAgent provider=\"gemini\" apiKey=\"YOUR_DEV_ONLY_KEY\" navRef={navRef}>\n  {children}\n</AIAgent>\n```\n\n> ⚠️ **Never ship an API key in your production app bundle.** Use a proxy or `analyticsKey` for production.\n\n---\n\n## Core Concepts\n\n### Interaction Modes\n\nControl how much autonomy the AI has:\n\n| Mode | What it does |\n| --- | --- |\n| `companion` | Read-only. The AI reads the screen and guides the user in plain language. Cannot tap, type, scroll, or navigate. |\n| `copilot` | **Default.** Performs approved actions. Asks once before starting a flow, executes steps silently, then asks before irreversible commits. |\n| `autopilot` | Full autonomy. All actions execute without confirmation. Use only for trusted, low-risk automation flows. |\n\n```tsx\n<AIAgent interactionMode=\"companion\" analyticsKey=\"twomilia_pub_xxxxxxxx\">\n  {children}\n</AIAgent>\n```\n\nCompanion mode is the safest choice when trust matters more than automation. The assistant can read the screen, answer questions, look up knowledge and app data, and escalate to a human — but it cannot operate the app on the user's behalf.\n\n### App Data With `useData`\n\nRegister async data sources the AI can query directly instead of guessing from what's visible on screen. Use this for order status, product catalogs, account data, recommendations, or any backend API.\n\n```tsx\nimport { useData } from '@twomilia/react-native';\n\nfunction OrdersScreen() {\n  useData(\n    'orders',\n    'Read the signed-in customer orders and delivery status',\n    {\n      orderId: 'Order identifier',\n      status: 'Current delivery status',\n      eta: 'Estimated arrival time',\n    },\n    async ({ query }) => searchOrders(query)\n  );\n\n  return <OrdersList />;\n}\n```\n\n### App Actions With `useAction`\n\nRegister safe app-owned operations the AI can call by name. Use this for actions that are better expressed in code than by tapping UI — applying coupons, filtering results, clearing a cart, toggling a setting.\n\n```tsx\nimport { useAction } from '@twomilia/react-native';\n\nfunction CartScreen() {\n  useAction(\n    'apply_coupon',\n    'Apply a coupon code to the current cart',\n    {\n      code: { type: 'string', description: 'Coupon code', required: true },\n    },\n    async ({ code }) => applyCoupon(String(code))\n  );\n\n  return <CartView />;\n}\n```\n\nThe handler is always kept fresh via an internal ref — no stale closure bugs, even when it captures mutable state.\n\n### Reading Agent State With `useAI`\n\nAccess the agent from any component inside the `<AIAgent>` tree:\n\n```tsx\nimport { useAI } from '@twomilia/react-native';\n\nfunction MyScreen() {\n  const { send, isLoading, status, messages, clearMessages, cancel } = useAI();\n\n  return (\n    <Button\n      title=\"Check my order\"\n      onPress={() => send('What is the status of my latest order?')}\n    />\n  );\n}\n```\n\nTemporarily disable UI control for a specific screen without changing the root config:\n\n```tsx\nconst { send } = useAI({ enableUIControl: false });\n```\n\n### Knowledge Base\n\nGive the AI domain knowledge it can query during a conversation:\n\n```tsx\n<AIAgent\n  knowledgeBase={[\n    {\n      id: 'returns',\n      title: 'Return policy',\n      content: 'Customers can request returns within 30 days of delivery.',\n    },\n    {\n      id: 'shipping',\n      title: 'Shipping times',\n      content: 'Standard shipping takes 3–5 business days.',\n    },\n  ]}\n>\n  {children}\n</AIAgent>\n```\n\nYou can also pass a custom retriever function or configure project-level knowledge in the Twomilia dashboard (when using `analyticsKey`).\n\n### AI Zones\n\n`AIZone` is a declarative boundary that grants the AI permission to modify a specific subtree — highlighting elements, injecting hint cards, or simplifying a complex view for the user.\n\n```tsx\nimport { AIZone } from '@twomilia/react-native';\n\nfunction CheckoutScreen() {\n  return (\n    <AIZone\n      id=\"checkout-form\"\n      allowHighlight\n      allowInjectHint\n      allowSimplify\n    >\n      <PaymentForm />\n    </AIZone>\n  );\n}\n```\n\nWithout an `AIZone`, the AI can still read and interact with elements — zones just add richer intervention capabilities like card injection and simplification.\n\n---\n\n## Guardrails\n\nGuardrails are enforced by the runtime, not by the LLM alone.\n\n### Consent (Apple Guideline 5.1.2(i))\n\nBy default, the SDK shows a consent dialog before the first AI interaction. No screen data is sent to the AI provider until the user explicitly agrees.\n\n```tsx\n// Persist consent across sessions (uses AsyncStorage)\n<AIAgent consent={{ required: true, persist: true }}>\n  {children}\n</AIAgent>\n\n// Opt out — only appropriate when you have your own consent flow\n<AIAgent consent={{ required: false }}>\n  {children}\n</AIAgent>\n```\n\n### Action Safety\n\nSemantic guardrails classify each action as `allow`, `ask`, or `block` based on risk level. Active by default in copilot mode.\n\n```tsx\n<AIAgent\n  interactionMode=\"copilot\"\n  actionSafety={{\n    classifier: 'default',\n    unknownActionDecision: 'ask',\n    approvalReuse: 'risk-boundary',\n    onDecision: (decision) => {\n      console.log(decision.capability, decision.risk, decision.decision);\n    },\n  }}\n>\n  {children}\n</AIAgent>\n```\n\n### Element-Level Controls\n\nAdd these props directly to any component:\n\n```tsx\n// Block the AI from interacting with this element\n<TextInput aiIgnore />\n\n// Force a confirmation before the AI interacts with this element\n<Button aiConfirm title=\"Delete account\" onPress={deleteAccount} />\n```\n\n### Content Masking\n\nMask sensitive values before the LLM ever sees them:\n\n```tsx\n<AIAgent\n  transformScreenContent={(content) =>\n    content.replace(/\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b/g, '[CARD REDACTED]')\n  }\n>\n  {children}\n</AIAgent>\n```\n\nRead the full safety model in [docs/guardrails.md](./docs/guardrails.md).\n\n---\n\n## Support Mode\n\nSupport mode transforms the AI into a customer support assistant with a greeting, quick replies, self-service help, escalation to humans, and CSAT collection.\n\n```tsx\n<AIAgent\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n  userContext={{ userId: user.id, email: user.email, name: user.name }}\n  supportMode={{\n    enabled: true,\n\n    // Greeting shown when the chat opens\n    greeting: {\n      message: 'Hi! 👋 How can I help you today?',\n      agentName: 'Nora',\n    },\n\n    // Quick reply buttons below the greeting\n    quickReplies: [\n      { label: 'Track my order', icon: '📦' },\n      { label: 'Return an item', icon: '↩️' },\n      { label: 'Talk to a human', icon: '💬' },\n    ],\n\n    // Self-service help (zero LLM cost for common questions)\n    quickActions: {\n      enabled: true,\n      topics: [\n        {\n          id: 'orders',\n          label: 'Orders',\n          icon: '📦',\n          articles: [\n            {\n              question: 'How do I track my order?',\n              answer: 'Go to Orders → tap your order → tap Track.',\n            },\n          ],\n        },\n      ],\n    },\n\n    // Escalation to human support\n    escalation: {\n      provider: 'mobileai',  // or 'custom' with onEscalate callback\n    },\n\n    // CSAT survey after conversation\n    csat: {\n      enabled: true,\n      surveyType: 'csat',\n      ratingType: 'emoji',\n      onSubmit: (rating) => console.log('CSAT:', rating.score),\n    },\n\n    // Topics that always go to a human — no AI attempt\n    autoEscalateTopics: ['account deletion', 'legal request', 'billing dispute'],\n\n    // AI persona\n    persona: {\n      agentName: 'Nora',\n      preset: 'warm-concise',\n    },\n  }}\n>\n  {children}\n</AIAgent>\n```\n\nWhen `analyticsKey` is set and `escalation.provider` is `'mobileai'`, tickets appear in the Twomilia dashboard inbox and human replies are streamed back into the chat in real time via WebSocket.\n\n### Offline Notifications\n\nPass a push token so users get notified when a human replies:\n\n```tsx\n<AIAgent\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n  pushToken={expoPushToken}\n  pushTokenType=\"expo\"  // 'fcm' | 'expo' | 'apns'\n>\n  {children}\n</AIAgent>\n```\n\n---\n\n## Common Recipes\n\n### Custom Chat UI\n\nHide the built-in chat bar and drive the assistant from your own UI.\n\n```tsx\nimport { AIAgent, useAI } from '@twomilia/react-native';\n\nfunction MyAssistantInput() {\n  const { send, isLoading, status, messages, cancel } = useAI();\n\n  return (\n    <MyCustomChatUI\n      messages={messages}\n      isLoading={isLoading}\n      status={status}\n      onSend={send}\n      onCancel={cancel}\n    />\n  );\n}\n\n<AIAgent showChatBar={false} analyticsKey=\"twomilia_pub_xxxxxxxx\">\n  <MyAssistantInput />\n  {children}\n</AIAgent>\n```\n\n### Proactive Help\n\nTrigger a help hint automatically when the SDK detects user hesitation:\n\n```tsx\n<AIAgent\n  proactiveHelp={{\n    enabled: true,\n    idleThresholdMs: 8000,       // Trigger after 8s of inactivity\n    message: 'Need help? I can guide you through checkout.',\n  }}\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n>\n  {children}\n</AIAgent>\n```\n\n### Guided Onboarding\n\nWalk new users through structured setup steps proactively on first launch:\n\n```tsx\n<AIAgent\n  onboarding={{\n    enabled: true,\n    triggerOnce: true,\n    steps: [\n      { screen: 'Profile', message: 'Let's set up your profile first.' },\n      { screen: 'Preferences', message: 'Now pick what matters to you.' },\n    ],\n  }}\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n>\n  {children}\n</AIAgent>\n```\n\n### Custom Support Prompt\n\nBuild support instructions programmatically instead of the `supportMode` config:\n\n```tsx\nimport { AIAgent, buildSupportPrompt } from '@twomilia/react-native';\n\n<AIAgent\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n  instructions={{\n    system: buildSupportPrompt({\n      enabled: true,\n      persona: { agentName: 'Nora', preset: 'warm-concise' },\n      autoEscalateTopics: ['account deletion', 'legal request'],\n    }),\n  }}\n  userContext={{ userId: user.id, email: user.email }}\n  navRef={navRef}\n>\n  {children}\n</AIAgent>\n```\n\n### MCP Bridge\n\nConnect local or remote Model Context Protocol tools:\n\n```tsx\n<AIAgent\n  mcpServerUrl=\"ws://localhost:3101\"\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n>\n  {children}\n</AIAgent>\n```\n\nSee [mcp-server/README.md](./mcp-server/README.md) for setup.\n\n### Budget Caps\n\nPrevent runaway token costs per user request:\n\n```tsx\n<AIAgent\n  maxTokenBudget={8000}   // stop if prompt + completion exceeds 8k tokens\n  maxCostUSD={0.05}       // stop if estimated cost exceeds $0.05\n  analyticsKey=\"twomilia_pub_xxxxxxxx\"\n>\n  {children}\n</AIAgent>\n```\n\n---\n\n## TypeScript\n\nAll types are exported from the package root. Import them directly:\n\n```tsx\nimport type {\n  // Agent core\n  AIMessage,\n  AgentMode,\n  InteractionMode,\n  ExecutionResult,\n  TokenUsage,\n  ConversationSummary,\n\n  // Screen / element\n  ScreenMap,\n  ScreenMapEntry,\n  DehydratedScreen,\n  InteractiveElement,\n\n  // Knowledge base\n  KnowledgeEntry,\n  KnowledgeRetriever,\n  KnowledgeBaseConfig,\n\n  // Action safety\n  ActionSafetyConfig,\n  ActionSafetyDecision,\n  ActionSafetyRisk,\n\n  // Tools\n  ToolDefinition,\n  ActionDefinition,\n\n  // Rich UI\n  BlockDefinition,\n  ChatBarTheme,\n  RichUITheme,\n  RichUIThemeOverride,\n\n  // Support\n  SupportModeConfig,\n  EscalationConfig,\n  EscalationContext,\n  CSATConfig,\n  CSATRating,\n  SupportTicket,\n  QuickActionsConfig,\n  HelpTopic,\n  HelpArticle,\n\n  // Provider\n  AIProviderName,\n} from '@twomilia/react-native';\n```\n\n---\n\n## More Docs\n\n- [API Reference](./docs/api-reference.md): All `AIAgent` props, hook signatures, and type definitions.\n- [Guardrails](./docs/guardrails.md): Approval flow, semantic safety, masking, overrides, and tracing.\n- [Rich UI](./docs/rich-ui.md): Structured blocks, `RichContentRenderer`, `AIZone`, themes, and block handlers.\n- [Production](./docs/production.md): Proxy setup, support tickets, analytics, consent, and security notes.\n- [Wireframe Capture](./docs/wireframe-capture.md): Visual telemetry snapshots.\n- [MCP Bridge](./mcp-server/README.md): Connect local or remote Model Context Protocol tools.\n- [AI Emulator Testing](./example-ai-testing/README.md): Test app flows through an AI-driven emulator harness.\n\n---\n\n## Requirements\n\n| Requirement | Version |\n| --- | --- |\n| React Native | `>=0.73.0` |\n| React | `>=18.0.0` |\n| iOS | Native build (not Expo Go) |\n| Android | Native build (not Expo Go) |\n| Expo | Development build or prebuild |\n\n---\n\n## Troubleshooting\n\n**The chat button doesn't appear.**\nMake sure `<AIAgent>` is the outermost component in your tree, wrapping the navigation container and all screens. Check that you're running a native build, not Expo Go.\n\n**The assistant cannot navigate.**\nPass `navRef` (from `useNavigationContainerRef()`), generate a screen map, and verify that route names in the map match your navigation setup exactly.\n\n**The assistant does not see a control.**\nAdd an `accessibilityLabel` to the element. Make sure it is mounted (not conditionally hidden) when the user sends the message. Avoid wrapping important controls in components that strip host props.\n\n**A sensitive control is visible to the assistant.**\nAdd `aiIgnore` to the element, or mask its value with `transformScreenContent`.\n\n**The runtime asks more than expected.**\nCheck `actionSafety.onDecision` logs. Low confidence, unknown capability, changed scope, and high-impact risk boundaries intentionally ask rather than silently acting. Tune `approvalReuse` or override specific capabilities via `actionSafety.overrideDecision`.\n\n**The consent dialog keeps appearing.**\nPass `consent={{ required: true, persist: true }}` to persist consent using `AsyncStorage`. Make sure `@react-native-async-storage/async-storage` is installed and linked.\n\n**Voice mode does not start.**\nConfirm microphone permissions are granted (`react-native-audio-api` or `expo-speech-recognition` require them). Install the voice dependencies and run a fresh native build. On iOS, add `NSMicrophoneUsageDescription` to `Info.plist`.\n\n**Analytics are not appearing in the dashboard.**\nVerify `analyticsKey` starts with `twomilia_pub_`. Check that the device has internet access. Enable `debug={true}` and look for `TelemetryService` log lines.\n\n**MCP bridge is not connecting.**\nConfirm the MCP server is running and the `mcpServerUrl` is reachable from the device (use your machine's local IP address on a physical device, not `localhost`).\n\n**Build fails with codegen errors (New Architecture).**\nRun `npx react-native build-android --mode debug` or `npx pod-install` to trigger codegen. Make sure `@twomilia/react-native` is listed in your app's `package.json`, not just a workspace dependency.\n\n---\n\n## License\n\nSee [LICENSE](./LICENSE).\n","readmeFilename":"README.md"}