@wireai/activation 0.13.6-next.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/AGENTS.md +21 -9
  2. package/CHANGELOG.md +258 -3
  3. package/INTEGRATION_PROMPT.md +7 -4
  4. package/README.md +41 -35
  5. package/dist/analytics/index.d.mts +9 -2
  6. package/dist/analytics/index.d.ts +9 -2
  7. package/dist/analytics/index.js +56 -12
  8. package/dist/analytics/index.js.map +1 -1
  9. package/dist/analytics/index.mjs +56 -12
  10. package/dist/analytics/index.mjs.map +1 -1
  11. package/dist/{currentSession-Cs3lweFZ.d.mts → currentSession-CUvTOchb.d.mts} +62 -7
  12. package/dist/{currentSession-DD6dKB0i.d.ts → currentSession-CW_5Mq4O.d.ts} +62 -7
  13. package/dist/index.d.mts +26 -6
  14. package/dist/index.d.ts +26 -6
  15. package/dist/index.js +635 -544
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +635 -544
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/questionnaire/index.js.map +1 -1
  20. package/dist/questionnaire/index.mjs.map +1 -1
  21. package/dist/reviews/index.js.map +1 -1
  22. package/dist/reviews/index.mjs.map +1 -1
  23. package/llms.txt +3 -3
  24. package/package.json +1 -1
  25. package/src/OnboardingFlow.tsx +23 -5
  26. package/src/WireOnboarding.tsx +4 -3
  27. package/src/activation/useWireActivation.ts +14 -1
  28. package/src/activation/wireActivation.ts +68 -2
  29. package/src/analytics/analyticsFacade.ts +24 -0
  30. package/src/analytics/eventQueue.ts +106 -11
  31. package/src/analytics/reportClientEvent.ts +31 -7
  32. package/src/analytics/useAnalytics.ts +17 -0
  33. package/src/context/deviceId.ts +10 -3
  34. package/src/permissions/permissionMemory.ts +12 -1
  35. package/src/session/persistedSession.ts +32 -8
  36. package/src/session-analytics/lifecycle.ts +26 -5
  37. package/src/session-analytics/reportSessionStart.ts +14 -10
  38. package/src/session-analytics/useLifecycleEvents.ts +70 -32
  39. package/src/session-analytics/useSessionStart.ts +57 -15
  40. package/src/types.ts +43 -8
  41. package/src/utils/readPlan.ts +26 -10
  42. package/src/utils/readProgress.ts +5 -0
@@ -472,10 +472,11 @@ type OnboardingResult = {
472
472
  /** The raw message thread, for custom downstream parsing. */
473
473
  raw: Message[];
474
474
  /**
475
- * The backend's onboarding plan, when it sent one. Present ONLY on the AI path: a tenant running
476
- * the static flow, or any run the server finished without a plan, leaves this `undefined` AND
477
- * leaves the key off the result object entirely so a host written before plans existed sees
478
- * byte-identically what it always saw.
475
+ * The backend's onboarding plan, present when the backend sent one the kit never infers it
476
+ * from which flow ran. Whether a plan arrives is the backend's configuration, not the kit's. Any
477
+ * run the server finished without a plan leaves this `undefined` AND leaves the key off the
478
+ * result object entirely — so a host written before plans existed sees byte-identically what it
479
+ * always saw.
479
480
  *
480
481
  * ⚠️ The kit does NOT interpret this and does NOT validate it. It checks one structural fact (a
481
482
  * plan is an object) and hands the payload straight through, unread, unlogged, and never attached
@@ -484,6 +485,16 @@ type OnboardingResult = {
484
485
  * the host's decision, not the kit's.
485
486
  */
486
487
  plan?: unknown;
488
+ /**
489
+ * The experiment ARM the backend assigned this session, when it is running one. Absent when
490
+ * the tenant runs no experiment, which is the common case, and absent is NOT an error.
491
+ * The kit does NOT interpret it, does not log it, does not brand it and never attaches it
492
+ * to an analytics event. What an arm key MEANS is the host's decision.
493
+ *
494
+ * Like {@link plan}, the KEY is set only when an arm was actually seen, so a run with no
495
+ * experiment leaves the result byte-identical to what every host already reads.
496
+ */
497
+ variant?: string;
487
498
  };
