# MyAi mobile

## 2026-10-19 local selection update

The [selection update](/reference/docs/LOCAL_AI_SELECTION.md) removes largest-memory automatic ordering, adds OS headroom and invalid-reading guards, persists Fast/Balanced/Quality preferences, and keeps manual choices. Larger automatic choices require device/artifact observations rather than an assumption that unused RAM means better answers. Native generation preserves model Jinja templates, adds Qwen 3 sampling defaults, bounds Fast context/output and releases failed contexts. Typecheck and the expanded 50-test mobile suite pass. The native Android build was attempted but this laptop has no configured Java/Android SDK; no new native Android, physical-phone, battery, thermal or iOS claim is made. Existing native evidence below is unchanged and does not validate these changes.

## 0.2.5 web-search update

Automatic current-information lookup uses Bing RSS summaries, with local answer generation, source links, an offline setting, bounded requests, cancellation, and relevance filtering. No cloud AI, paid service or API key was added. Searches send the current query only; saved history, files and notes are excluded. Shared desktop/mobile search logic is checked for byte equality. Windows development-runtime and native Android acceptance evidence and package checksums are recorded under `artifacts/web-search/`; Apple and physical-phone behavior remain unverified. Windows code-integrity policy blocks the unsigned standalone executable; the official Electron development runtime passed the desktop acceptance flow, and the separate Program Files installation was preserved. This is summary retrieval, not full-page browsing or guaranteed live factual accuracy.

## MyAi 0.2.4 — simple setup and helpers

The default flow chooses a suitable free AI for the device, prefers a compatible existing download, and checks live memory/storage/temperature. Model management and connection settings are optional advanced controls. Helper creation needs a name and a description; Save and Cancel stay available outside the scrolling form. Starting another helper asks before discarding an unsent draft. Native acceptance and screenshots are recorded under `artifacts/simple-experience/after/`; this is an Android emulator check, not physical-phone or iOS certification.


## Free AI update — 0.2.1

The current Android update adds saved local agents for coding, writing, learning and general tasks. Profiles keep a model and bounded instructions; every new task snapshots them into a normal local conversation. Editing/deleting a profile preserves existing conversations. Profiles are validated in preferences/export/import and erased with chats & notes. Phone agents cannot execute commands or edit arbitrary device files; approved desktop tools remain a separate paired workflow.

Model recommendations now use available RAM and storage rather than always recommending the first catalog entry. Pinned 1.7B/4B general and 7B coding choices are offered with conservative memory budgets. Inference checks usable RAM and temperature again, and switching models releases the previous context before measuring free memory. Catalog availability is not a physical-device performance certification.

The Android APK is `artifacts/mobile/core-0.2.1/HOLD-0.2.1-android.apk`, versionName 0.2.1/versionCode 3, 177,039,639 bytes, SHA-256 `65d922e879162748188c6e637ef42ba807fc11a5acc2bd7dd00cab5591487813`. It is development-signed and its installed copy matches. Typecheck and 43 tests passed; six native emulator groups exercised validation, creation, actual local coding, restart/edit isolation, memory gating and deletion. [Native acceptance](/reference/docs/evidence/mobile-local-agents-0.2.1.json) and [92-input build binding](/reference/docs/evidence/mobile-free-ai-build-0.2.1.json) have exact scope. The iOS interface shares this code but has not been built or run on Apple hardware. Earlier 0.2.0 evidence below remains historical, scoped to its own hashes.

HOLD is a native React Native application in `apps/mobile`, with actual local llama.cpp text and photo inference. It works without an account, Metro server, or HOLD service. An optional paired desktop is a separate, explicitly chosen destination; local chats never fall back to it. Expo Go cannot load the native modules.

## Architecture decision

