Skip to main content
praisonai/mobile is a purpose-built entry point that bundles only the webview-safe surface of the SDK, so phones and browsers pull ~77 kB instead of 2.97 MB.

Quick Start

1

Import the phone-safe Agent

The same Agent, from the webview-safe entry.
Never ship a raw API key into a phone or webview build. Route requests through your own backend or an ephemeral-token endpoint. This example assumes apiKey was fetched from a trusted source at runtime.

Why a Separate Entry

The root praisonai entry re-exports the CLI, MCP server, tool registry, and knowledge store — a bundler cannot tell you don’t call them, so it follows every re-export and dies on a Node builtin.

What’s Exported

The mobile entry is an allowlist matching src/mobile.ts — everything here is verified loadable in a webview. Not exported (and why): CLI (spawns processes), MCP server (opens sockets), tool registry (reads the filesystem), knowledge store (reads/writes files). None of them can run in a webview, so importing them from a phone build was always a build-time crash waiting to happen.

Bundle-Size and Parse Floor

The mobile entry bundles well under the 400 kB target for a phone build, and its lazy-chunk footprint sits under the 1000 kB budget with a deliberately tight 87.4 kB margin.
The 1000 kB budget was re-derived from the measured 912.6 kB bundle after the #4874 and #4882 reclaims — see PR #4903 for the floor/lid derivation.

Why the lazy budget matters

Any static import from a leaf on Agent’s graph pulls into the initial parse. A literal import('@ai-sdk/…') — even in a code path the webview never reaches — is a bundler instruction, so esbuild ships the package as a chunk regardless. PraisonAI PR #4874 removed 326.7 kB of @ai-sdk/* provider packages (which dragged in ~678 kB, mostly zod) by moving auth to a leaf module and routing embeddings through a computed specifier. The regression test in src/praisonai-mobile/tools/bundle.test.mjs now enforces this: no @ai-sdk/* provider package may appear in the mobile bundle, aside from ai’s own internals (@ai-sdk/provider, @ai-sdk/provider-utils, @ai-sdk/gateway).
Verify these numbers against a fresh npm run build && node src/praisonai-mobile/tools/bundle.mjs before relying on them — they are ground truth as of the #4874 merge but can drift as other PRs land.
The chrome58 floor is the WebView Android’s minSdkVersion: 26 (Android 8.0) ships with. scripts/webview-gate.mjs runs the esbuild build at that target on every CI run; if a maintainer adds a top-level await — directly, or by adding a require() that earns the esm-shim createRequire banner — the build fails with esbuild’s own file:line diagnosis. Anything higher than chrome58 is fine. The entry is also verified on every CI run for Node builtins by scripts/webview-gate.mjs; if a maintainer adds an import that pulls a Node builtin into either entry, the build fails.

Webview-Safe UUIDs

randomUUID from praisonai/mobile replaces import { randomUUID } from 'crypto', a static Node builtin import that kills a webview bundle at load.
It calls globalThis.crypto.randomUUID where available (every supported webview and Node ≥ 19), falls back to getRandomValues, and as a last resort uses Math.random — those ids are run and message identifiers, not secrets.

Adding an Import to the Mobile Entry

For contributors: an export belongs in the mobile entry only if it can run in a webview. If a piece of the SDK can run in a webview and would be useful on mobile, add it to src/mobile.ts and to the WEBVIEW_ENTRIES list in scripts/webview-gate.mjs. If it cannot run in a webview, it does not belong there — even if it would be convenient. A consumer reaching for something absent gets a clear build-time resolution error, which is a far better outcome than a blank screen on a device at import time.

Framework Examples

The same 5-line Agent example, with the runtime-appropriate way to source apiKey.

Best Practices

A key shipped in a phone or webview build is extractable by anyone with the app. Fetch an ephemeral token from your own backend at runtime and pass it as apiKey.
AgentEvent, AgentStreamOptions, SimpleAgentConfig, and StopReason are all re-exported from praisonai/mobile. Import them from there so a mobile build never reaches into the root entry by accident.
import { randomUUID } from 'crypto' is a static Node builtin import that kills a webview bundle at load. Use randomUUID from praisonai/mobile instead.
If you add an export to src/mobile.ts, add its entry to WEBVIEW_ENTRIES in scripts/webview-gate.mjs so CI keeps verifying it stays webview-safe.
esbuild refuses to lower top-level await for chrome58. This includes indirect top-level await — a require() in one of the files on the mobile import graph causes scripts/esm-shim.js to prepend a createRequire banner that IS a top-level await. If you need a runtime lookup (e.g. reading a package’s version), use await import(pkg, { with: { type: 'json' } }) inside a function, not a bare require(pkg) at module scope.
A literal specifier like import('@ai-sdk/openai') is a bundler instruction, not just a runtime one — esbuild emits the package as a chunk even if no reachable code path calls it, and a webview cannot resolve it anyway. Reach provider packages through createAISDKProvider(providerInfo.package, …), a computed specifier the bundler cannot follow, which is how every chat provider already works. src/praisonai-mobile/tools/bundle.test.mjs fails the build if any @ai-sdk/* provider package (openai/google/cohere/mistral/groq/…) ships in the mobile bundle.

Stream Events

Structured tool events from streamEvents()

TypeScript SDK

The full TypeScript framework

Streaming

Stream text token-by-token

Agent

Full agent configuration