488
499
  /**
489
500
  * Lifecycle events emitted as the flow runs, for host-side analytics. The kit owns
@@ -495,7 +506,11 @@ type OnboardingResult = {
495
506
  * - `resumed`: a persisted session was restored after an app kill (fires INSTEAD of
496
507
  * `started`, so host funnels don't double-count the same session). Also carries
497
508
  * `contextId`. Requires the `storage` prop.
498
- * - `turn`: a new assistant card arrived (`step` = 1-based index of cards seen).
509
+ * - `turn`: a new assistant card arrived (`step` = 1-based index of cards seen). Carries
510
+ * `variant`, the backend's experiment arm for this session, from the first card that
511
+ * declared one onwards — this is how the arm is available DURING the flow rather than
512
+ * only at completion. The key is absent whenever no arm has been seen, which is every
513
+ * turn of every tenant running no experiment.
499
514
  * - `error`: the backend errored or the first-card watchdog timed out.
500
515
  * - `retry`: a transient failure is being auto-retried (`attempt` = 1-based).
501
516
  * - `fallback`: retries are exhausted; the kit degraded to the static `fallbackFlow`
@@ -516,6 +531,7 @@ type OnboardingEvent = {
516
531
  type: "turn";
517
532
  step: number;
518
533
  component?: string;
534
+ variant?: string;
519
535
  } | {
520
536
  type: "error";
521
537
  reason: "backend" | "timeout";
@@ -654,10 +670,19 @@ type WireOnboardingProps = {
654
670
  *
655
671
  * ```tsx
656
672
  * <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
657
- * // no device id of your own? read the kit's:
658
- * <WireOnboarding userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} ... />
673
+ * // no device id of your own? pass `storage` and leave this prop alone — the kit injects its
674
+ * // own key, and only after it has confirmed the key actually persists (see `autoJoinKey`).
675
+ * <WireOnboarding config={{ ...config, storage }} ... />
659
676
  * ```
660
677
  *
678
+ * ⛔ Do NOT hand-build the auto key with `activationJoinContext(resolveAutoDeviceKey({...}))`.
679
+ * `resolveAutoDeviceKey` is SYNCHRONOUS by contract: it hands back a freshly minted id and adopts
680
+ * the persisted one a storage read later, and it cannot tell you whether the id survives the
681
+ * launch at all. A key that differs on every launch corrupts `min_sessions` rather than merely
682
+ * leaving the join empty. The auto-join path does the awaited, durability-checked read for you;
683
+ * a host that genuinely wants the value in hand should await `hydrateDeviceIdentity` and refuse a
684
+ * `durable: false` record, which is exactly what the kit does internally.
685
+ *
661
686
  * SINCE 0.12.2, leaving it out no longer silently empties the funnel: when you pass `storage` and
662
687
  * this prop carries no `device_key`, the kit injects its OWN per-install key — the same one the
663
688
  * analytics surfaces mint and persist — so the default wiring joins. Anything you DO pass wins
@@ -790,6 +815,17 @@ type OnboardingProgress = {
790
815
  slot_id?: string;
791
816
  /** Whether the CURRENT screen may be skipped (backend-marked; default false → no Skip shown). */
792
817
  skippable?: boolean;
818
+ /**
819
+ * The EXPERIMENT ARM the backend assigned this session, when the tenant is running one. Rides the
820
+ * render envelope as a sibling of {@link step}/{@link total}/{@link key}/{@link slot_id}, and is
821
+ * OMITTED entirely for a tenant running no experiment — which is the common case, and is not an
822
+ * error. The assignment is sticky for the session, so a later card that omits it does not
823
+ * un-assign it.
824
+ *
825
+ * The kit does NOT interpret this. It is whitelisted, latched and handed to the host exactly as
826
+ * received; what an arm key MEANS is the host's decision. See {@link OnboardingResult.variant}.
827
+ */
828
+ variant?: string;
793
829
  };
