# RUM Changelog

> New instrumentation, config options, and fixes for Last9's Web, Android, iOS, React Native, and Flutter RUM SDKs, by version.

Source: https://last9.io/docs/real-user-monitoring/changelog/

Release notes for all five RUM SDKs. See [Distribution](/docs/real-user-monitoring/#distribution) for CDN links and versioned URLs.

## Current stable releases

| Platform     | Stable channel | Version |
| ------------ | -------------- | ------- |
| Web          | `stable/v2`    | `2.8.0` |
| Android      | `stable/v1`    | `1.6.0` |
| iOS          | `stable/v1`    | `1.6.0` |
| React Native | `stable/v1`    | `1.6.0` |
| Flutter      | `stable/v1`    | `1.6.0` |

Stable channels are major-version pinned. Web `stable/v2` currently serves
`2.8.0`. Mobile `stable/v1` currently serves `1.6.0`, which is also the latest
versioned mobile release. Pin versioned CDN URLs from
[Distribution](/docs/real-user-monitoring/#distribution) for the iOS Swift
Package Manager Git URL, reproducible lockfiles, or an exact artifact
checksum.

Each platform section below is ordered newest first. Entries marked `stable`
are served by the corresponding stable channel.

## Web

### v2.8.0 (2026-08-12) · stable

**Fold dynamic segments in network span names.** Network span names now fold fully-numeric and UUID path segments to `?` (e.g. `GET /workspaces/1` → `GET /workspaces/?`), so per-endpoint metrics aggregate instead of exploding by id. Uses the same rule as view naming; version-like segments such as `v2` are preserved.

- Behavior change: the emitted `span_name` for fetch/XHR/resource spans changes. The full raw URL remains on `http.url`. GraphQL span names (operation-based) are unaffected.

### v2.7.1 (2026-08-10)

**Disable browser bundle source maps.** Production ESM and UMD bundles no longer emit source map files.

### v2.7.0 (2026-07-14)

**Add GraphQL network observability**

- Automatically enriches GraphQL `fetch`/XHR spans from the request body. No GraphQL client dependency or setup required.
- Descriptive span names, e.g. `GraphQL: "GetUser" query` (anonymous operations become `GraphQL: query`).
- Sets `graphql.operation.name` and `graphql.operation.type`, and tags the span with `l9.span.category=network`.
- Detects GraphQL `errors[]` on HTTP 200 responses (`error.type=GraphQLError`, `graphql.error.count`).
- Detection is body-driven, so plain REST requests are never misclassified. Request bodies are scanned up to 256KB, response bodies up to 64KB.

### v2.6.1 (2026-07-09)

**`recordCustomEvent` no longer auto-creates a view span.** If there's no active view span when a custom event fires, no view span is fabricated to attach it to. The span event is dropped in that case, but the log is still emitted unconditionally (as an orphan, omitting `trace.id`/`span.id`/`view.id`). Aligns Web with the same orphan-log rule used by the mobile SDKs.

### v2.6.0 (2026-07-07)

**Add `propagateTraceHeaders` to disable trace context injection.** `network.backendCorrelation.propagateTraceHeaders` (default `true`). Set to `false` to stop injecting `traceparent`, `tracestate`, and B3 headers on outgoing requests while keeping network span collection and baggage propagation.

```javascript
L9RUM.init({
  network: {
    backendCorrelation: {
      enabled: true,
      propagateTraceHeaders: false,
    },
  },
});
```

### v2.5.1 (2026-06-02)

**Fix unknown browser RUM error types.** Infers `exception.type` for non-`Error` throws and rejections (strings, primitives, plain objects), preserves explicit error names, and safely handles malformed error payloads in global error listeners.

### v2.5.0 (2026-05-13)

**Add native WebView session correlation.** Browser RUM picks up a native-provided `window.__LAST9_RUM_NATIVE_CONTEXT` and uses the native `session.id`, so WebView spans correlate with the host mobile app's spans. WebView spans are stamped with `native.view.id` and `view.type=webview`.

### v2.4.0 (2026-05-04)

**Add `geoAttributesOverride`.** Set `enduser.geo.*` resource attributes explicitly; overrides win over auto-detected values including IP-based geolocation.

```typescript
L9RUM.init({
  geoAttributesOverride: {
    country: "India",
    country_code: "IN",
    city: "Bangalore",
  },
});
```

### v2.3.0 (2026-04-27)

**Comprehensive browser performance metrics.** For parity with industry-standard RUM tooling:

- Full Navigation Timing breakdown (`document.dom_processing_time`, `dom_interactive_time`, `page_rendering_time`, `page_load_time`, plus `dns_time`/`tcp_time`/`ssl_time`/`request_time`/`response_time`)
- Long task tracking (`longtask.blocking_time`, `longtask.total_blocking_time`, attribution data)
- Browser/device network context (`network.effective_type`, `downlink`, `rtt`, `save_data`)
- Extended Core Web Vitals attribution for LCP, CLS, INP, FCP
- Resource timing enhancements (`resource.render_blocking`, transfer/encoded/decoded body sizes)

### v2.2.0 (2026-03-16)

**Add `isolateTracePerRequest`.** Each outgoing fetch/XHR request can propagate its own unique `traceId` instead of sharing the view's root span traceId, so backends can correlate logs/traces to the exact request. `session.id`/`view.id` are added to baggage automatically so requests can still be grouped by session and view.

### v2.1.0 (2026-02-06)

**Add W3C Baggage propagation for custom attribute forwarding.** Propagate whitelisted global span attributes to backend services via W3C Baggage headers, so a backend can read attributes like `tenant.id` or a caller-supplied session identifier off the `baggage` header. Includes percent-encoding, a configurable size limit (default 8192 bytes per the W3C recommendation), and URL pattern filtering via `trackedUrls`. Never whitelist sensitive data (tokens, passwords, PII).

### v2.0.28 – v2.0.37 (2026-01 – 2026-02)

Earlier hardening and coexistence fixes, squashed:

- `forceOverwrite` option so Last9 can take ownership of `traceparent` while preserving other vendors' `tracestate` entries when multiple RUM/APM libraries run on the same page.
- Backend-correlation header fixes when another RUM SDK on the page registers its own global propagator or patches `fetch`/`XMLHttpRequest`.
- Sessions persisted to `localStorage` (shared across tabs) with a 30-minute inactivity timeout alongside the existing 4-hour max duration; sampling decisions persisted the same way.
- `view.name` auto-generated from the URL path when not set explicitly, with dynamic segment normalization (`/user/12345/profile` → `/user/?/profile`).
- `geoIpEnabled` added. Geolocation enrichment is **off by default** to avoid collecting location metadata without consent.
- Removed deprecated `unload` listeners in favor of `visibilitychange`/`pagehide`/`freeze`/`beforeunload`.

## Android

### v1.6.0 (2026-08-21) · stable

**Auto-name WebView-hosted views from the current URL.** A WebView-hosted view (instrumented via `instrument(webView)` / `getWebViewInjectedJavaScript()`, tagged `view.type=webview`) is now auto-named from the current main-frame URL: `app.screen.name` is the folded pathname (e.g. `https://www.example.com/health-record?webViewFrame=edgeToEdge` → `/health-record`) instead of the host Activity class name. Numeric/UUID path segments fold to `?` (e.g. `/order/12345` → `/order/?`). The full URL (scheme, host, path, and query) is recorded on `view.url`.

- Fires on both full loads (`WebViewClient.onPageStarted`) and in-WebView main-frame history changes (`doUpdateVisitedHistory`).
- An explicit `startView` / `setViewName` still wins — the URL-derived name never overwrites an app-provided one. `view.url` is still recorded either way.
- Scoped to WebView-hosted views. Normal native (class-named) view names are unchanged.

### v1.5.2 (2026-08-21)

**Fix: `addEvent` during a view's teardown attaches to that view, not the next one.** A custom event fired while screen A is being torn down now lands on **A**, not on the next screen B. Previously the Android sequence `A.onPause` (ends A's view) → `B.onResume` (starts B) → `A.onDestroy` (`addEvent`) attached the event to B — or dropped it as an orphan log without `view.id` in the pause→resume gap.

- On view end the just-ended view is kept for a 1000 ms grace window (`span.end()` is deferred but ended with its logical timestamp, so `view.time_spent` is unchanged). During the window `addEvent` attaches to the just-ended view even if the next view is already current; after it, and whenever there is no current view, the OTLP log still correlates to the last view for the rest of the session.
- A manual `startView(...)` still ends the previous view immediately. Network and TTFD correlation still follow the current view only.
- Prefer firing screen summaries in `onPause` (Activity) / `onDestroyView` or `onPause` (Fragment) rather than `onDestroy`.

### v1.5.1 (2026-08-20)

Version parity release. No functional Android changes. Released in lockstep with the iOS SDK's new Swift Package Manager distribution.

### v1.5.0 (2026-08-20)

**`autoViewTrackingEnabled` + no duplicate native views from `startView`.** Added `L9RumConfig.autoViewTrackingEnabled` (default `true`). When `false`, Activity resume/pause no longer opens or closes view spans — the app owns view names entirely via `startView` / `setViewName`. Startup, session, and lifecycle spans are unaffected.

- Mixing a manual `startView`/`setViewName` with Activity auto-tracking no longer creates a duplicate native view: a manual view is not overwritten by an auto view on the next resume, and pausing the host Activity no longer ends a manual view.
- `setViewName` renames the current auto-tracked view in place (`app.screen.name`) with no extra span — the recommended way to name a WebView host Activity alongside `instrument(webView)`.
- Fixed a `view.type=webview` / `native.view.id` leak: WebView re-injection no longer re-marks the now-active view as a WebView host. Only the view live when `instrument(webView)` / `getWebViewInjectedJavaScript()` is called is tagged.

### v1.4.1 (2026-08-19)

**Keep the session alive on Home / background.** Backgrounding the app — Home button, or leaving for a system file picker — no longer ends the RUM session. Returning within the 30-minute inactivity window resumes the same session, so later events keep their `session.id`. Rollover still happens on inactivity timeout, max duration, or `shutdown()`.

**Backfill `Session End` for killed sessions on next launch.** A swipe-from-Recents, crash, or OS kill terminates the process with no guaranteed teardown, so no `Session End` is emitted at that moment. On the next cold start, if an orphaned persisted session is found, the SDK backfills that session's `Session End` span — backdated to last activity, with `session.end_reason=process_death` — then starts a fresh session chained via `session.previous_id`. A same-process re-init (for example a React Native JS reload) still restores the in-window session.

### v1.4.0 (2026-08-18)

**Configurable batch export schedule delay.** Added `scheduleDelayMs` to `L9RumConfig` — how long the batch span and log processors wait before flushing queued telemetry (previously the OTel Java default of 5s). When unset: 5000 ms in production, and 1000 ms when `debugLogs` is `true`, so a single `addEvent` becomes visible within ~1s during development. View span _events_ still export only when the view span ends; the lower debug delay speeds up the dual-emitted custom-event logs.

### v1.3.0 (2026-08-12)

**Fold dynamic segments in network span names.** Network span names fold fully-numeric and UUID path segments to `?` (e.g. `GET /workspaces/1` → `GET /workspaces/?`). Applies to OkHttp auto-instrumentation and bridged (`startNetworkSpan`) spans; version-like segments such as `v2` are preserved. The raw URL remains on `url.full`. GraphQL span names are unaffected. A `spanNameOverride` or span hook still overrides the folded name.

### v1.2.1 (2026-08-10)

Version parity release. No functional Android changes. Version bumped to `1.2.1` to keep all mobile SDKs at the same version; this release carries an iOS fix for a main-thread deadlock in WebView instrumentation (`instrument(webView:)`).

### v1.2.0 (2026-07-30)

Version parity release. No functional Android changes. Version bumped to `1.2.0` to keep all mobile SDKs at the same version; this release carries the new React Native Expo config plugin.

### v1.1.10 (2026-07-31)

Version parity release. Bumped to `1.1.10` to keep all mobile SDKs and bridge dependencies at parity for the React Native `1.1.10` release, which fixes a New Architecture Android clean-build failure (`:app:configureCMakeDebug` missing the generated codegen JNI directory).

### v1.1.9 (2026-07-29)

**`getSessionId()` no longer returns an empty string.** On the sampled path, session start writes an internal placeholder entry before the session-start span produces the real id, so `getSessionId()` could return `""` to a caller reading from another thread during that window. It now reports `null` for that state; the placeholder is never surfaced to callers.

- `addSessionIdObserver`'s initial callback had the same gap — it read the session id directly instead of through `getSessionId()`. Both now go through one accessor.
- Session start was already synchronous on Android, so the companion iOS fix (a session id missing right after `initialize()`) never affected Android.

### v1.1.8 (2026-07-27)

Version parity release to keep all mobile SDKs and bridge dependencies at `1.1.8`. No functional Android changes.

### v1.1.7 (2026-07-27)

**AGP / AndroidX consumer compatibility — OpenTelemetry Android agent removed.** The SDK depended on `io.opentelemetry.android:android-agent`, which pulled 67 AndroidX modules into every consumer's dependency graph — including `androidx.core` 1.17.0, whose AAR metadata requires AGP 8.9.1+ and `compileSdk` 36. Consumers on older toolchains failed at `:app:checkReleaseAarMetadata` (`Dependency 'androidx.core:core:1.17.0' requires Android Gradle plugin 8.9.1 or higher`) with no consumer-side fix short of a full toolchain upgrade.

- The agent was never used — no `io.opentelemetry.android` class was referenced. Replaced with direct `opentelemetry-api` and `opentelemetry-sdk` (`api`, 1.59.0).
- **No instrumentation removed.** The agent's auto-instrumentation (activity, fragment, ANR, crash, slow rendering, startup) was never activated; the SDK builds its own tracer/logger pipeline.
- The resolved runtime graph drops from 133 modules to 30 and contains **no AndroidX**, so `androidx.core` resolves to whatever the consumer app declares. Verified on AGP 8.3.2 / Kotlin 1.9.22 / Gradle 8.6 / `compileSdk` 36.
- `opentelemetry-exporter-otlp` is now declared at `1.59.0` (matching what the removed agent BOM already promoted). `kotlin-stdlib` now resolves to `1.9.24` instead of being promoted to `2.0.20`.
- CI now fails the build if **any** AndroidX module reaches the runtime classpath, alongside the existing Kotlin-2.x-metadata `okhttp`/`okio` checks.

### v1.1.6 (2026-07-22)

Version parity release with React Native `1.1.6` (TurboModule migration). No functional Android changes.

### v1.1.5 (2026-07-20)

Version parity release to match the v1.1.5 React Native iOS bridge fix and Flutter iOS deployment-target fix. No functional Android changes.

### v1.1.4 (2026-07-20)

**Kotlin 1.9.x consumer compatibility — OkHttp/Okio transitive metadata.** The OTLP sender pulled `okhttp` 5.x / `okio` 3.x transitively, both compiled with Kotlin 2.2 metadata, breaking apps pinned to Kotlin 1.9.x even after the earlier `kotlin-stdlib` fix. `okhttp` and `okio` are now excluded from the SDK's published dependencies and the runtime pins `okhttp` 4.12.0, keeping the entire dependency graph Kotlin-1.9-readable.

### v1.1.3 (2026-07-15)

**Warm and hot app-start types now emitted.** The AppStart span's `start.type` could structurally only ever be `cold`: a one-shot latch never reset after first launch, so warm starts (Activity recreated) and hot starts (backgrounded → foregrounded) were never classified. Startup classification is now a state machine that re-opens on every background→foreground transition — cold on first launch, warm/hot on each subsequent return to the foreground. Warm/hot start duration now measures from foreground entry rather than process creation.

### v1.1.2 (2026-07-15)

**Kotlin 1.9.x consumer compatibility.** Emits Kotlin metadata `1.9.0` so apps on Kotlin 1.9.x can compile against the AAR (fixes a consumer build failure caused by Kotlin metadata `2.1.0`). Stops publishing `kotlin-stdlib` 2.x on the compile classpath. Minimum consumer toolchain: Kotlin ≥ 1.9.0, AGP ≥ 8.3.

### v1.1.1 (2026-07-15)

**Network interceptor no longer fails requests during attribute collection.** The OkHttp interceptor read cellular attributes via `TelephonyManager.getDataNetworkType()`, which requires `READ_PHONE_STATE` and can throw `SecurityException` on some OEMs even when the permission is absent or granted. That exception could turn an already-successful HTTP response into a failed request. Attribute collection is now fully isolated from the request path, so a failure there can never fail the request, and cellular subtype/carrier reads are gated on a runtime permission check.

### v1.1.0 (2026-07-13)

**Add GraphQL Layer 1 + Layer 2 network observability**

- Parses GraphQL operation name/type from JSON request bodies and `/graphql` paths.
- Descriptive network span names, e.g. `GraphQL: "GetUserPreferences" query`.
- Sets `graphql.operation.name`, `graphql.operation.type`, `l9.span.category=network`.
- Detects GraphQL `errors[]` on HTTP 200 responses (`error.type=GraphQLError`, `graphql.error.count`).
- Optional `networkSpanHook` on `L9RumConfig` for cross-cutting span customization.

### v1.0.0 (2026-07-09)

**`addEvent` dual-emits an OTLP log.** Alongside the span event on the active view span, `addEvent(name, attributes)` now also emits an OTLP log record carrying `event.type=custom`, `event.name`, and caller attributes. If there's no active view span, the span event is dropped but the log still sends as an orphan (no `trace.id`/`span.id`/`view.id`). A custom event never silently vanishes.

### v0.9.0 (2026-07-02)

**Embedded per-flow lifecycle: `shutdown()` and safe re-initialization.** New `shutdown()` flushes and fully tears down RUM so a later `initialize()` starts clean; new `isActive()` reports whether RUM is running. Intended for integrations scoped to a single flow (e.g. a checkout SDK embedded in a host app). Set per-flow attributes via `spanAttributes()` once known.

```kotlin
L9Rum.initialize(application, config)
L9Rum.spanAttributes(mapOf("tenant.id" to "acme", "feature.flag" to "beta"))

if (L9Rum.isActive()) {
    L9Rum.shutdown() // flush + full teardown; a later initialize() re-arms RUM
}
```

### v0.8.0 (2026-06-17)

**Fix Client-ID per-install device identity.** The `Client-ID` ingestion header now uses an SDK-generated, auto-persisted per-install UUID instead of `serviceName`. Previously every device of an app shared one `Client-ID`, collapsing them into a single ingestion rate-limit bucket. No migration path: existing installs generate a fresh ID once.

### v0.7.1 (2026-06-04)

**Fix unknown error types.** Normalizes blank bridged error types using stack headers before falling back to `Error`.

### v0.7.0 (2026-05-22)

**Add network phase child spans.** DNS, TCP connect, TLS handshake, and TTFB child spans under each OkHttp parent HTTP span. Added `L9Rum.instrumentOkHttp(builder, context)` to attach both the interceptor and event-listener factory in one call.

### v0.6.1 (2026-05-21)

Patch release. Version sync across SDKs.

### v0.6.0 (2026-05-20)

**Add Time To Full Display.** `view.ttfd` on View spans reports the maximum time from a completed network response to the next rendered frame.

### v0.5.0 (2026-05-13)

**Add WebView session correlation.** `L9Rum.getWebViewInjectedJavaScript()` and `L9Rum.instrument(webView)` inject native `session.id` and `native.view.id` context so Browser RUM inside an Android WebView correlates with the host app's session.

### v0.4.0 (2026-05-12)

**Add `ignorePatterns` for network tracking exclusions.** Skip noisy requests (CDN assets, images, health checks) before span creation via `fullUrl`/`pathname`/`hostname` patterns, string or regex. Trace headers are still propagated by default unless `propagationMode` is set to `STRIP`.

### v0.3.1 (2026-04-29)

**Add per-request trace isolation and W3C Baggage propagation.** `isolateTracePerRequest` gives each OkHttp request its own `traceId`; `L9BaggageConfig` supports whitelisted attributes, size limits, and URL filtering.

### v0.3.0 (2026-04-22)

**Add resource monitoring and ANR detection.** Periodic memory/CPU sampling, Android ANR detection, and network connectivity attributes (connection type, cellular subtype, carrier) on HTTP spans.

### v0.2.0 (2026-04-15)

**Add sessions, views, errors, and network instrumentation.** Session lifecycle tracking, Activity-backed view tracking, handled/unhandled error capture, OkHttp network instrumentation, user identification.

### v0.1.0 (2026-04-08)

Initial Android SDK release. OpenTelemetry provider setup, OTLP export, resource attributes, batching configuration.

## iOS

### v1.6.0 (2026-08-21) · stable

**Auto-name WebView-hosted views from the current URL.** A WebView-hosted view (instrumented via `instrument(webView:)` / `getWebViewInjectedJavaScript()`, tagged `view.type=webview`) is now auto-named from the committed main-frame URL: `app.screen.name` is the folded pathname (e.g. `https://www.example.com/health-record?webViewFrame=edgeToEdge` → `/health-record`) instead of the host view controller class name. Numeric/UUID path segments fold to `?`. The full URL is recorded on `view.url`.

- Fires on main-frame navigation commits (`WKNavigationDelegate.webView(_:didCommit:)`). Client-side SPA route changes (`pushState` / `replaceState` / hash) are not observable via `WKNavigationDelegate`, so an in-WebView route change without a full navigation does not re-name the view.
- An explicit `startView` / `setViewName` still wins — the URL-derived name never overwrites an app-provided one. `view.url` is still recorded either way.
- Scoped to WebView-hosted views. `instrumentationVersion` reported on the OTel tracer updated to `1.6.0`.

### v1.5.2 (2026-08-21)

**Fix: `addEvent` during a view's teardown attaches to that view, not the next one.** A custom event fired while screen A is being torn down now lands on **A**, not on the next screen B. Previously, once B's `viewDidAppear` made B active, an `addEvent` from A's later teardown (or in the disappear→appear gap) attached to B or went out as an orphan log without `view.id`.

- On view end the just-ended view is kept for a 1000 ms grace window (`span.end()` is deferred but ended with its logical timestamp). During the window `addEvent` attaches to the just-ended view even if the next view is already active; after it, and whenever there is no active view, the OTLP log still correlates to the last view for the rest of the session.
- A manual `startView(...)` still ends the previous view immediately. Prefer firing screen summaries in `viewWillDisappear` rather than `viewDidDisappear` / `deinit`.
- `instrumentationVersion` reported on the OTel tracer updated to `1.5.2`.

### v1.5.1 (2026-08-20)

**Swift Package Manager distribution.** `Last9RUM` can now be added in Xcode via Swift Package Manager from a git URL (`https://github.com/last9/last9-rum-ios.git`), alongside the existing versioned/stable CDN xcframework and CocoaPods. Pin an exact version tag (e.g. `1.5.1`) or track the `stable/v1` branch for the latest non-breaking release within a major. The package wraps the same binary xcframework, so there is no checksum to manage by hand. No runtime changes; `instrumentationVersion` reported on the OTel tracer updated to `1.5.1`.

### v1.5.0 (2026-08-20)

**`autoViewTrackingEnabled` + no duplicate native views from `startView`.** Added `L9RumConfig.autoViewTrackingEnabled` (default `true`). When `false`, swizzled `viewDidAppear` / `viewWillDisappear` no longer open or close view spans — the app owns view names entirely via `startView` / `setViewName`. Startup, session, and lifecycle spans are unaffected.

- `startView` now ends whatever native view span is currently open (a prior manual view **or** an auto span still held by the current view controller) before opening a new one, so exactly one native view is ever active.
- `setViewName` renames the **active** view span in place instead of guessing at the key-window root view controller — so it correctly renames auto-tracked child view controllers (e.g. React Native's `RNSScreen`) too, with no extra span.
- `instrumentationVersion` reported on the OTel tracer updated to `1.5.0`.

### v1.4.1 (2026-08-19)

**Keep the session alive on Home / background.** Backgrounding the app — Home button, `didEnterBackground`, or leaving for a system file picker — no longer ends the RUM session. Returning within the 30-minute inactivity window resumes the same session, so later events keep their `session.id`. Rollover still happens on inactivity timeout, max duration, or app termination (`willTerminate`).

**Backfill `Session End` for killed sessions on next launch.** A force-quit, crash, or OS memory-kill terminates the app with no runnable code, so iOS never emits a `Session End` at that moment. On the next cold start, if an orphaned persisted session is found, the SDK backfills that session's `Session End` span — backdated to last activity, with `session.end_reason=process_death` — then starts a fresh session chained via `session.previous_id`. A same-process re-init still restores the in-window session.

### v1.4.0 (2026-08-18)

**Configurable batch export schedule delay.** Added `scheduleDelayMs` to `L9RumConfig` — how long the batch span and log processors wait before flushing queued telemetry (previously hardcoded to 5s). When unset: 5000 ms in production, and 1000 ms when `debugLogs` is `true`. View span _events_ still export only when the view span ends.

### v1.3.0 (2026-08-12)

**Fold dynamic segments in network span names.** Network span names fold fully-numeric and UUID path segments to `?` (e.g. `GET /workspaces/1` → `GET /workspaces/?`). Applies to URLSession auto-instrumentation and bridged (`startNetworkSpan`) spans; version-like segments such as `v2` are preserved. The raw URL remains on `url.full`. GraphQL span names are unaffected.

### v1.2.1 (2026-08-10)

**Fix WebView instrumentation main-thread deadlock.** Fixed a reproducible main-thread hang when calling `L9Rum.shared.instrument(webView:)` right after creating a `WKWebView`. The view/session observers registered for the WebView re-entered the SDK's non-recursive lock — both synchronously during registration and later from `shutdown()` (which ends the active view span/session while holding the lock) — deadlocking the calling thread and freezing the app.

- `sweepDeadWebViewInstrumentations()` now acquires the lock non-blocking (`try()`) and skips when it is already held, removing the re-entrancy; dead entries are still reclaimed via `instrument()` and teardown.
- `instrumentationVersion` reported on the OTel tracer updated to `1.2.1`.

### v1.2.0 (2026-07-30)

Version parity release. No functional iOS changes. Version bumped to `1.2.0` to keep all mobile SDKs at the same version; this release carries the new React Native Expo config plugin. `instrumentationVersion` reported on the OTel tracer updated to `1.2.0`.

### v1.1.10 (2026-07-31)

Version parity release. Bumped the pod and `instrumentationVersion` to `1.1.10` to keep all mobile SDKs at parity for the React Native `1.1.10` release, which fixes a New Architecture Android clean-build failure in the React Native SDK.

### v1.1.9 (2026-07-29)

**Session id is available as soon as `initialize()` returns.** The session was previously started with a fire-and-forget dispatch onto an internal serial queue, so `getSessionId()` could return `nil` right after `initialize(config:)` returned even though `isActive()` already reported `true`. `start()` is now synchronous and does not return until the session exists, matching Android.

- `getSessionId()` could also return an empty string on the sampled path, where session start writes an internal placeholder before the session-start span produces the real id. It now reports `nil` for that state; the placeholder is never surfaced.
- `addSessionIdObserver`'s initial callback shared the same gap and now reads through the same accessor.
- The same window existed after `flush()`, which ends and restarts the session, and is closed by the same change. `flush()` now blocks the calling thread until the session has restarted (bounded by creating and enqueuing one span) — prefer calling it off the main thread.
- `instrumentationVersion` reported on the OTel tracer updated to `1.1.9`.

### v1.1.8 (2026-07-27)

Version parity release to keep all mobile SDKs and bridge dependencies at `1.1.8`. No functional iOS changes. `instrumentationVersion` reported on the OTel tracer updated to `1.1.8`.

### v1.1.7 (2026-07-27)

Version parity release with Android `1.1.7` (OpenTelemetry Android agent removed on Android). No functional iOS changes. `instrumentationVersion` reported on the OTel tracer updated to `1.1.7`.

### v1.1.6 (2026-07-22)

Version parity release with React Native `1.1.6` (TurboModule migration). No functional iOS changes.

### v1.1.5 (2026-07-20)

**Modernized key-window lookup.** View lifecycle tracking now resolves the key window via `UIWindowScene.connectedScenes` instead of the iOS 15-deprecated `UIApplication.shared.windows`. Behavior is unchanged; the SDK compiles cleanly under `-warnings-as-errors`, and iOS core builds (simulator + device) now run as a required CI gate on every release.

### v1.1.4 (2026-07-20)

Version parity release to match Android's v1.1.4 Kotlin 1.9.x compatibility fix. No functional iOS changes.

### v1.1.3 (2026-07-15)

Version parity release to match Android's v1.1.3 warm/hot app-start fix (that bug was Android-specific). No functional iOS changes.

### v1.1.2 (2026-07-15)

Version parity release to match Android's Kotlin 1.9.x compatibility fix. No functional iOS changes.

### v1.1.1 (2026-07-15)

Version parity release to match Android's network-interceptor fix. iOS was never affected: cellular attributes are read on a background `NWPathMonitor` callback (off the request path), and CoreTelephony returns optionals rather than throwing.

### v1.1.0 (2026-07-13)

**Add GraphQL Layer 1 + Layer 2 network observability.** Parses GraphQL operation metadata from request bodies in `L9URLProtocol`, descriptive span names (`GraphQL: "{name}" {type}`), and detects `errors[]` on HTTP 200 responses.

### v1.0.0 (2026-07-09)

**`addEvent` dual-emits an OTLP log.** Same behavior as Android above. The dual-emitted span event attaches to the active view span even when that view is an auto-tracked child view controller (e.g. React Native's `RNSScreen`), not just the window's root view controller.

### v0.9.0 (2026-07-02)

**Embedded per-flow lifecycle: `shutdown()` and safe re-initialization.** Same model as Android.

```swift
L9Rum.shared.initialize(config: config)
L9Rum.shared.spanAttributes(["tenant.id": "acme", "feature.flag": "beta"])

if L9Rum.shared.isActive() {
    L9Rum.shared.shutdown() // flush + full teardown; a later initialize() re-arms RUM
}
```

### v0.8.0 (2026-06-17)

**Fix Client-ID per-install device identity.** Per-install UUID persisted in `UserDefaults`. Known limitation: `UserDefaults` is included in encrypted device backups, so a restored backup briefly shares a `Client-ID` with the original device until the value diverges. Move the ID to the Keychain or a backup-excluded file if strict per-device uniqueness across restores is required.

### v0.7.1 (2026-06-04)

**Fix unknown error types.** Normalizes blank bridged error types before falling back to `Error`.

### v0.7.0 (2026-05-22)

**Add network phase child spans.** DNS, TCP connect, TLS handshake, TTFB via `URLSessionTaskMetrics.transactionMetrics`. No config change required.

### v0.6.1 (2026-05-21)

Patch release. Version sync across SDKs.

### v0.6.0 (2026-05-20)

**Add Time To Full Display.** `view.ttfd` on View spans.

### v0.5.0 (2026-05-13)

**Add WKWebView session correlation.** `L9Rum.shared.instrument(webView:)` injects native session/view context into `WKWebView` for correlation with Browser RUM.

### v0.4.0 (2026-05-12)

**Add `ignorePatterns` for network tracking exclusions.** Same model as Android, applied to `URLProtocol` interception.

### v0.3.1 (2026-04-29)

**Add per-request trace isolation and W3C Baggage propagation.** Via `URLSession`.

### v0.3.0 (2026-04-22)

**Add resource monitoring and network context.** Memory/CPU sampling, network connectivity attributes, startup tracking.

### v0.2.0 (2026-04-15)

**Add sessions, views, errors, and network instrumentation.** Full RUM instrumentation over the initial export setup.

### v0.1.0 (2026-04-08)

Initial iOS SDK release.

## React Native

### v1.6.0 (2026-08-21) · stable

**Auto-name WebView-hosted views from the current URL (via native).** A WebView instrumented with `L9Rum.instrumentWebView(...)` now has its host native view auto-named from the current main-frame URL: `app.screen.name` becomes the folded pathname (e.g. `.../health-record?webViewFrame=edgeToEdge` → `/health-record`) instead of the host Activity/ViewController class name. The full URL is recorded on `view.url`. An explicit `L9Rum.startView` / `setViewName` still wins. Android updates the name on full loads and in-WebView history changes; iOS updates on navigation commit (SPA route changes are not observable). Native pins: Android `io.last9:rum-android:1.6.0` and iOS `Last9RUM 1.6.0`.

### v1.5.2 (2026-08-21)

**`addEvent` during a view's teardown attaches to that view (via native).** `addEvent` is a native pass-through, so it inherits the 1000 ms grace window: an event fired while a screen is being torn down attaches to **that** screen, not the next one, and the OTLP log still correlates to the last view when none is current. Prefer firing screen summaries before teardown (for example on `blur` / a screen's unmount effect). A manual `startView(...)` ends the previous view immediately. Native pins: Android `1.5.2` and iOS `Last9RUM 1.5.2`.

### v1.5.1 (2026-08-20)

Version parity release. No JS-side changes. Bumps the pinned native dependencies to Android `io.last9:rum-android:1.5.1` and iOS `Last9RUM 1.5.1`.

### v1.5.0 (2026-08-20)

**`autoViewTrackingEnabled` config (via native).** Added `autoViewTrackingEnabled` to the init config (default `true`), forwarded to the native SDKs. Set `false` when your app owns all view names (for example only via navigation instrumentation / manual `startView`) to suppress native Activity/ViewController auto views.

- Carries the native fixes: `startView` never leaves a duplicate native view open, and `setViewName` renames the active view (including auto-tracked child view controllers) in place.
- Bumps the pinned native dependencies to Android `io.last9:rum-android:1.5.0` and iOS `Last9RUM 1.5.0`.

### v1.4.1 (2026-08-19)

**Keep the session alive on Home / background (via native).** Backgrounding the app (Home button or a system file picker) no longer ends the RUM session — the same session continues when the app returns within the 30-minute inactivity window. A force-kill/crash now backfills the killed session's `Session End` on the next cold start (`session.end_reason=process_death`) and starts a fresh session. Native pins: Android `1.4.1` and iOS `Last9RUM 1.4.1`.

### v1.4.0 (2026-08-18)

**Configurable batch export schedule delay (via native).** Added `scheduleDelayMs` to the config — how long the native batch processors wait before flushing queued spans/logs. When omitted: 5000 ms in production, and 1000 ms when `debugLogs` is `true`.

### v1.3.0 (2026-08-12)

**Network span names fold dynamic segments (via native).** Network span names fold fully-numeric and UUID path segments to `?` (e.g. `GET /workspaces/1` → `GET /workspaces/?`). RN forwards the raw URL to the native SDK, which does the folding. Native pins: Android `1.3.0` and iOS `Last9RUM 1.3.0`.

### v1.2.1 (2026-08-10)

**Fix robust `instrumentWebView` argument resolution.** `L9Rum.instrumentWebView(...)` now resolves the native WebView tag from a WebView load event (`event.nativeEvent.target`), a numeric reactTag, a ref object, or a component instance — and never throws. Previously it passed the argument straight to `findNodeHandle`, which threw `Argument appears to not be a ReactComponent` for a `react-native-webview` >= 13 ref (its `.current` is a methods-only imperative handle, not a resolvable component). Older `react-native-webview` refs and other component instances keep working.

- Recommended usage across all `react-native-webview` versions and both architectures: `onLoadStart={(e) => L9Rum.instrumentWebView(e.nativeEvent.target)}`.
- Native pins updated to iOS `Last9RUM 1.2.1` and Android `io.last9:rum-android:1.2.1`; the iOS pin carries a fix for a reproducible main-thread hang when instrumenting a WebView.

### v1.2.0 (2026-07-30)

**Expo config plugin for drop-in Continuous Native Generation (CNG).** Add `"@last9/rum-react-native"` to the `plugins` array in `app.json` and the two native install steps are re-applied automatically on every prebuild: the Last9 CDN Maven repository on Android and the `Last9RUM` Podfile pod source on iOS. Previously these lived in the generated `android/` and `ios/` folders and were wiped on each prebuild, so CNG apps had to re-add them by hand.

```json
{
  "expo": {
    "plugins": ["@last9/rum-react-native"]
  }
}
```

- The iOS podspec version is derived from the installed SDK version, so it never drifts. The plugin is idempotent across repeated prebuilds. It requires a development build (EAS Build or `expo run:*`) — it does not run in Expo Go, which cannot load native modules.
- No changes to the JS API. The manual Android/iOS install steps remain supported for bare React Native apps.
- Bumps the iOS pod to `Last9RUM 1.2.0` and the native Android SDK to `io.last9:rum-android:1.2.0`.

### v1.1.10 (2026-07-31)

**Fix Android New Architecture clean-build failure (`configureCMakeDebug`).** On the New Architecture, a clean or parallel Android build (and Android Studio's Gradle sync) could fail at `:app:configureCMakeDebug` with `add_subdirectory given source ".../@last9/rum-react-native/android/build/generated/source/codegen/jni/" which is not an existing directory`. Introduced with the TurboModule/Codegen migration in `1.1.6`.

- Root cause: the consuming app's autolinking `add_subdirectory()`s the library's generated `codegen/jni` folder, which is produced at build time and is not in the npm package — and nothing ordered the app's CMake configure after the library's codegen under parallel execution.
- Fix: the package now ships a committed `android/src/main/jni/CMakeLists.txt` and points autolinking at it via `react-native.config.js`, mirroring first-party libraries such as `react-native-safe-area-context`.
- No JS-side or public API changes. Bumps the native Android SDK to `io.last9:rum-android:1.1.10` and the iOS pod to `Last9RUM 1.1.10` for version parity.

### v1.1.9 (2026-07-29)

**Fix `initialize()` resolving with an empty or missing `sessionId` on iOS.** `await L9Rum.initialize(config)` resolves with `{ sessionId }`, but on iOS that could come back as `null` or an empty string even on success: the native SDK started the session asynchronously and the bridge read it on the next line, racing session creation. Fixed in native `Last9RUM 1.1.9` — session start is synchronous and the internal placeholder is never surfaced, so `initialize()` resolves with a real session id or rejects.

- `getSessionId()` is fixed by the same change on both platforms and never returns an empty string.
- No JS-side or public API changes; no action needed beyond upgrading.
- Bumps the iOS pod to `Last9RUM 1.1.9` and the native Android SDK to `io.last9:rum-android:1.1.9` (Android carries the `getSessionId()` half; its session start was already synchronous).

### v1.1.8 (2026-07-27)

Version parity release. Bumps the native Android SDK and iOS pod dependency to `1.1.8`. There are no JS-side changes.

### v1.1.7 (2026-07-27)

**AGP / AndroidX consumer compatibility on Android.** Bumps native Android SDK to `io.last9:rum-android:1.1.7`, which removes the OpenTelemetry Android agent and with it 67 transitive AndroidX modules — including `androidx.core` 1.17.0, whose AAR metadata requires AGP 8.9.1+. React Native Android apps on an older toolchain were failing the AAR metadata check with no fix short of a full toolchain upgrade. Picked up automatically; `androidx.core` now resolves to whatever the consumer app declares.

- No instrumentation is lost — the removed agent's auto-instrumentation was never activated by the SDK.
- Bumps the iOS pod to `Last9RUM 1.1.7` (version parity; no functional iOS changes).
- Plugin version aligned to `1.1.7`. There are no JS-side changes.

### v1.1.6 (2026-07-22)

**Fix `initialize()` hanging on the New Architecture (bridgeless).** On bridgeless (default from RN 0.76+), `await L9Rum.initialize(...)` could hang forever: native called `resolve(...)`, but the JS promise never settled because the module was reached through React Native's interop layer ([facebook/react-native#44555](https://github.com/facebook/react-native/issues/44555)).

- The module is now a real TurboModule (`RNL9RumSpec` Codegen spec). JS↔native calls dispatch over JSI directly.
- iOS: `getTurboModule:` returns `NativeL9RumSpecJSI`; promise selectors renamed to `resolve:`/`reject:`.
- Android: `TurboModule` marker + `BaseReactPackage` with `isTurboModule = true`.
- Backwards compatible: public JS API unchanged; Old Architecture (Paper) still supported.
- Bundles native Android/iOS RUM `1.1.6`.

```typescript
// Upgrade to 1.1.6+ (latest versioned: 1.6.0) and await initialize on bridgeless
const { sessionId } = await L9Rum.initialize(config);
```

### v1.1.5 (2026-07-20)

**Fix iOS build failure when bridging a nil session ID.** After `initialize()` began resolving `{ sessionId }`, the iOS bridge failed to compile (`binary operator '??' cannot be applied to operands of type 'String?' and 'NSNull'`). The bridge now resolves a non-nil session id to JavaScript as a string and nil as `null`. Also aligns the `endNetworkSpan` status-code bridge signature so HTTP status codes marshal correctly, and adds a real React Native iOS consumer build (autolinking + CocoaPods + Swift compile) as a required CI gate on every release. Bundles native Android/iOS RUM `1.1.5`.

### v1.1.4 (2026-07-20)

Bundles native Android RUM `1.1.4`, which stops the OTLP `okhttp`/`okio` dependencies (Kotlin 2.2 metadata) from reaching a consumer's compile classpath — fixing Android build failures for apps on Kotlin 1.9.x. No JS-side changes.

### v1.1.3 (2026-07-15)

Bundles native Android RUM `1.1.3`, which fixes AppStart `start.type` being stuck on `cold` — warm and hot starts are now classified for the whole process lifetime. No JS-side changes.

### v1.1.2 (2026-07-15)

**Initialize no longer fails silently.** `L9Rum.initialize()` now returns `Promise<{ sessionId }>` and **rejects** when required config is missing or native init doesn't become active. Use `await L9Rum.initialize(...)` and/or `await L9Rum.isActive()` to verify readiness. Don't rely only on a null `getSessionId()` after a fire-and-forget call. Bundles native Android/iOS RUM `1.1.2`.

```typescript
try {
  const { sessionId } = await L9Rum.initialize(config);
} catch (error) {
  console.error("RUM failed to initialize", error);
}

const running = await L9Rum.isActive();
```

### v1.1.1 (2026-07-15)

Bundles native Android RUM `1.1.1`, fixing a `SecurityException` crash in the shared OkHttp interceptor for RN Android apps using `nativeNetworkInterception`. iOS pod bumped to `1.1.1` for parity.

### v1.1.0 (2026-07-13)

**Add GraphQL Layer 1 + Layer 2 network observability**

- **Layer 1 (on by default):** descriptive span name `GraphQL: "{name}" {type}` with `graphql.operation.*` attributes; `errors[]` on HTTP 200 flagged as `error.type=GraphQLError`.
- **Layer 2 (opt-in):** `l9GqlLink` Apollo Link (requires the optional `@apollo/client` peer dependency) with opt-in `captureVariables` (redacted) and `captureErrorMessages` (truncated).
- Bundles native Android/iOS RUM `1.1.0`.

### v1.0.0 (2026-07-09)

**`addEvent` dual-emits an OTLP log** (inherited from native). Bridge unchanged; behavior comes from the underlying Android/iOS SDKs.

### v0.9.0 (2026-07-02)

**Embedded per-flow lifecycle.** `L9Rum.shutdown()` and `L9Rum.isActive()` bridged from native. `shutdown()` returns `Promise<void>`, so a caller can `await L9Rum.shutdown()` before re-initializing the next flow.

```typescript
L9Rum.initialize(config);
L9Rum.spanAttributes({ "tenant.id": "acme", "feature.flag": "beta" });

if (await L9Rum.isActive()) {
  await L9Rum.shutdown();
}
```

### v0.8.0 (2026-06-17)

**Fix Client-ID per-install device identity.** Handled entirely by the native iOS/Android SDKs; no RN changes required.

### v0.7.1 (2026-06-04)

**Fix unknown error types.** Infers `exception.type` for non-`Error` values (strings, primitives, plain objects).

### v0.7.0 (2026-05-22)

**Add network phase child spans.** RN now defaults to native network interception for DNS/TCP/TLS/TTFB phase timing instead of JS-only fetch/XHR interception. JS fallback remains available when `nativeNetworkInterception` is explicitly disabled.

### v0.6.1 (2026-05-21)

**Patch release.** Added typed serialized ignore-pattern shapes for the native bridge payload and applied `ignorePatterns` filtering to RN's `fetch`/`XMLHttpRequest` instrumentation.

### v0.6.0 (2026-05-20)

**Add Time To Full Display.** Native `view.ttfd` reporting for RN network requests.

### v0.5.0 (2026-05-19)

**WebView session correlation.** `L9Rum.instrumentWebView(event.nativeEvent.target)` (recommended) or a numeric tag, ref, or component instance installs a forwarding delegate that re-injects `session.id`/`native.view.id` on every page commit; `L9Rum.getWebViewInjectedJavaScript()` remains available as a one-shot static script. `webViewAutoLoadBrowserRum: true` auto-loads Browser RUM into the WebView.

### v0.4.0 (2026-05-12)

**Add `ignorePatterns` for network tracking exclusions.** Browser-style `ignorePatterns` on `L9Rum.initialize()`.

### v0.3.1 (2026-04-29)

**Add per-request trace isolation and W3C Baggage propagation.** Bridged to native Android/iOS.

### v0.3.0 (2026-04-22)

**Add React Navigation and runtime monitoring support.** `L9ReactNavigationInstrumentation`, JS-level fetch/XHR interception, native resource monitoring and Android ANR detection exposed through RN config.

### v0.2.0 (2026-04-15)

**Add React Native bridge for mobile RUM APIs.** Core RUM APIs bridged to Android/iOS: `identify()`, `clearUser()`, `captureError()`, `addEvent()`, `startView()`, `setViewName()`.

### v0.1.0 (2026-04-08)

Initial React Native SDK release.

## Flutter

### v1.6.0 (2026-08-21) · stable

**Version parity release.** No Dart-side functional change. Bumped to `1.6.0` to keep all mobile SDKs at the same version; this release carries native WebView view auto-naming (host WebView views named from the folded URL path + a `view.url` attribute).

- **WebView auto-naming does not yet apply to Flutter.** It fires only for WebViews handed to the native `instrument(webView)` entry point. Flutter has no `instrumentWebView` bridge — it uses `getWebViewInjectedJavaScript()`, which stamps `view.type=webview` but does not attach the native navigation delegate that drives URL-based naming. Name WebView host views with `L9Rum.startView(...)` in the meantime.
- Bumps the pinned native dependencies to Android `io.last9:rum-android:1.6.0` and iOS `Last9RUM 1.6.0`.

### v1.5.2 (2026-08-21)

**`addEvent` during a view's teardown attaches to that view (via native).** `addEvent` is a native pass-through, so it inherits the 1000 ms grace window with no Dart changes. Prefer firing screen summaries before teardown (for example in `dispose()` / a route's pop callback). A manual `startView(...)` ends the previous view immediately. Native pins: Android `1.5.2` and iOS `Last9RUM 1.5.2`.

### v1.5.1 (2026-08-20)

Version parity release. No Dart-side changes. Bumps the pinned native dependencies to Android `io.last9:rum-android:1.5.1` and iOS `Last9RUM 1.5.1`.

### v1.5.0 (2026-08-20)

**`autoViewTrackingEnabled` config (via native).** Added `L9RumConfig.autoViewTrackingEnabled` (default `true`), forwarded to the native SDKs. Set `false` when your app owns all view names to suppress native Activity/ViewController auto views.

- Carries the native fixes: `startView` never leaves a duplicate native view open, and `setViewName` renames the active view in place.
- Bumps the pinned native dependencies to Android `io.last9:rum-android:1.5.0` and iOS `Last9RUM 1.5.0`.

### v1.4.1 (2026-08-19)

**Keep the session alive on Home / background (via native).** Backgrounding the app no longer ends the RUM session — the same session continues when the app returns within the 30-minute inactivity window. A force-kill/crash now backfills the killed session's `Session End` on the next cold start (`session.end_reason=process_death`). Native pins: Android `1.4.1` and iOS `Last9RUM 1.4.1`.

### v1.4.0 (2026-08-18)

**Configurable batch export schedule delay (via native).** Added `scheduleDelayMs` to `L9RumConfig`. When omitted: 5000 ms in production, and 1000 ms when `debugLogs` is `true`.

### v1.3.0 (2026-08-12)

**Network span names fold dynamic segments (via native).** Network span names fold fully-numeric and UUID path segments to `?` (e.g. `GET /workspaces/1` → `GET /workspaces/?`). Flutter forwards the raw URL to the native SDK, which does the folding. Native pins: Android `1.3.0` and iOS `Last9RUM 1.3.0`.

### v1.2.1 (2026-08-10)

Version parity release. No Dart-side changes. Plugin version aligned to `1.2.1` across `pubspec.yaml`, the Android library, and the iOS podspec. Native pins bumped to iOS `Last9RUM 1.2.1` and Android `io.last9:rum-android:1.2.1`; the iOS pin carries a fix for a reproducible main-thread hang when instrumenting a WebView.

### v1.2.0 (2026-07-30)

Version parity release. No Dart-side changes. Plugin version aligned to `1.2.0` across `pubspec.yaml`, the Android library, and the iOS podspec to keep all mobile SDKs at the same version; this release carries the new React Native Expo config plugin. Native pins: iOS `Last9RUM 1.2.0` and Android `io.last9:rum-android:1.2.0`.

### v1.1.10 (2026-07-31)

Version parity release. Plugin version aligned to `1.1.10` across `pubspec.yaml`, the Android library, and the iOS podspec, with native pins bumped for parity. No Dart-side changes. Ships alongside the React Native `1.1.10` fix for a New Architecture Android clean-build failure.

### v1.1.9 (2026-07-29)

**`getSessionId()` no longer returns null or an empty string after init.** Bumps the iOS pod to `Last9RUM 1.1.9`, which fixes a session id missing right after `initialize()`: iOS started the session asynchronously, so a `getSessionId()` call soon after initializing raced session creation and returned `null`, or returned an internal empty-string placeholder if it landed mid-creation. Session start is now synchronous and the placeholder is never surfaced.

- Bumps the native Android SDK to `io.last9:rum-android:1.1.9`, which carries the same placeholder fix. Android's session start was already synchronous, so it never returned a missing session id.
- There are no Dart-side changes; Flutter apps pick this up automatically.

### v1.1.8 (2026-07-27)

Version parity release. Bumps the native Android SDK and iOS pod dependency to `1.1.8`. There are no Dart-side changes.

### v1.1.7 (2026-07-27)

**AGP / AndroidX consumer compatibility on Android.** Bumps native Android SDK to `io.last9:rum-android:1.1.7`, which removes the OpenTelemetry Android agent and with it 67 transitive AndroidX modules — including `androidx.core` 1.17.0, whose AAR metadata requires AGP 8.9.1+. Flutter Android apps on an older toolchain were failing the AAR metadata check with no fix short of a full toolchain upgrade. Picked up automatically; `androidx.core` now resolves to whatever the consumer app declares.

- No instrumentation is lost — the removed agent's auto-instrumentation was never activated by the SDK.
- Bumps the iOS pod to `Last9RUM 1.1.7` (version parity; no functional iOS changes).
- Plugin version aligned to `1.1.7` across `pubspec.yaml`, the Android library, and the iOS podspec. There are no Dart-side changes.

### v1.1.6 (2026-07-22)

Version parity release. Bumps native Android/iOS SDK references to `1.1.6`. No Dart-side changes.

### v1.1.5 (2026-07-20)

**Fix iOS `pod install` deployment-target mismatch.** The plugin declared iOS `13.0` while depending on a core pod that requires `15.1`, which could fail `pod install` for consumer apps; the plugin's minimum deployment target is now `15.1`. A full Flutter iOS consumer build now runs as a required CI gate on every release. Bundles native Android/iOS RUM `1.1.5`. No Dart-side changes.

### v1.1.4 (2026-07-20)

Bundles native Android RUM `1.1.4` (Kotlin 1.9.x consumer compatibility — `okhttp`/`okio` Kotlin 2.2 metadata no longer reaches the consumer compile classpath). No Dart-side changes.

### v1.1.3 (2026-07-15)

Bundles native Android RUM `1.1.3` (warm/hot app-start classification fix). No Dart-side changes.

### v1.1.2 (2026-07-15)

Version parity release. Bumps native Android/iOS SDK references to `1.1.2`. No Dart-side changes.

### v1.1.1 (2026-07-15)

Version parity release. Bumps native Android/iOS SDK references to `1.1.1`. Flutter was never affected by the underlying Android interceptor crash: it doesn't install the native OkHttp interceptor.

### v1.1.0 (2026-07-13)

**Add GraphQL Layer 1 + Layer 2 network observability.** `L9HttpClient` captures request bodies for native span resolution; GraphQL spans named `GraphQL: "{operationName}" {type}`. **Layer 2:** `L9DioInterceptor` (Dio) and `L9GqlLink` (gql/graphql) with opt-in `captureErrorMessages`/`captureVariables`.

### v1.0.0 (2026-07-09)

**`addEvent` dual-emits an OTLP log** (inherited from native). Flutter bridge unchanged. Bundles native Android/iOS RUM `1.0.0`.

### v0.9.0 (2026-07-02)

**Embedded per-flow lifecycle.** `L9Rum.shutdown()` and `L9Rum.isActive()` bridged from native. Requires native iOS/Android SDK `0.9.0`.

```dart
await L9Rum.initialize(config);
await L9Rum.spanAttributes(<String, dynamic>{'tenant.id': 'acme', 'feature.flag': 'beta'});

if (await L9Rum.isActive()) {
  await L9Rum.shutdown();
}
```

### v0.8.0 (2026-06-17)

**Fix Client-ID per-install device identity.** Handled by the native iOS/Android SDKs; no Flutter changes required.

### v0.7.1 (2026-06-04)

**Fix unknown error types.** Infers `exception.type` from Dart runtime types for primitive/non-`Error` payloads.

### v0.7.0 (2026-05-22)

**Add network phase child spans.** Native interception enabled by default on Android/iOS bridges; Dart `HttpClient` fallback emits a best-effort `ttfb` phase when native isn't available.

### v0.6.1 (2026-05-21)

Patch release. Version sync across SDKs.

### v0.6.0 (2026-05-20)

**Add Time To Full Display.** Native `view.ttfd` reporting for Flutter network requests.

### v0.5.0 (2026-05-19)

**WebView session correlation.** `L9Rum.getWebViewInjectedJavaScript()` writes native `session.id`/`native.view.id` into the WebView; pair with the host plugin's navigation callback (e.g. `webview_flutter`'s `onPageStarted`). `webViewAutoLoadBrowserRum: true` auto-loads Browser RUM into the WebView.

### v0.4.0 (2026-05-12)

**Add `ignorePatterns` for network tracking exclusions.** Skips noisy `dart:io` HTTP requests before span creation.

### v0.3.1 (2026-04-29)

**Add per-request trace isolation and W3C Baggage propagation.** Bridged to native Android/iOS.

### v0.3.0 (2026-04-22)

**Add Flutter navigation and runtime monitoring support.** `L9NavigationObserver`, `HttpOverrides` instrumentation, native resource monitoring and Android ANR detection exposed through Flutter config.

### v0.2.0 (2026-04-15)

**Add Flutter plugin bridge for mobile RUM APIs.** Core RUM APIs bridged to Android/iOS.

### v0.1.0 (2026-04-08)

Initial Flutter SDK release.
