Architecture
How the native container, the typed bridge, and native session ownership work.
The SDK is a native container around the Loops web runtime loaded in mode=sdk.
The web owns the conversation UI; native owns the session, routing, and analytics.
Understanding this helps you reason about callbacks and security.
The container
LoopsAIChatFragment
└─ WebView → chat.loopsai.com/<agentId>?mode=sdk&platform=android…
↕ loopsAIBridge (typed message bridge)LoopsAIChatFragment is the single host; the Activity, Compose, and convenience
entry points all wrap it.
The bridge
Native and web talk over a typed message envelope:
{ "protocolVersion": 1, "type": "nativeAction", "name": "productQuoteChanged",
"requestId": "req_123", "payload": { /* … */ } }- Web → native actions drive the host callbacks (ready, message, responding, product quote, conversation active, analytics passthrough, open URL, close).
- Native → web actions are the methods you call (context updates, search, try-on, size). See Flow modes.
- Unknown action names are ignored, and the legacy flat
{ action }shape is rejected.protocolVersionlets a shipped binary evolve without breaking older app versions.
The transport prefers WebViewCompat.addWebMessageListener (which enforces the
origin allowlist) and falls back to addJavascriptInterface only when needed.
Native session ownership
The system WebView can wipe partitioned storage, which would lose the anonymous session. The SDK fixes this natively:
- It bootstraps the session against the runtime and persists the anon session in
app-private
SharedPreferences(com.loopsai.chat.session). - It re-injects the session (
_lsuid/_lscid) on every WebView load, and appliespersistSessionupdates the runtime emits. - Bootstrap retries transient failures (transport errors and 5xx only) with exponential backoff.
The result: the session survives app relaunch with no extra work on your side. The
anon id is pseudonymous — not a credential — so it lives in app-private storage,
not EncryptedSharedPreferences.
Security
- Origin allowlist — only
chat.loopsai.comand your customenvironmenthost can drive the bridge; navigation to other origins opens in the system browser instead. - Foreign links open in the system browser, never inside the WebView.
- Camera capture is granted to the WebView only for allowlisted origins, and only when the host app holds the matching permission.
- WebView debugging is enabled only in debug builds.
Authenticated (non-anonymous) sessions via a token provider are on the roadmap; v1 is anonymous-session based. Today the bridge accepts a token-refresh request but it is a no-op until the auth provider ships.
Host callbacks reference
| Callback | Fires when |
|---|---|
onReady | The runtime is live; safe to push context. |
onResponding | The assistant starts/stops responding. |
onProductQuoteChanged | The active product quote changes (null = cleared). |
onAnalyticsEvent | A canonical analytics event crossed the bridge. |
onConversationActive | A conversation becomes active. |
onMessageEvent | A message / new-chat / overlay event. |
onCloseRequested | The runtime asked to close (default: finish/pop). |
onOpenUrlRequested | A foreign link should open externally. |
onError | A load / bridge / session error occurred. |