Research checked 2026-10-15. React Native with Expo native project generation provides a single accessible consumer interface while preserving native memory, temperature, audio, storage and inference boundaries. `llama.rn` 0.12.9 is pinned instead of the then-latest 0.13 prerelease. Its supported New Architecture is enabled. iOS attempts Metal, then falls back to CPU if initialization fails. Android uses CPU until GPU support has been verified on actual devices. HOLD does not claim Apple Neural Engine, NNAPI, Hexagon or Android GPU acceleration.

Alternatives evaluated: MLC needs platform model libraries alongside converted weights; that adds a model compilation/distribution pipeline. Google AI Edge's MediaPipe LLM inference is in maintenance mode, so a new integration would use its LiteRT-LM successor. The shared GGUF catalog and a maintained native binding made llama.cpp the practical initial choice.

Primary sources: [llama.rn repository and native installation](https://github.com/mybigday/llama.rn), [MLC model library compilation](https://llm.mlc.ai/docs/compilation/compile_models.html), [Google AI Edge status](https://developers.google.com/edge/mediapipe/solutions/genai/llm_inference), [on-device speech recognition binding](https://github.com/jamsch/expo-speech-recognition), [Android official SDK tools](https://developer.android.com/studio).


## Current functionality

- A canonical HOLD mark, contextual response/conversation actions, model recommendations based on real RAM/storage, system/light/dark themes, keyboard-aware layout and large-text support. Code blocks scroll horizontally and can be copied; Markdown never executes HTML or loads remote images.
- Pinned official Qwen 2.5 0.5B Q4_K_M, Qwen 3 0.6B Q8, and SmolVLM 256M Q8 plus its vision projector. Native SHA-256/byte checks gate installation and every new inference context. Downloads pause/resume, account for remaining storage, and stop in the background. The photo model requires both verified files.
- Genuine native streaming, Stop, Retry, Edit last message, model switching, local history/search/rename/delete and response sharing. Context uses the native chat template/tokenizer, preserves the newest turn and omits whole older turns when needed. Reply details expose measured context, local integrity/loading time, first-token callback intervals, total response time and native token rate; omission warnings stay visible. Measurements persist only with the local conversation and validated export/import. Each conversation can choose a role, instructions, notes scope, output length, and supported local temperature/context limits.
- Optional notes are written and edited by the user, disabled globally by default, and can be disabled again per conversation. Erase notes is explicit. This is not automatic semantic memory or silent ingestion.
- Native camera and photo-library input, resized/re-encoded into private JPEG storage. SmolVLM receives the actual image through llama.rn's multimodal pipeline. Android's incoming text/photo share flow opens a review screen and a new local draft; it never auto-sends or silently routes to desktop. An iOS incoming-share extension is not implemented.
- Plaintext JSON export through the system share sheet or Android Save to Files. Import previews counts, validates versions/limits, regenerates IDs, strips desktop credentials/grants, re-decodes photos natively, and commits the append in a transaction. Preferences restore is a separate choice and imported notes remain disabled. Invalid stored rows are preserved for recovery export rather than silently erased.
- Voice input always requests on-device recognition. Explicit local WAV transcription accepts mono 16 kHz 16-bit PCM, at most two minutes, produces an editable draft, and removes the temporary audio after completion/error. It requires Android 13+ or iOS and an installed supported recognizer. Android playback lazily starts the system speech engine only after Listen, chooses an offline-marked voice, reports completion and supports Stop. No cloud retry exists.
- Desktop pairing uses a short-lived single-use invitation, TLS 1.3 certificate pinning before secrets, native HMAC device authentication, session freshness, replay protection, and OS-protected credentials. The phone explicitly chooses a desktop model and new conversation. QR photo/camera decoding is local: bundled ZXing on Android and Vision on iOS. The camera uses an explicit Read code button. No arbitrary remote endpoint field exists.
- A paired desktop can explicitly grant selected saved agents. The phone can submit a bounded task, review its status/result, and cancel its own work. Every run still requires desktop approval; project paths, tools, models, budgets, grants and proposed edits cannot be approved or changed from the phone. Foreground polling runs no faster than every five seconds, stops in the background, and never automatically reruns a task. Removing a connection attempts authenticated self-revocation, then deletes the local key; offline removal warns that the remote record could not be revoked. Credential writes are serialized so an older refresh cannot restore a forgotten connection.
- Native current/total memory, low-memory, thermal and low-power state. Unsafe loads are refused; pressure/heat/backgrounding cancel work and release the native context. These guards are not physical-device thermal/battery benchmarks.

## Privacy and data boundaries

The application initiates network access for a user-selected model download and for an explicitly chosen paired desktop. Publisher links and OS share destinations open only after the user's action. Speech is delegated to the installed operating-system service with the mandatory on-device/offline flags; HOLD does not control another vendor's system-service implementation or claim a whole-device telemetry audit.

The initial expo-camera scanner dependency was found to include ML Kit, whose vendor documents usage metrics. The replacement uses ZXing/Vision. Android configuration sets `barcodeScannerEnabled:false` **and** `expo.autolinking.android.buildFromSource:["expo-camera"]`: Expo's prebuilt module otherwise retains its default scanner dependencies. [Expo documents this requirement](https://docs.expo.dev/guides/prebuilt-expo-modules/). Final exclusion must be checked against the release runtime graph and APK, not inferred from the configuration flag. See the v2 verification record for the tested artifact.

Conversation/photo storage is the private OS sandbox and device encryption, not a separate app-password vault. Android backup/cleartext traffic/Expo OTA are disabled; external-storage permissions are blocked. iOS data protection and backup exclusion are source-prepared but need Apple verification. SecureStore pairing secrets are not included in exports. Exported JSON is intentionally readable and may include photos and notes. Export share-cache files remain long enough for the receiving app; Clear chats & notes removes HOLD export/recovery caches along with chats, notes and attached photos. Downloaded models and pairing credentials have separate removal controls.

## Verification and artifacts

The original alpha APK and [baseline evidence](/reference/docs/evidence/mobile-native-verification.json) remain unchanged. Baseline documentation is preserved in [mobile-baseline-notes.md](/reference/docs/evidence/mobile-baseline-notes.md). New candidates, screenshots and native UI XML are kept separately under `artifacts/mobile/v2`; the detailed v2 report records which candidate produced each observation. Do not treat an earlier candidate's UI check as a test of identical final bytes.

The final artifact is `artifacts/mobile/v2/HOLD-0.2.0-android.apk`, 177,020,791 bytes, SHA-256 `509bfe572c83f75f78965b06a574a253555bf0ec29d937f97a74f2efe47cac63`. Android reports versionName **0.2.0**, versionCode **2**, `debuggable=false`; this is a development-signed build, not an owner-signed store release. The installed APK was pulled back from Android and matched byte-for-byte. The [v2 verification record](/reference/docs/evidence/mobile-native-verification-v2.json) separates ten earlier candidates from the current metrics and accessibility acceptance. [Source hashes](/reference/docs/evidence/mobile-build-source-hashes-v2.json) match all 89 recorded build inputs to the physical build copy.

The current TypeScript check passes and **38 mobile tests pass**: [test output](/reference/docs/evidence/mobile-tests-v2.txt), [typecheck output](/reference/docs/evidence/mobile-typecheck-v2.txt). Tests cover model integrity, lifecycle/download races, resource guards, scoped notes, context bounds, search, strict portable import/private photo paths, WAV bounds/formats, pairing/task response validation, credential write/delete races, SQLite close/reopen, and bounded optional response metrics across persistence/export/import. Node SQLite unit tests are distinct from actual Android storage acceptance.

The current APK completed **three process-cold model loads and three replies reusing the context**, all real offline Qwen2.5 CPU inference. [Individual measurements and method](/reference/docs/evidence/mobile-performance-v2.json) include model integrity/loading, first-token callback intervals, native prompt/decode counts and rate, total response time, three Android activity startup observations and 26 PSS/RSS samples. Median model loading was 1.22 s and median native generation was 8.04 tokens/s (range 2.71–12.50). The host was concurrently packaging desktop builds; these are emulator observations, not phone performance predictions. Native rate excludes setup/prompt processing; first-token intervals end at the JavaScript callback, not screen drawing. Android activity startup is also distinct from an interactive React frame. The reproducible harness is `apps/mobile/scripts/verify-emulator-performance.mjs`.

[Current accessibility acceptance](/reference/docs/evidence/mobile-accessibility-v2.json) covers both 1080×2340 at 440 dpi and 720×1600 at 360 dpi (320 dp wide), including 200% text. The narrow keyboard previously covered Send; hiding the chat header/model selector while typing and bounding the input viewport fixes that without capping font scaling. TalkBack was actually bound with touch exploration: focus was visible, Tab/Enter opened conversation actions, microphone refusal remained explicit, and dismissing it restored typing. A TalkBack-enabled pointer Send produced a real offline response. Saved response details reopened after process restart. These checks do not constitute complete screen-reader or human-audible voice certification. The narrow enlarged layout has a small scrollable transcript; a long error temporarily takes most vertical space until dismissed.

Actual Android 16/API 36 x86_64 emulator acceptance now includes: native model download/pause/resume/verification/removal/re-download; Qwen2.5 and Qwen3 offline replies/model switching; actual SmolVLM camera/photo inference and airplane-mode follow-up; persisted history/search/delete/rename; local and desktop retry/edit; notes opt-in producing Cedar; 4,096-token context reload; native export/import with photo re-decode; invalid import rejection; incoming Android share review/drafts; microphone denial; local WAV transcription with no active network; offline TTS API completion; 160% text and font-change photo-picker regression; QR/manual pairing, revocation and host-session refresh.

Later native acceptance also covers 200% text after returning from Android Settings, background interruption retaining partial native output, live system themes, and a deliberately corrupt SQLite row preserved for native recovery export. These observations remain attached to their exact candidate hashes. On the prior 68988206 APK, fresh Qwen2.5 inference with airplane mode and no active network produced `FinalCHECK`, persisted through force-stop/relaunch, and reopened from history. It did not exactly follow the requested uppercase `FINALCHECK`; functional inference passed, exact capitalization did not. Final offline readout returned to idle and the full bundled ZXing notice opened after search/keyboard dismissal.

The WAV fixture is the speech binding's public test recording (SHA-256 `16caad9ba2383de2059819a8178c56ac5c24ebc5e4221fb5f223cb57a9af834f`, 103,278 bytes). Its spoken sentence, “This is a remote resource from the internet,” was transcribed into the draft while Android reported airplane mode, no IP route and no active default network. That sentence describes the fixture's content, not a network operation. No audible synthesis listening claim is made for the headless emulator.

Native pairing cancellation retained partial output and the host reported zero active requests. That fixture generated real Qwen tokens first, then dispatched them at 500 ms intervals to make Stop testable; it is not a model-throughput or mid-generation engine-cancellation benchmark. The isolated harness automatically approved test invitations; production desktop approval was exercised separately by the desktop owner. The later QR-only fixture advertised a model but deliberately did not implement chat.

Prior 68988206 APK paired-task acceptance used actual Qwen3 4B CPU inference and the production encrypted task queue. An approved `read_file` task returned the synthetic project's codename **SILVER PINE**. Pending cancellation, desktop rejection, cancellation while running, phone restart, desktop session rotation, restored results without rerun, and phone-initiated revocation all passed. Task approval in this isolated fixture was controlled explicitly by the test operator rather than the production OS dialog. Its running-task cancellation used no artificial stream delay. Qwen3 0.6B initially requested a nonexistent example file and correctly failed; that model-quality failure remains preserved beside the successful 4B evidence.

The small vision model described the camera's synthetic house, but also produced inaccurate details and failed a wall-color question. This verifies actual image inference, not factual/visual quality. The 6 GiB emulator is not a low-end physical phone. Earlier system status-bar clipping also appeared on the launcher and disappeared after a full emulator restart; current captures are clear.

## Remaining validation and product limits

No physical phone or Mac was available. iOS native modules/configuration exist but were not compiled or run. There is no claim of physical camera QR capture, human-audible voice quality, microphone transcription from a real speaker, screen-reader certification, GPU/ANE/NNAPI/Hexagon use, sustained memory/thermal/battery benchmarks, backup security, or independent security/legal review. Live camera preview/capture and local QR photo decoding were tested; a real desktop-screen QR through a physical camera remains separate.

Arbitrary GGUF import, semantic memory, arbitrary on-phone project/tool execution, and an iOS incoming-share extension are not implemented. The phone can request and monitor explicitly approved desktop agents; agent configuration and change approval remain desktop actions. Paired chat currently supports selected text models; photos are local only. The prepared iOS path and physical-device performance/accessibility checks require suitable hardware. Store signing/publication still requires owner-controlled credentials and explicit authorization.

## Build and run

Requirements: Node 24, JDK 17, Android SDK 36/build tools 36/NDK 27.1.12297006 and CMake 3.22.1 for Android. A Mac with a current compatible Xcode and CocoaPods is required for iOS. Exact JavaScript dependencies are recorded in `package-lock.json`.

```sh
cd apps/mobile
npm ci
npm run typecheck
npm test
npm run bundle:android
npm run android
```

Android development APK, with a bundled JS runtime and no Metro server required:

```powershell
$env:JAVA_HOME = 'path-to-jdk-17'
$env:ANDROID_HOME = 'path-to-android-sdk'
$env:PATH = "$env:JAVA_HOME\bin;$env:PATH"
npx expo prebuild --platform android --no-install
npm run signing:development
.\android\gradlew.bat -p android assembleRelease --no-daemon -PreactNativeArchitectures=arm64-v8a
```

The generated release build is signed with the standard **development key** unless owner signing variables are supplied. It is not a Play Store release. `scripts/build-android.ps1` performs dependency, typecheck, test, native-generation and build steps. Use `-PreactNativeArchitectures=arm64-v8a,x86_64` for an emulator-capable combined artifact.

Development keystores are deliberately excluded from source control. `npm run signing:development`, the Windows build script, and the manual Android CI job generate a local key when a clean checkout contains an existing native project without its ignored key. Existing keys and owner-controlled release signing settings are preserved. Independently generated development keys are not interchangeable for in-place updates; keep the same local key when testing updates.

Windows native C++ tooling requires a short **physical** source path, such as `C:\src\hold`; a drive alias alone does not fix Expo's canonical dependency paths. The original long project-mirror location triggered Ninja's 260-character limit. The validation build uses an isolated physical copy at `C:\hold-mobile-01a13` and a short alias for SDK/cache locations, with originals preserved. To bound native compiler memory on a busy machine, pass `--max-workers=2 -Dorg.gradle.parallel=false --init-script <absolute-path-to-scripts/native-memory.gradle>`; limiting Gradle workers alone does not limit Ninja's C++ jobs.

On macOS:

```sh
cd apps/mobile
npm ci
npx expo prebuild --platform ios
npm run ios
# unsigned simulator build:
xcodebuild -workspace ios/HOLD.xcworkspace -scheme HOLD -configuration Release -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO
```

Expo deliberately refuses to generate iOS native files on Windows. `app.json`, the native Swift module and the macOS CI job define the generation path; no iOS build success is claimed. `.github/workflows/mobile.yml` is a manual workflow that builds an Android release APK and an unsigned iOS simulator app, then uploads artifacts. Android upload is gated by the resolved dependency, DEX and manifest scanner exclusion check. It does not publish to either store and has not been run on GitHub. EAS profiles are optional; local native builds do not require an Expo account or EAS subscription.

## Owner release setup

Android: create and securely back up an upload keystore with `keytool -genkeypair`; use a unique owner-controlled alias and strong passwords. Set `HOLD_UPLOAD_KEYSTORE` to its absolute path and the three corresponding password/alias environment variables in `.env.example`. Do not commit the key or passwords. The release-signing config plugin reads them during Gradle build. Verify with Android SDK `apksigner verify --verbose --print-certs`. Generate a Play upload bundle with `bundleRelease`. Store publication needs an owner's Play Console account and explicit release approval; neither blocks local builds.

iOS: on an authorized Mac run the generation steps, open `ios/HOLD.xcworkspace`, select the owner's Apple development team in Signing & Capabilities and use the desired bundle identifier (default `software.elpmis.hold`). A physical device can then be selected and tested. Distribution requires the owner's Apple Developer enrollment, provisioning and App Store Connect configuration. Archive in Xcode, validate and submit only after explicit owner approval. Store fees are not estimated here because current account-specific costs have not been verified. No accounts or secrets were created.


## Dependency evidence

Both full and production-declared npm audits currently report 15 high entries propagated through Expo/Metro tooling from braces and node-forge advisories. Raw outputs are [production](/reference/docs/evidence/mobile-audit-production-v2.json) and [full](/reference/docs/evidence/mobile-audit-full-v2.json). The [registry/advisory check](/reference/docs/evidence/mobile-audit-remediation-v2.json) found no compatible published patched version on 2026-10-15. No breaking Expo downgrade was applied to silence the audit. Production-declared dependencies include build tools; this flag does not describe the shipped Hermes bundle. The [final bundle-scope check](/reference/docs/evidence/mobile-audit-runtime-scope-v2.json) matches the APK's Hermes bytes to the native build and finds no braces/node-forge source paths among its 713 mapped modules. This narrows known exposure; it does not certify native dependencies or the absence of other vulnerabilities.

Offline license search/detail views use `assets/THIRD_PARTY_LICENSES.json`, bundled in the final APK with 673 entries: 499 npm, six llama.cpp components, 144 Android AAR/JAR entries and 24 static native notices. Readable source notices and provenance are [mobile-third-party-notices.txt](/reference/docs/evidence/mobile-third-party-notices.txt) and [mobile-license-inventory.json](/reference/docs/evidence/mobile-license-inventory.json). Android inventory and embedded notices are [mobile-native-license-inventory.json](/reference/docs/evidence/mobile-native-license-inventory.json) and [mobile-native-embedded-notices.txt](/reference/docs/evidence/mobile-native-embedded-notices.txt), supplemented by [upstream notices](/reference/docs/evidence/mobile-native-upstream-notices.json) and [static native notices](/reference/docs/evidence/mobile-native-static-notices.json). Final scanner exclusion passed for the exact delivered APK in [mobile-scanner-exclusion.json](/reference/docs/evidence/mobile-scanner-exclusion.json). Notice collection is not owner legal approval; a final iOS native-transitive audit remains separate.
# Chat model chooser — 2026-10-19 source update

The chat model bar now displays the model's capability and name and opens a downloaded-model sheet. Selection retains the conversation and draft, releases the previous allocation and persists the chosen model for the next response. Photo drafts/history restrict choices to photo-understanding models. Helper and paired-desktop chats retain their own model selection. Catalog IDs and immutable download pins were preserved.

`npm run typecheck` and the existing 50 mobile unit tests pass for this source update. No new native Android/iOS build, emulator run or physical-device test was performed, and these checks do not certify phone behavior. Native mobile image/video creation is not implemented or advertised. Desktop experimental generation and evidence are documented in [LOCAL_MEDIA_GENERATION.md](/reference/docs/LOCAL_MEDIA_GENERATION.md).

2026-10-20 UI simplification: the default chat bar now says Chat or Photos, without model names. The manual chooser is explicitly titled Advanced options. Native inference, identifiers, downloads and saved overrides are unchanged. Mobile TypeScript and all 50 unit checks passed; physical-device verification remains outstanding.
