Changelog
Every released version of the React Native SDK, what changed, and whether it asks anything of you.
The published package is @graineai/inapp-react-native.
Nothing here needs a native rebuild. The audio path lives in a WebView pointed
at a hosted page, so an SDK upgrade is npm install and an over-the-air
update — which is the whole reason it is built that way.
Upgrading from anything older than 0.27.4
Go straight to the latest. Two releases in that range were never published —
their fixes are folded into the ones after them — and 0.27.0 is broken.
0.31.0 — the engine warms itself, and a call cannot outlive its UI
Three fixes that only show up on a real device.
The agent starts in about a second, not several. Mounting the launcher
early was always meant to do the slow work before the customer taps: mint a
credential, load the SIP stack, open the socket, register. The page has done
all of that behind a warm signal from the start — and the only thing that
ever sent it was a host without autoStart opening its bar, by which point
there is nothing left to save. With autoStart, the common integration, it was
never sent at all and every open paid mint, register and invite in series in
front of the customer. An idle page now warms the moment it reports ready, and
re-warms itself before the credential expires. Nothing to change in your code.
A call can no longer outlive the thing showing it. Every teardown — the bar
closing, the app backgrounding, the launcher unmounting — reaches the page by
injecting JavaScript into the WebView, which is exactly the thing that may
already be gone: destroyed on unmount, reclaimed under memory pressure, or
reloaded. The message went with it and the call survived, still listening and
still billing, with nothing on screen. The page now hangs up on pagehide,
which needs nobody's cooperation. A backgrounded app is still the host's call
to make, deliberately: on iOS, hidden also means the app switcher and a
permission dialog.
A component drawn while the panel was closed is not lost
useGraineWidget now starts from whatever the agent has already drawn, and
the client holds it until it is answered.
The event stream has no replay, and your widget UI almost certainly lives somewhere that unmounts — a panel, a sheet, a bar that closes. So a component the agent drew while that was closed reached nobody and was gone: the customer opened the panel to nothing, and the agent waited for an answer to a card that had never been on screen. Nothing in the logs said so, because every layer had done its job.
Nothing to change in your code. The hook seeds itself on mount.
A card is forgotten the moment it is submitted or dismissed, and when a call
starts or ends — so nothing replays something the customer already dealt with,
and last call's card never opens on top of this one. client.pendingWidget and
client.clearPendingWidget() are there if your host needs to drive it
directly, for example to open the panel when one arrives.
0.30.0 — pressing Next with blanks is a moment, and pictures beat a list read aloud
The agent speaks up when someone moves on with something missing. A new
friction rule, incompleteSubmit, watches for a tap whose label means "carry
on" while a field on the screen is still blank or rejected. The agent names
what is missing before the screen throws an error at them.
The default list is submit, next, continue, save, proceed, apply, confirm, done, pay, verify, matched as lowercase substrings — so submit also catches
submit_application. It is set in the dashboard for every app running the
agent, and an app may override it for its own screens. Nothing about it is
written in the prompt or in your app: the fields you already report are what it
counts. Friction rules →
Product lists render in the app. A new component kind, product_list,
carries a picture, a title, up to three facts and a price per row, and a button
on each one. The agent picks the moment; the customer taps a row and the choice
goes back as both a sentence and structured data.
Build it in Embed & Widgets, once, and it reaches a website embed and a native app the same way — the SDK relays whatever kind the dashboard sends. A new one starts filled in, with sample rows and sample pictures.
A root-relative imageUrl now resolves on a device. /samples/car-blue.png
is an ordinary image on a web page and a URI with no host on a phone, where it
draws an empty box and says nothing. It is resolved against the agent's own
origin on the way in, so what you author is what the app shows.
Showing things on screen →
Components are chosen per agent. The library stays shared, but every enabled component used to be registered as a callable tool on every agent in the workspace — so a car catalogue was a tool on a loan agent. Pick what each one may show under Embed & Widgets → On screen. Picking nothing still means everything, so no existing agent changes behaviour.
optional on a field. An empty field was always counted as work left, so
a screen with an optional middle name told the agent two things were missing
and the agent offered to fill one. Mark it and it stays in the frame — the
agent can still fill it if asked — but out of the count, out of the new
friction rule, and marked "(optional)" on its own line. A field carrying an
actual error is outstanding either way.
0.29.0 — the agent knows what comes next
journey.next on useGraineScreen. step and of say how far along someone
is; asked "what happens after this?", an agent with only a step number invents
an answer — and on a setup flow an invented step is one the customer then goes
looking for.
The runtime also now states what is outstanding — 2 of 4 still need attention: PAN number, Date of birth — counted from the fields you already
report, so the agent answers "am I done?" without tallying a list.
What the agent is actually told →
0.28.1 — an empty line is a full stop, not a line
The engine closes a turn by sending it back with empty text. 0.28.0 folded that as text, so a caption went blank a few seconds after it appeared. It settles the open turn now.
0.28.0 — the transcript belongs to the SDK
The CC button works between turns. The launcher used to hand out one caption
at a time through onCaption and keep nothing, so every host that wanted a CC
button accumulated the transcript itself — and all of them had the same hole:
turning captions on mid-call showed an empty box, because the only line that
existed was the one being said at that moment.
api.captions— every turn, oldest first, capped atCAPTION_HISTORY(12)api.caption— the turn being said now, or the last oneapi.captionsOn/setCaptionsOn— the switch, defaulting onclearCaptions()— forget the transcript without ending the call
A turn arrives many times as its words come in, then once settled. Those fold
into one entry, so the list holds turns rather than keystrokes. It is cleared
when a call starts and when you set active={false} — a call's words outliving
its call is a privacy question, not a feature.
Also: activating with autoStart now reports connecting. It did not, so a
host status line read "tap to talk" for the whole second a call was being
placed, inviting a second tap on a call already on its way.
onCaption is unchanged and still fires for every line.
Captions and hanging up →
0.27.6 — not published
Folded into 0.28.0. Deactivating (active={false}) now posts the stop frame
unconditionally, and clears connected / connecting.
It had been guarded on the launcher's own belief about whether a call was running — and those flags are cleared by the page's own frame, so any ordering where that landed first closed the UI and left the call up: still listening, still billing, with nothing on screen to stop it. And because nothing else cleared the call state, a bar reopened after such a close came back reading "listening" for a call that had ended.
0.27.5 — mount the engine before the customer needs it
GraineVoiceLauncher gains active (default true, so nothing existing
changes). Mount it early with active={false} and the page loads without
registering or starting anything; flip it to true when your UI opens.
Five seconds of "Connecting…" measured on the reference app: opening the bar created the WebView, loaded the page, minted a credential, registered on the SBC and only then placed the call — all in series, all in front of the customer. How long connecting takes →
0.27.4 — tap capture reaches an app's own root
GraineTouchCapture and GraineFab are exported from the package entry.
GraineProvider mounts capture around its children, so an app that mounts
the provider deep — around a call bar — saw taps on the bar and none on its
screens. Mount it at your root instead; it needs no provider above it.
Screens outside the provider →
Includes the 0.27.3 fix below.
0.27.3 — not published
Folded into 0.27.4. An empty availableActions disarmed the agent. The SDK
filtered the declared catalogue through its handler registry and sent []
whenever that registry was momentarily empty — and [] on the wire means
nothing is callable here, so the agent silently lost every tool for that
screen. It now sends the key only when a screen states one.
0.27.2 — connecting took ten seconds
The engine mounted on the tap, so the page load and the SIP registration were
both paid in front of the customer. GraineFab mounts it on open and keeps it
warm; the tap costs a microphone prompt and an INVITE.
0.27.1 — the FAB overlay is a prop
0.27.0 imported react-native-screens for the FAB's iOS overlay — a native
module most apps do not declare, which breaks expo export and cannot ship
over the air. The overlay is a prop now, so an app that wants it passes it and
an app that does not is unaffected.
Do not install 0.27.0
It cannot be delivered over the air. Use 0.27.1 or later.
0.27.0 — the provider mounts the FAB, and taps are tracked
fab on GraineProvider, captureTaps, and useGraineInit(). Broken —
see 0.27.1.
0.26.2 — a screen change no longer wipes the screen
useGraineScreen put a screen-less frame on the wire on every dependency
change, not just on unmount — so editing a field, or any re-render with a new
object identity, told the agent there was no screen at all. Reporting and
withdrawal are separate effects now: report on change, withdraw only on
unmount. Fast navigation →
0.26.1 — the launcher re-pushes context on foreground
A phone that slept came back with the agent holding a screen the customer had left.
Earlier
0.23.0 – 0.26.0 — the first published SDK, the screen-context contract,
friction detection, and in-app actions. Start here →