794
830
 
795
831
  /**
@@ -958,6 +994,19 @@ type EventQueue = {
958
994
  notifyOnline(): void;
959
995
  /** Current pending (in-memory) count. */
960
996
  size(): number;
997
+ /**
998
+ * Tear this queue down when its owner goes away (a hook's effect cleanup, a host disposing its
999
+ * own instance). Idempotent, never throws, and REQUIRED for any queue that can be re-created:
1000
+ * a remount builds a second queue while the first is still holding the storage claim and a live
1001
+ * backoff timer, and the two then fight over one persisted slot.
1002
+ *
1003
+ * It clears the retry timer, releases the claimed storage key so a replacement gets the SAME slot
1004
+ * instead of a rotated `…#2` nobody reads next launch, and makes the queue inert — `enqueue`,
1005
+ * `flush` and every write become no-ops, so a timer that already fired cannot `removeItem` the
1006
+ * slot the live queue owns. Anything still buffered in memory at that point stays on disk under
1007
+ * the released key, which is exactly where the replacement queue looks for it.
1008
+ */
1009
+ dispose(): void;
961
1010
  };
962
1011
  /** Test-only: forget every claimed queue key. A real RELAUNCH is a new process, so a test that
963
1012
  * simulates one in-process must call this or its second queue reads as a concurrent sibling.
@@ -1349,6 +1398,12 @@ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => stri
1349
1398
  *
1350
1399
  * Never throws or rejects: a missing, hung, or rejecting adapter resolves to the in-memory id, and
1351
1400
  * with no `storage` it resolves immediately (there is nothing to hydrate from).
1401
+ *
1402
+ * ⚠️ IT RETURNS A BARE STRING, so it CANNOT say whether the id survives the launch — a degraded
1403
+ * adapter resolves to the in-memory mint and reads identically to a persisted one. No kit surface
1404
+ * uses it any more (0.14.0 moved the two lifecycle hooks off it): a caller writing a cross-launch
1405
+ * join key wants {@link hydrateDeviceIdentity} and its `durable` flag. Kept as public API for a host
1406
+ * that only wants "the id", never as the way to decide whether to stamp one.
1352
1407
  */
1353
1408
  declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Promise<string>;
1354
1409
  /**
@@ -472,10 +472,11 @@ type OnboardingResult = {
472
472
  /** The raw message thread, for custom downstream parsing. */
473
473
  raw: Message[];
474
474
  /**
475
- * The backend's onboarding plan, when it sent one. Present ONLY on the AI path: a tenant running
476
- * the static flow, or any run the server finished without a plan, leaves this `undefined` AND
477
- * leaves the key off the result object entirely so a host written before plans existed sees
478
- * byte-identically what it always saw.
475
+ * The backend's onboarding plan, present when the backend sent one the kit never infers it
476
+ * from which flow ran. Whether a plan arrives is the backend's configuration, not the kit's. Any
477
+ * run the server finished without a plan leaves this `undefined` AND leaves the key off the
478
+ * result object entirely — so a host written before plans existed sees byte-identically what it
479
+ * always saw.
479
480
  *
480
481
  * ⚠️ The kit does NOT interpret this and does NOT validate it. It checks one structural fact (a
481
482
  * plan is an object) and hands the payload straight through, unread, unlogged, and never attached
@@ -484,6 +485,16 @@ type OnboardingResult = {
484
485
  * the host's decision, not the kit's.
485
486
  */
486
487
  plan?: unknown;
488
+ /**
489
+ * The experiment ARM the backend assigned this session, when it is running one. Absent when
490
+ * the tenant runs no experiment, which is the common case, and absent is NOT an error.
491
+ * The kit does NOT interpret it, does not log it, does not brand it and never attaches it
492
+ * to an analytics event. What an arm key MEANS is the host's decision.
493
+ *
494
+ * Like {@link plan}, the KEY is set only when an arm was actually seen, so a run with no
495
+ * experiment leaves the result byte-identical to what every host already reads.
496
+ */
497
+ variant?: string;
487
498
  };
