embed_flutter 0.4.0
embed_flutter: ^0.4.0 copied to clipboard
A powerful Flutter SDK that provides a powerful voice-enabled AI-agent functionality with real-time widget tree monitoring for screen context fetching and realtime voice communication.
Changelog #
0.4.0 #
React Native parity, the click-naming convention shared with Android, RN and
web, call-safety fixes, and an SDK that handles its own Android permissions.
Minor version bump: click event names change, the signed-in user is kept
until clearUserSession(), and the package is now a Flutter plugin (Android
native part, iOS Dart-only) — hosts add no permissions.
Call safety #
- A reconnect no longer turns the microphone back on. LiveKit fires "room connected" again after a full reconnect (for example a switch between Wi-Fi and mobile data). That was handled as a new call: a mic the user muted — or that an agent pause muted — was re-enabled while the UI still showed it muted, the AI runtime restarted (dropping a running mission), "call started" was sent twice and a workflow avatar reopened. A repeat now only restores the mic as it was, re-routes audio, re-asserts a pause and re-sends the screen (React Native parity).
- Headsets keep call audio. The SDK no longer forces the loudspeaker: a
connected Bluetooth or wired headset is used, otherwise the user's speaker
choice, and a device change no longer switches the speaker back on. Before
livekit_client2.9, forcing the speaker on Android routed audio away from a connected headset. - The SDK handles Android permissions itself; hosts add nothing. It now
ships a small Android plugin whose manifest declares every permission a call
needs (
INTERNET,ACCESS_NETWORK_STATE,RECORD_AUDIO,MODIFY_AUDIO_SETTINGS,WAKE_LOCK,BLUETOOTH_CONNECT, andBLUETOOTHup to Android 11), merged into the host app at build time. At call start it requestsBLUETOOTH_CONNECT(Android 12+) itself, only when a Bluetooth headset is connected; a denial never blocks the call (React Native parity). The README no longer lists the non-existentandroid.permission.MICROPHONE. - No hidden live calls. Leaving the screen (or the widget hiding) while a call is still connecting now cancels it. It used to finish connecting and go live with the mic on and no End button.
- A failed token no longer hides the widget. A token failure when the user taps Start now shows "Couldn't connect — tap to retry" instead of removing the widget. A failed pre-call prep (a network blip at sign-in) is retried with backoff (2 s → 30 s, up to 5 times) instead of waiting for the next screen change.
Behaviour changes #
- Click analytics now use the naming convention shared with Android 1.2.3,
React Native 1.2.0 and web.
event_nameis<name>_<suffix>(pressed/toggled/selected/opened). It no longer ends in_<screen>; the screen stays incontext.screen.- The name is the control's
ValueKey<String>, itsSemantics(identifier:), then its tooltip or visible text (slugged), then — for a control with no text — the image / SVG file name or common icon it draws (React Native's rule), then its role. A control with no key is no longer named by a randomhashCode. datacarries Android's keys:element_id, type, kind, role, checked, source, click_sequence, component, legacy_event_name. The Flutter-onlybuttonId,formState,clickedAtandlastClickedAtare gone. When a keyed Material button had a pre-0.4.0 name, it is sent aslegacy_event_nameso old dashboards can be mapped.
- Every tappable control is tracked, not only
ElevatedButton,TextButton,OutlinedButton,FloatingActionButtonandIconButton: the tapped control is found in the semantics tree, soGestureDetector,InkWell,ListTile, checkboxes, switches, radios and tabs report too. Checkbox / switch / radio clicks carry their state after the tap. - No more lost clicks. One 500 ms debounce timer used to be shared by all buttons, so tapping two buttons quickly dropped the first. Now only the same name repeated within 300 ms is dropped. Drags beyond touch slop are not clicks.
- Clicks need a user. A click before
USER_DATAsetsapp_user_idis not sent (React Native parity). EmbedTracker.attachButtonandEmbedTracker.onButtonClickedare deprecated no-ops.
React Native parity: agent, identity and events #
- Touching the app stops the agent. A user touch while the agent runs a
mission or highlight (running or queued) ends it with
mission_statuscancelled,failure_reason: user_interrupted; the highlight box and glow are removed, and auser_interactionframe is sent (at most one per second). Touches on the SDK's own widget and the agent's own taps never count. - The signed-in user is kept across app restarts until a logout
(
EmbedWidget.clearUserSession()orembedClearStorageCache()) — 0.2.0 and 0.3.0 forgot it at every launch. Token fetch, route catalog, UI-graph sync and events work from cold boot;USER_DATAstill sets or replaces the user. Hosts must callEmbedWidget.clearUserSession()on logout. EmbedWidget.clearUserSession()is now a full logout (RNEmbedLogout): it ends the call, wipes the on-device UI-graph cache and typed form values, then forgets the user in memory and on disk — also with no widget mounted. Config and API key are kept. Each step is isolated, here and inembedClearStorageCache().EmbedWidget.initializeLiveKit()is idempotent: it does nothing during a call (re-preparing used to orphan the live call) or when already prepared.- Events go out whenever a user is signed in, whatever the widget is doing
(hidden, delayed, excluded route, config still loading, not mounted). With no
user they are not sent and
embedEventreturns false (as in RN); a blankUSER_DATAid and an empty analyticsevent_nameare rejected.SCREEN_STATEis no longer dropped before config loads. - Failed analytics are retried one per event, oldest first, from a
persisted queue (at most 100) — identical events are no longer merged. The
queue drains on resume, config load and a signed-in
USER_DATA./user-context/updatehas a 15 s timeout and never sends without anapp_user_id.event_id/session_idare UUID v4 (they were millisecond stamps that could collide). embedOnAgentreceives every React Native agent event:agent_visible,agent_tap_to_open,agent_tap_to_close,gen_tool_triggered,popup_message_visible, the mic permission events and the conversation start/end — listeners first, even beforeUSER_DATA, then the backend.rage_clickandscreen_vieware analytics only (observe them withembedOn), as in RN. A throwing listener is logged and skipped; a listener may trigger SDK events. Popup and mic payloads carry RN's fields.embedCollapseWidget()reportsagent_tap_to_close.agent_visiblefires when the FAB really shows (it also fired while hidden). Every connected call reportsagent_conversation_endedexactly once.- Deprecated:
EventHelper.flushPendingEvents()(useflushAnalyticsEventQueue()),EventHelper.clearPendingEvents()andPendingEventSender— nothing is held for widget state any more.
React Native parity: widget, accessibility, privacy, networking #
- Widget config.
colorPalette.gradientColors(2+ stops), the 17 namedgradientPresets, a lonegrad1/grad2(RN's darken / accent rules),fillTypehorizontal/verticalgradient direction andsolid/flat/colorfills,position.leftPadding/topPadding(vertical insets mirror; negatives clamp to 0; the drag stays inside the insets),collapsedView.viewCloseDelay(ms) and snake_case keys / numeric strings everywhere.borderRadiusrounds the open card only (default 20); the collapsed FAB stays round. An unknownwidgetPositionis bottom-right. - Dashboard widget changes show on the next launch, not the one after.
At launch the widget draws the config saved by the previous launch while
/initializeis in flight; it was only refreshed whenother_datachanged, so a palette, direction, position or text edit appeared one launch late. A changedwidget_confignow refreshes it too, silently — no loading flash, and an open card stays open. colorPalette.borderColorborders the Start call button, 1 px, and only when set (AndroidExpandedCard, RN). It used to border the open card, which now has no border; the card no longer defaults to a white 20 % edge.- Call status line: rotating phrases per state,
.....while muted (with a 2 s "Speaking" hold), "Connecting..." and "Couldn't connect — tap to retry". - Captions grow and scroll instead of being cut at 3 lines, following the newest words unless the user scrolls back.
- Accessibility: screen-reader labels on the FAB ("Voice assistant", expanded state), Start call, Mute / Unmute microphone, Show / Hide captions, End call and Close; the nudge and captions are live regions.
pauseAgent()/resumeAgent()/isAgentPaused(andembed…helpers): pause the agent without ending the call (backendagent_controlRPC), muting the microphone; re-asserted after a reconnect.- Privacy:
Semantics(identifier: 'embed-redact…')hides a node's content — and everything inside it — from capture, cache and click names; the agent can still act on it. - Networking:
/initializeand/tokentime out (15 s; token 6 s + one 8 s retry on a network failure); a failed/initializeis retried with backoff (2 s → 30 s) unless refused with a 4xx; every request sendsX-Revrag-App-Version. Events carry top-levelapp_versionanddata.screen_name. - The tooltip is a diagonal gradient and the CC icon takes the palette text colour.
0.3.0 #
Tree-capture hardening, Android/backend wire parity, and multi-step mission parity. Verified against the revrag-core wire contract. Minor version bump: capture semantics, defaults and stable IDs change (see Behaviour changes).
Requires Flutter >= 3.29 / Dart >= 3.7 (SemanticsData.role).
Behaviour changes #
- Missions no longer depend on
dataCapture, widget visibility or the route allowlist. Mission captures and responses (find / verify / info queries / candidates) are blocked only by explicit host exclusions (disabledRoutes, an open excluded popup), as on Android.flags.dataCapture: falsestops telemetry pushes only. A refused mission response is replaced by a failure frame (screen_not_capturable/payload_too_large) instead of being dropped. transmitOnHiddenWidgetdefaults to on (Android parity). Only an explicitfalseunderflagsortreePushturns it off.- Named popup routes in
disabledRoutesblock capture while open, but no longer hide the FAB (popups don't change the page route). Their route-change analytics are restored. - Screen names prefer the route name (or host-provided name) over widget
inference; tabs of an
IndexedStack/PageViewshell are namedroute/tabfrom stable signals (ValueKey<String>,Semanticsidentifier, heading). Class names are never used in release builds, so--obfuscateno longer scrambles screen names, stable-ID prefixes or cache keys. - Stable IDs changed (number-led label words no longer enter IDs and
collisions get content-derived suffixes; tappable images are buttons;
full-screen labelled wrappers are groups; overlay nodes live under
screen.overlay.*; role and screen-name changes below). The on-device cache format is bumped toschemaVersion: 2; older screen files are discarded locally and never uploaded. - The stale-node prune is new: a node is dropped only after it has been absent from eligible changing captures spanning 14 days; a quiet stretch, an open overlay or a partial capture never counts as absence.
- Cache uploads no longer include
_firstSeen/_lastSeen/_seenCountbookkeeping ortokens.
Wire parity #
- Nodes carry
disabled(measured),placeholder, sliderrange(when the host provides min/max),parent_id, and thesetTextaction on editable fields. Roles use Android's vocabulary (scrollable,switch,radio,navigation,modal,overlay,listitem); raw Flutter role names no longer leak. ui_diffkeeps its delta shape:movedentries now carrypath(the key the backend reads),updatedentries carrysubtree_text,normalized_text,layer,editable,secure,parent_id,visible_fraction, and fraction-only visibility changes are reported.- A node is
visibleonly when at least 5% of it is on screen. - Snapshot frames carry
app_user_id,app_versionandsdk. verification_resultcarries the achieved slidervalueandstrategy; slider failure codes match the backend/Android (invalid_params,value_out_of_range,range_unknown,set_slider_not_applied).- Every terminal
mission_statuscarriesstep_index(onsucceeded, the last step), so the agent can say which chained step failed. continue_on_failuremissions emitstep_failedand keep going.- New
flags.nativeBridgegates the synthetic-pointer tap fallback.
Multi-step missions #
- Cancels and newer missions take effect immediately (waits, retry delays and
attempts race the cancel signal); a pre-cancelled mission no longer cancels
the running one; a cancelled step emits only
cancelled. - A blind step right after a settle wait polls up to 2 s for a slow next screen before failing.
- Malformed steps fail
invalid_mission; unexpected errors failexception; terminal statuses are never evicted from the outbound queue. - Standalone highlights (guided walkthrough) run as internal
hl_missions: full find ladder, scroll into view withalign, re-find before drawing;duration_ms: 0stays until cleared. Highlights yield while a mission runs.
Execution safety #
- Taps: the synthetic pointer needs >=50% visibility and a hit test that reaches the target; a tap with no visible effect is never retried (no double side effects); agent taps are not counted as user clicks.
set_texton a non-editable target writes only to a field BOUND to the matched label (or takes the slider path), with no focus-tap side effect; clears are verified.- A measured-disabled target fails fast with
target_disabled; the enabled twin wins ties. backuses the host hook / Navigator first and restores a custom tab only when nothing can pop. Retries reuse the node found on the first attempt.- Text-match promotion never climbs past a control;
find_candidateshonours scope, role and forbid filters; scrolling prefers the topmost layer's vertical scroller, skips pagers, and waits for pagination. - The stable-ID rung cross-checks text, so a shifted ID cannot tap the wrong sibling ("3 Months" vs "6 Months").
Result accuracy #
A mission is reported succeeded only when the INTENDED target was acted on
and verification supports it; otherwise it is failed with a specific reason
(the terminal vocabulary is unchanged).
set_textnever types into a field whose own label/hint names something else ("PIN" for "Full Name") and never redirects to merely the nearest field: the field must sit in the label's own container, directly below / right of it with nothing labelled in between, or be the one field whose label agrees. Otherwisetarget_mismatch, nothing typed. Obscured fields are skipped unless the request asks for a PIN / password.- Side-effect verbs (set_text, click / tap / navigate, check, select,
set_slider) match whole words only ("PIN" ≠ "Pincode"), refuse an
ambiguous match (
ambiguous_target, candidates still sent ondisambiguating) and refuse a qualified label for a write ("Name" → "Nominee Name"). Read-only verbs keep the lenient ladder. - Taps: a tappable twin must have the same text ("Name" never taps "Change
Name"); a container's only button is tapped only when it agrees with the
request (
target_mismatchotherwise). A stable id now naming a different number ("12 Months" for "6 Months") is not acted on. A retry re-finding by rectangle must agree on the label. - Verification is target-scoped: a tap needs a route change, an overlay
opening / closing, or a change on the target, its option group, its own
subtree or its bounded container. Changes elsewhere on the page — and
anything that was already changing before the tap (a baseline capture
~200 ms earlier) — no longer count.
navigateneeds a change of place; optionalparams.expect_screenmust match. set_textverifies the requested field's VALUE (never its label), which must have changed unless it already held exactly the text (set_text_already_satisfied); formatter tolerance (case, spaces, separators, masks) is kept.- When it cannot look, the SDK says so:
verification_unavailable(one retry for idempotent verbs, none after a tap / back),check_not_applied,select_not_applied,back_had_no_observable_effect. A tap after which something changed elsewhere on screen (not on the target, and not a self-updating clock or countdown) is reportedverification_unavailablewith evidenceunattributed_change— never success, and never "nothing happened". - Second look, observation only. When a dispatched tap / navigate / back cannot be confirmed, the SDK re-captures once ~1.5 s later and re-runs the verifier — a late, network-driven update becomes a verified success. The action is never dispatched a second time; still unconfirmed → the original failure is reported.
read_fieldon an empty field failsfield_has_no_textinstead of answering with the label.- Additive wire keys:
verification_resultgainsrequested_stable_id,requested_text(label only, ≤60 chars, never for obscured fields),acted_stable_id,acted_role,redirected,match_source,verified,value_matched(a boolean, never the value); terminalmission_statusgainssteps_completed,steps_total,steps_skipped,steps_failedand mirrors the identity keys on failure. Inboundset_textvalues are redacted in the debug wire log. - More conservative on device: taps whose only visible effect is elsewhere
on the screen (a counter, a badge, a status line outside the tapped card)
now report
tap_had_no_observable_effectinstead of success.
Icon-only controls and custom tab bars #
- Small icon-only tappables are named from what they draw: an image's asset
file name (
ic_home_active.png→home), a Material / Cupertino icon's literal name (Icons.pie_chart→pie chart), or — for a tab — "Tab i of n". Large wrappers are never named; a real label is never overridden. - Custom bottom-navigation rows are reported as
tab(excluding Material button bars, grids and keypads) and kept first under the 15 KB budget. - Custom tab rows report
selectedwhen exactly one item is drawn differently, else from the tab the session saw lead to the current screen. backafter a custom-tab switch re-selects the previous tab.
Fixes #
- A modal no longer hides on-screen UI outside it; an inline
Dialogno longer turns its page into an overlay; sheet scrims attach to their sheet. - Effect durations of 0 no longer crash the highlight / border / awakening animations (clamped to 1 ms).
mission_explorationand mission responses are trimmed to fit the 15 KB transport cap instead of being dropped.- Cache: on-screen nodes are never capped away; over-cap overlays no longer re-upload on every capture; a 200 with zero rows written is retried; a file the backend keeps rejecting is quarantined instead of blocking sync; sync enables as soon as the gate and API key allow it.
- Lifecycle flushes send a diff, not a full baseline; the active border is dismissed on stop.
- Release builds: tracker IDs no longer collapse to
Instance of …(0.2.0 formats kept for strings, numbers, enums, dates and lists);WireLogand inbound frame logging are silent. - The SDK's own panel is no longer captured as host UI.
0.2.0 #
Native-parity release: the Flutter SDK's config wiring, call/agent modes, back navigation, and widget UX brought in line with the Android and React Native SDKs and the revrag-core wire contract. Verified live on a physical device.
Breaking changes — migration required #
- Multi-agent (
flowName) support removed — React / React Native parity. One integration is one agent, so the per-flow scoping is gone:embedInitialize,EmbedProviderandembedEvent(andembedTrack*) no longer take aflowName.EmbedWidget.enabledRouteschanges shape fromMap<String, List<String>>(flow → routes) to a flatList<String>:enabledRoutes: {'main': ['home','account']}→enabledRoutes: ['home','account'].EmbedWidget.groupContinuity/groupDelays(flow-keyed maps) become the singlecontinuity:/buttonDelay:.EmbedRouteManager's flow surface (currentFlow,catalogFlow,getFlowForRoute,setCurrentFlow,flowRoutes,continuityForFlow/delayMsForFlow/delayPolicyForFlow) is replaced by a flat API (activeGroup,isRouteEnabled,groupForRoute,enabledRoutes,continuity,delayMs,delayPolicy). Config, cache and event scoping collapse to one agent; existing on-device caches stay valid (the default scope is unchanged). Multi-agent apps must split into separate integrations.
EmbedWidgetapiKey/embedUrl/primaryColor/groupInsetsremoved (and theEmbedButtonInsettype). Credentials come fromembedInitialize; the FAB position (widget_config.position) and colors (widget_config.colorPalette) come from the backend — the host no longer overrides them on the widget. Use a per-widget key only viaembedInitialize.EmbedWidget/EmbedProviderrightPadding/bottomPaddingremoved. FAB position is now driven solely by the backendwidget_config(right_padding/bottom_padding/widget_position) — the host no longer overrides it in code. Set the position from the dashboard instead; remove the two params from yourEmbedWidgetcall.embedInitializeis now the single, all-named intake —embedInitialize(apiKey: …, appVersion: …, embedUrl: …).apiKeymoved from positional to a required named parameter, andappVersionis now required (embedSetAppVersion()and thepackage_info_plusauto-derivation are gone).EmbedProviderremoved. It only forwardedapiKey/embedUrl/appVersionintoembedInitializeand mounted anEmbedWidget; withembedInitializeas the single intake it was a redundant wrapper. CallembedInitialize+ wrap withEmbedWidgetdirectly.- Server-driven panels/cards now render only in
workflowmode. A call the user starts from the FAB (or the inactivity auto-trigger) is a co-pilot call and drops inbound panel frames. To keep panels, start the call from host code withEmbedWidget.startCall(agentTriggerMode: AgentTriggerMode.WORKFLOW). other_dataflags removed — no peer SDK ever read them:widget_tree_snapshot_enabledandscreen_capture_enabled(widget-tree snapshot sends now gate onaction_config.flags.dataCapture), andembed_route_allowlist(the platform route allowlist is now host-prop-only viaEmbedWidget(usePlatformRouteAllowlist: true)). Tenants relying on the old flags must move the opt-out todataCapture/ the host prop.
New #
AgentTriggerMode(co-pilot / workflow) — host-declared atEmbedWidget.startCall(agentTriggerMode:)and latched per call; a pureresolveCallPlandecides who owns the UI, whether panels render, and whether the avatar surface opens. The enum values areCO_PILOT/WORKFLOW(identifier-for-identifier with Android), and the mode's cross-SDK wire string is lowercasecopilot/workflow— serialize withmode.wire, read back withparseAgentTriggerMode(String?)(case- and separator-insensitive, mirrors Android's parser).- Visibility groups (
buttonVisibility) — React Native parity.EmbedWidget(buttonVisibility: EmbedButtonVisibilityConfig(groups: [...]))lets different sets of screens carry different FAB continuity / pre-show delay / delay-policy (EmbedButtonGroupConfig{id, screens, continuity, delayMs, delayPolicy}). Resolves per current route (first matching group wins); a group'sscreensalso join the allow-list. Screens in no group fall back to the widget-levelcontinuity/buttonDelay/embedButtonDelayMs. EmbedWidget.expandWidget()/embedExpandWidget()— programmatic inverse ofcollapseWidget()(emitsagent_tap_to_open);startCall()does not auto-expand.EmbedWidget.openAvatarScreen()— programmatically open the full-screen agent surface (starts a workflow call if none is running, else re-opens the surface after a collapse).action_configcoverage completed —dataCapturemaster gate,awakeningEffect/activeBorderGlow/highlighting/clickTrackingkill-switches, the three themed gradients, and all 26effects.*tuning knobs (unset knobs reproduce the built-in look exactly). New: an active-border watchdog and nudge-time FAB breathing.widget_configcompleted —position.*andagentAvatar.avatarSizemerged; sizing follows the nativeAvatarSizingrule and bar typography scales with it.isCollapsedopening state honored once per lifetime.call_idlatched from/tokenand sent flat onagent_conversation_started/_ended(Android parity).- System-back rung —
backnow sweeps every reachable navigator (deepest-first, PopScope-aware) so nested / per-tab / activity-per-screen hosts no longer exhaust the ladder.
Fixes & behavior (native parity) #
- Inactivity:
nudgeDelay <= 0= off;show_popupshows only with a non-empty tooltip;shouldAutoCollapseCard(captions suppress the fold and cancel a running countdown). - Widget UX: one centered 90/60/40 expanded-bar width; awakening plays only while no call is live and collapse returns the FAB to its dock; static collapsed avatar; avatar-open icon hidden in co-pilot; ink-square and glow-bloom collapse artifacts fixed; a silent config revalidate no longer collapses an open bar; avatar cannot surface on a co-pilot call (stale-plan race closed).
- Memory (OOM guard): the
ExplorationLognode union is now call-scoped (reset onAgentRuntime.stop, not only at mission start) and capped LRU to 12 screens, so it can no longer grow unbounded across calls for the life of a long-lived host process. - Internal:
lib/src/embed.dartsplit into focused part files; comment cleanup;CLAUDE.mdanddocs/CONFIG_WIRING.mdadded.
Fixes (device-verified) #
- Orb-flight performance. The awakening overlay no longer paints a full-screen radial-gradient bloom or two rainbow sweep-shader rings per frame: the bloom is bounded to a circle and a single burst ring is drawn, with smaller blur radii. On a mid-tier device this cut janky frames from ~10% to ~6% (raster p95 22.8 ms → 16.9 ms) — the bottleneck was fill-rate/shader churn, not blur.
- Mic and speaker toggles are single-flight. Rapid mute/unmute (or speaker)
taps no longer stack concurrent native
setMicrophoneEnabled/setSpeakerOncalls that churned the audio session and lagged the agent's speech; a burst now collapses to one native op at a time, and the stale-isMutedread is gone. - FAB collapse no longer dips. On call end the FAB settles straight at its
dock instead of dropping ~14 px and springing back — the
elasticOutlanding bounce that restarted after the FAB had already docked is removed.
Previously staged (0.0.21-dev) #
Behavioral change — retry policy. A tap/navigate whose SEMANTIC dispatch
fired but produced zero observable evidence now fails after ONE attempt
(VerificationOutcome.unknown, terminal): retrying an already-fired action
re-fires it physically (double/triple submits). Synthetic-pointer fallbacks
and null after-snapshots keep their retries — nothing semantic was delivered.
Hosts that tuned retriesPerStep for this case should rely on the backend's
failure_reason: tap_had_no_observable_effect to re-plan instead.
Source-breaking for StepVerifier subclasses. StepVerifier.verify
gained an optional named parameter UiNode? resolvedTarget (the find-ladder
winner the runner acted on). Custom verifier overrides must add it (and may
ignore it).
Also in this release: failure_reason emitted alongside error on
mission_status (the backend reads only the former); node model gains
value/checked/editable/secure with capture-time (sticky) secure
redaction — typed field contents no longer ride text or stable ids; find
ladder edge fixes (rung-1 promotion, single-token subtree hatch, IoU floor,
normalised forbid_text, container-anchored inside_text); config cache
un-brick (other_data versioned + TTL + revalidate; isLive hides only on
explicit false); wait clamp; stale-node tap refusal; CI + lib/core test
suite (180+ tests).
0.0.20 #
Fixes #
- Fixed agent audio dropping when a wired/Bluetooth headset is connected mid-call. The SDK no longer forces the audio route on a polling timer; it relies on LiveKit's native headset prioritization and reacts only to real device-change events.
- The inactivity tooltip no longer appears while a call is connecting/connected (previously it could pop up after the bar auto-collapsed).
- The inactivity tooltip now auto-dismisses on any user interaction (tap, scroll, drag), not only a FAB tap.
Features #
- The expanded voice bar auto-collapses back to the FAB after a call connects while keeping the call live. Delay is configurable via
collapsedView.collapsedTime(seconds, default 15s). - Added a "live call" pulse around the collapsed FAB that appears only after the bar fully collapses and only while the user or agent is speaking. Color is configurable via
colorPalette.pulseColor(default purple#B391F3). - The default avatar fallback now uses a Lottie animation instead of the Material
chat/personicons.
Tweaks #
- Reduced the expanded bar height, Connect-button padding, and mute/end-call button sizes.
- Bumped the SDK version reported to the backend (
sdk_version) to0.0.20.
0.0.19 #
Fixes #
- Relaxed the
livekit_clientconstraint from the exact pin2.6.5back to^2.5.3. The exact pin transitively forcedmeta ^1.17.0(required by livekit_client 2.6.5), which brokepub getfor apps on older Flutter SDKs that pinmeta 1.16.0. The range lets pub select a compatible livekit_client version per app.
0.0.18 #
FAB pre-show delay & per-flow delay policy #
- Added
embedButtonDelayMs(onEmbedWidget/EmbedProvider) — global delay before the FAB appears after navigating to an enabled route. Default0(no delay). - Added
groupDelays(Map<String, EmbedButtonDelay>) — per-flow delay override withEmbedButtonDelayPolicy:perScreen,oncePerGroupEntry,oncePerAppSession. Falls back toembedButtonDelayMs. - Added React-API aliases on
EmbedButtonContinuity:continuous(=keepVisible) andperScreen(=reset), plusEmbedButtonContinuity.fromString/EmbedButtonDelayPolicy.fromStringparsers.
Fixes #
- Nudge/inactivity countdown now starts when the FAB becomes visible (after
embedButtonDelayMs), not when the route is first entered. inactivity_behaviour: "auto_trigger"now maps toInactivityMode.AUTO_TRIGGER(auto-starts the call likestart_call); previously it silently fell back to showing the popup. AddedInactivityMode.fromBehaviour.- FAB no longer renders off-screen on the app's initial/home route — position is recomputed once the screen size is available (post first layout) and on metrics changes.
- Fixed
agentTapToOpen/agentTapToCloseenum identifiers (backend valuesagent_tap_to_open/agent_tap_to_closeunchanged).
Docs #
- Added
doc/fab-visibility-advanced.md(Flutter FAB visibility guide) anddoc/embed-button-delay.md.
0.0.17 #
- Typewriter transcription panel: vertical expansion from bottom to top when
type == "calculation"data received via LiveKit - FAB positioning:
widget_position(bottom-right,bottom-left,top-right,top-left) now respected from backend config - Fixed
bottomPaddingandrightPaddingnot applying correctly to FAB position - Bounce animation on FAB when transcription panel closes
- Analytics event names updated:
agent_tap_to_open/agent_tap_to_close SdkEventNameenum added for all internally-fired analytics event names (exported as public API)- All public event APIs (
embedEvent,embedTrackEvent, etc.) now returnFuture<bool>for send-success feedback EmbedRouteListenerwidget added for GoRouter / ShellRoute automatic route detectionEmbedNavigatorObservernow accepts optionalnameExtractorfor GoRouter alias support- Route matching now strips leading slashes — GoRouter paths (
/home) match aliases (home) - Cache-first config strategy restored in
_fetchUIConfigwith API fallback on cold start - Removed all debug logs; all exceptions handled internally — zero crash guarantee for host app
- Removed dead
_buildTranscriptionPaneloverlay (superseded by inline vertical expansion)
0.0.16 #
- Graceful exception handling across SDK — zero crash guarantee
- Fixed event routing and SDK key resolution issues
- Stability Improvements
0.0.15 #
- Stability Improvements
0.0.14 #
- Minor Bug Fixes and Stability Improvements
0.0.13 #
- Minor Bug Fixes and Stability Improvements
0.0.12 #
- Added capability to send ANALYTICS_DATA event from screens which are not part of enabled routes.
0.0.11 #
- Added ANALYTICS_DATA event for tracking structured analytics events with a specific event identifier
0.0.10 #
- Minor Bug Fixes and UI improvements
0.0.9 #
- Added rightPadding and bottomPadding to set the initial position of Embed Widget
0.0.8 #
- Added onPermissionStatusChanged callback to recieve the permission status change events
- Minor Bug Fixes and Stability Improvements
0.0.7 #
- Minor Bug Fixes and Stability Improvements
0.0.6 #
- Bluetooth Headset Support added
- Added Inactivity Actions
- Minor Bug Fixes and Stability Improvements
0.0.5 #
- Added Multi Flow Support with enabledroutes
0.0.4 #
- Added Support for Re-initialisation of embed agent
0.0.3 #
- Added Support for Buttons
- Added Route based activation
- Added embedEvent call for user generated USER_DATA nd SCREEN_STATE events
- Added Embed Config Service for management of apiKey and embedUrl
- Improved widget context extraction
- Updated Documentation
0.0.2 #
- Removed the requirement for bluetooth permissions
- Updated License
0.0.1 #
- Initial release of Embedded Agent SDK
- Core functionality for automatic widget tree traversal
- Real-time widget monitoring with collapsible interface
- Comprehensive widget property extraction
- Cross-platform support for Android and iOS
- Example applications demonstrating SDK usage
- JSON output format for widget information
- Performance-optimized widget tree analysis