488
499
  /**
489
500
  * Lifecycle events emitted as the flow runs, for host-side analytics. The kit owns
@@ -495,7 +506,11 @@ type OnboardingResult = {
495
506
  * - `resumed`: a persisted session was restored after an app kill (fires INSTEAD of
496
507
  * `started`, so host funnels don't double-count the same session). Also carries
497
508
  * `contextId`. Requires the `storage` prop.
498
- * - `turn`: a new assistant card arrived (`step` = 1-based index of cards seen).
509
+ * - `turn`: a new assistant card arrived (`step` = 1-based index of cards seen). Carries
510
+ * `variant`, the backend's experiment arm for this session, from the first card that
511
+ * declared one onwards — this is how the arm is available DURING the flow rather than
512
+ * only at completion. The key is absent whenever no arm has been seen, which is every
513
+ * turn of every tenant running no experiment.
499
514
  * - `error`: the backend errored or the first-card watchdog timed out.
500
515
  * - `retry`: a transient failure is being auto-retried (`attempt` = 1-based).
501
516
  * - `fallback`: retries are exhausted; the kit degraded to the static `fallbackFlow`
@@ -516,6 +531,7 @@ type OnboardingEvent = {
516
531
  type: "turn";
517
532
  step: number;
518
533
  component?: string;
534
+ variant?: string;
519
535
  } | {
520
536
  type: "error";
521
537
  reason: "backend" | "timeout";
@@ -654,10 +670,19 @@ type WireOnboardingProps = {
654
670
  *
655
671
  * ```tsx
656
672
  * <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
657
- * // no device id of your own? read the kit's:
658
- * <WireOnboarding userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} ... />
673
+ * // no device id of your own? pass `storage` and leave this prop alone — the kit injects its
674
+ * // own key, and only after it has confirmed the key actually persists (see `autoJoinKey`).
675
+ * <WireOnboarding config={{ ...config, storage }} ... />
659
676
  * ```
660
677
  *
678
+ * ⛔ Do NOT hand-build the auto key with `activationJoinContext(resolveAutoDeviceKey({...}))`.
679
+ * `resolveAutoDeviceKey` is SYNCHRONOUS by contract: it hands back a freshly minted id and adopts
680
+ * the persisted one a storage read later, and it cannot tell you whether the id survives the
681
+ * launch at all. A key that differs on every launch corrupts `min_sessions` rather than merely
682
+ * leaving the join empty. The auto-join path does the awaited, durability-checked read for you;
683
+ * a host that genuinely wants the value in hand should await `hydrateDeviceIdentity` and refuse a
684
+ * `durable: false` record, which is exactly what the kit does internally.
685
+ *
661
686
  * SINCE 0.12.2, leaving it out no longer silently empties the funnel: when you pass `storage` and
662
687
  * this prop carries no `device_key`, the kit injects its OWN per-install key — the same one the
663
688
  * analytics surfaces mint and persist — so the default wiring joins. Anything you DO pass wins
@@ -790,6 +815,17 @@ type OnboardingProgress = {
790
815
  slot_id?: string;
791
816
  /** Whether the CURRENT screen may be skipped (backend-marked; default false → no Skip shown). */
792
817
  skippable?: boolean;
818
+ /**
819
+ * The EXPERIMENT ARM the backend assigned this session, when the tenant is running one. Rides the
820
+ * render envelope as a sibling of {@link step}/{@link total}/{@link key}/{@link slot_id}, and is
821
+ * OMITTED entirely for a tenant running no experiment — which is the common case, and is not an
822
+ * error. The assignment is sticky for the session, so a later card that omits it does not
823
+ * un-assign it.
824
+ *
825
+ * The kit does NOT interpret this. It is whitelisted, latched and handed to the host exactly as
826
+ * received; what an arm key MEANS is the host's decision. See {@link OnboardingResult.variant}.
827
+ */
828
+ variant?: string;
793
829
  };
794
830
 
795
831
  /**
@@ -958,6 +994,19 @@ type EventQueue = {
958
994
  notifyOnline(): void;
959
995
  /** Current pending (in-memory) count. */
960
996
  size(): number;
997
+ /**
998
+ * Tear this queue down when its owner goes away (a hook's effect cleanup, a host disposing its
999
+ * own instance). Idempotent, never throws, and REQUIRED for any queue that can be re-created:
1000
+ * a remount builds a second queue while the first is still holding the storage claim and a live
1001
+ * backoff timer, and the two then fight over one persisted slot.
1002
+ *
1003
+ * It clears the retry timer, releases the claimed storage key so a replacement gets the SAME slot
1004
+ * instead of a rotated `…#2` nobody reads next launch, and makes the queue inert — `enqueue`,
1005
+ * `flush` and every write become no-ops, so a timer that already fired cannot `removeItem` the
1006
+ * slot the live queue owns. Anything still buffered in memory at that point stays on disk under
1007
+ * the released key, which is exactly where the replacement queue looks for it.
1008
+ */
1009
+ dispose(): void;
961
1010
  };
962
1011
  /** Test-only: forget every claimed queue key. A real RELAUNCH is a new process, so a test that
963
1012
  * simulates one in-process must call this or its second queue reads as a concurrent sibling.
@@ -1349,6 +1398,12 @@ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => stri
1349
1398
  *
1350
1399
  * Never throws or rejects: a missing, hung, or rejecting adapter resolves to the in-memory id, and
1351
1400
  * with no `storage` it resolves immediately (there is nothing to hydrate from).
1401
+ *
1402
+ * ⚠️ IT RETURNS A BARE STRING, so it CANNOT say whether the id survives the launch — a degraded
1403
+ * adapter resolves to the in-memory mint and reads identically to a persisted one. No kit surface
1404
+ * uses it any more (0.14.0 moved the two lifecycle hooks off it): a caller writing a cross-launch
1405
+ * join key wants {@link hydrateDeviceIdentity} and its `durable` flag. Kept as public API for a host
1406
+ * that only wants "the id", never as the way to decide whether to stamp one.
1352
1407
  */
1353
1408
  declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Promise<string>;
1354
1409
  /**
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, U as PermissionPlacement, c as ClientEvent, g as EnvelopeSource } from './currentSession-Cs3lweFZ.mjs';
4
- export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-Cs3lweFZ.mjs';
3
+ import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-CUvTOchb.mjs';
4
+ export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-CUvTOchb.mjs';
5
5
  import { O as OnboardingTheme } from './types-BKfpdZzX.mjs';
6
6
  export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.mjs';
7
7
  export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-C3qQBHsA.mjs';
@@ -1286,6 +1286,8 @@ type PartialProgress = {
1286
1286
  /** The stable per-slot identity, when the backend sends one. See `OnboardingProgress.slot_id`. */
1287
1287
  slot_id?: string;
1288
1288
  skippable?: boolean;
1289
+ /** The experiment arm assigned to this session, when the tenant runs one. See `OnboardingProgress.variant`. */
1290
+ variant?: string;
1289
1291
  };
1290
1292
  declare const readProgress: (response?: WireAIResponse) => PartialProgress;
1291
1293
 
@@ -1570,6 +1572,15 @@ type WireActivationConfig = {
1570
1572
  appVersion?: string;
1571
1573
  /** Host persistence (AsyncStorage-compatible subset) so the auto-minted device key survives launches. */
1572
1574
  storage?: WireOnboardingStorage;
1575
+ /**
1576
+ * Where a FAILED `track()` goes instead of being dropped: your existing offline queue's `enqueue`
1577
+ * (the same wiring `useLifecycleEvents` takes). Pass it when you already run one queue for the app
1578
+ * and you want one backlog, one retry schedule, one storage slot.
1579
+ *
1580
+ * Omit it and the instance opens its OWN queue the first time a POST fails — never before, so an
1581
+ * app that is online the whole time allocates nothing. A successful `track()` never touches either.
1582
+ */
1583
+ sink?: (event: ClientEvent) => void;
1573
1584
  };
1574
1585
  /** The kit-owned activation surface. `sessionId` is a live getter (reads `getCurrentSessionId()`). */
1575
1586
  type WireActivation = {
@@ -1592,6 +1603,12 @@ type WireActivation = {
1592
1603
  subscribeRevalidation(listener: () => void): () => void;
1593
1604
  /** The current revalidation version — include in a decision-fetch effect's deps to re-fetch on bump. */
1594
1605
  getRevalidationVersion(): number;
1606
+ /**
1607
+ * Tear down the durability queue a failed `track()` may have opened (see
1608
+ * {@link EventQueue.dispose}). Idempotent, never throws, and a no-op on an instance that never had
1609
+ * a failure. Call it when you build an instance per mount; `useWireActivation` does it for you.
1610
+ */
1611
+ dispose(): void;
1595
1612
  };
1596
1613
  /**
1597
1614
  * Create a bound activation instance for a tenant transport. Resolves the device key once (explicit >
@@ -2031,8 +2048,9 @@ interface SessionStartConfig {
2031
2048
  appVersion?: string;
2032
2049
  /** Tenant/app id — namespaces the auto `device_key` fallback below. */
2033
2050
  appId?: string;
2034
- /** Host storage (AsyncStorage subset). Present → `app.session_started` falls back to the kit's
2035
- * persisted auto `device_key` when the host passes none. Absent no fallback (see below). */
2051
+ /** Host storage (AsyncStorage subset). Present AND actually persisting → `app.session_started`
2052
+ * falls back to the kit's persisted auto `device_key` when the host passes none. Absent, or an
2053
+ * adapter that throws / rejects → no fallback (a per-launch key is worse than none, see below). */
2036
2054
  storage?: WireOnboardingStorage;
2037
2055
  }
2038
2056
  /** Per-open identity the host supplies. All optional: a pre-auth open is device-only. */
@@ -2165,8 +2183,10 @@ interface LifecycleConfig {
2165
2183
  appVersion?: string;
2166
2184
  /** Tenant/app id — namespaces the first-open flag AND the hook's internal queue storage key. */
2167
2185
  appId?: string;
2168
- /** Host storage (AsyncStorage subset). Enables the persisted once-ever first-open flag AND the
2169
- * offline durability of the hook's internal queue. Omit it and both degrade to in-memory. */
2186
+ /** Host storage (AsyncStorage subset). Enables the persisted once-ever first-open flag, the
2187
+ * offline durability of the hook's internal queue, AND the auto `device_key` fallback the last
2188
+ * one only when the adapter actually persists (one that throws or rejects gets no fallback, the
2189
+ * same verdict as no storage at all). Omit it and all three degrade to in-memory. */
2170
2190
  storage?: WireOnboardingStorage;
2171
2191
  }
2172
2192
  /** Per-open identity + wiring the host supplies. All optional: a pre-auth open is device-only. */
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, U as PermissionPlacement, c as ClientEvent, g as EnvelopeSource } from './currentSession-DD6dKB0i.js';
4
- export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-DD6dKB0i.js';
3
+ import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, C as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, c as ClientEvent, U as PermissionPlacement, g as EnvelopeSource } from './currentSession-CW_5Mq4O.js';
4
+ export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, b as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-CW_5Mq4O.js';
5
5
  import { O as OnboardingTheme } from './types-BKfpdZzX.js';
6
6
  export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.js';
7
7
  export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-Cdgns6--.js';
@@ -1286,6 +1286,8 @@ type PartialProgress = {
1286
1286
  /** The stable per-slot identity, when the backend sends one. See `OnboardingProgress.slot_id`. */
1287
1287
  slot_id?: string;
1288
1288
  skippable?: boolean;
1289
+ /** The experiment arm assigned to this session, when the tenant runs one. See `OnboardingProgress.variant`. */
1290
+ variant?: string;
1289
1291
  };
1290
1292
  declare const readProgress: (response?: WireAIResponse) => PartialProgress;
1291
1293
 
@@ -1570,6 +1572,15 @@ type WireActivationConfig = {
1570
1572
  appVersion?: string;
1571
1573
  /** Host persistence (AsyncStorage-compatible subset) so the auto-minted device key survives launches. */
1572
1574
  storage?: WireOnboardingStorage;
1575
+ /**
1576
+ * Where a FAILED `track()` goes instead of being dropped: your existing offline queue's `enqueue`
1577
+ * (the same wiring `useLifecycleEvents` takes). Pass it when you already run one queue for the app
1578
+ * and you want one backlog, one retry schedule, one storage slot.
1579
+ *
1580
+ * Omit it and the instance opens its OWN queue the first time a POST fails — never before, so an
1581
+ * app that is online the whole time allocates nothing. A successful `track()` never touches either.
1582
+ */
1583
+ sink?: (event: ClientEvent) => void;
1573
1584
  };
1574
1585
  /** The kit-owned activation surface. `sessionId` is a live getter (reads `getCurrentSessionId()`). */
1575
1586
  type WireActivation = {
@@ -1592,6 +1603,12 @@ type WireActivation = {
1592
1603
  subscribeRevalidation(listener: () => void): () => void;
1593
1604
  /** The current revalidation version — include in a decision-fetch effect's deps to re-fetch on bump. */
1594
1605
  getRevalidationVersion(): number;
1606
+ /**
1607
+ * Tear down the durability queue a failed `track()` may have opened (see
1608
+ * {@link EventQueue.dispose}). Idempotent, never throws, and a no-op on an instance that never had
1609
+ * a failure. Call it when you build an instance per mount; `useWireActivation` does it for you.
1610
+ */
1611
+ dispose(): void;
1595
1612
  };
1596
1613
  /**
1597
1614
  * Create a bound activation instance for a tenant transport. Resolves the device key once (explicit >
@@ -2031,8 +2048,9 @@ interface SessionStartConfig {
2031
2048
  appVersion?: string;
2032
2049
  /** Tenant/app id — namespaces the auto `device_key` fallback below. */
2033
2050
  appId?: string;
2034
- /** Host storage (AsyncStorage subset). Present → `app.session_started` falls back to the kit's
2035
- * persisted auto `device_key` when the host passes none. Absent no fallback (see below). */
2051
+ /** Host storage (AsyncStorage subset). Present AND actually persisting → `app.session_started`
2052
+ * falls back to the kit's persisted auto `device_key` when the host passes none. Absent, or an
2053
+ * adapter that throws / rejects → no fallback (a per-launch key is worse than none, see below). */
2036
2054
  storage?: WireOnboardingStorage;
2037
2055
  }
2038
2056
  /** Per-open identity the host supplies. All optional: a pre-auth open is device-only. */
@@ -2165,8 +2183,10 @@ interface LifecycleConfig {
2165
2183
  appVersion?: string;
2166
2184
  /** Tenant/app id — namespaces the first-open flag AND the hook's internal queue storage key. */
2167
2185
  appId?: string;
2168
- /** Host storage (AsyncStorage subset). Enables the persisted once-ever first-open flag AND the
2169
- * offline durability of the hook's internal queue. Omit it and both degrade to in-memory. */
2186
+ /** Host storage (AsyncStorage subset). Enables the persisted once-ever first-open flag, the
2187
+ * offline durability of the hook's internal queue, AND the auto `device_key` fallback the last
2188
+ * one only when the adapter actually persists (one that throws or rejects gets no fallback, the
2189
+ * same verdict as no storage at all). Omit it and all three degrade to in-memory. */
2170
2190
  storage?: WireOnboardingStorage;
2171
2191
  }
2172
2192
  /** Per-open identity + wiring the host supplies. All optional: a pre-auth open is device-only. */