@wireai/activation 0.1.0 → 0.2.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 (57) hide show
  1. package/AGENTS.md +82 -40
  2. package/CHANGELOG.md +17 -2
  3. package/INTEGRATION_PROMPT.md +8 -8
  4. package/README.md +38 -38
  5. package/dist/analytics/index.d.mts +92 -0
  6. package/dist/analytics/index.d.ts +92 -0
  7. package/dist/analytics/index.js +439 -0
  8. package/dist/analytics/index.js.map +1 -0
  9. package/dist/analytics/index.mjs +426 -0
  10. package/dist/analytics/index.mjs.map +1 -0
  11. package/dist/coachmarks/index.js.map +1 -1
  12. package/dist/coachmarks/index.mjs.map +1 -1
  13. package/dist/decision-BzbiKwk3.d.mts +79 -0
  14. package/dist/decision-plDEOCkt.d.ts +79 -0
  15. package/dist/eventQueue-CxKi7Qd5.d.mts +546 -0
  16. package/dist/eventQueue-rV1dtJJR.d.ts +546 -0
  17. package/dist/index.d.mts +162 -418
  18. package/dist/index.d.ts +162 -418
  19. package/dist/index.js +407 -4
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +402 -5
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/questionnaire/index.d.mts +2 -1
  24. package/dist/questionnaire/index.d.ts +2 -1
  25. package/dist/questionnaire/index.js.map +1 -1
  26. package/dist/questionnaire/index.mjs.map +1 -1
  27. package/dist/reviews/index.d.mts +5 -40
  28. package/dist/reviews/index.d.ts +5 -40
  29. package/dist/reviews/index.js.map +1 -1
  30. package/dist/reviews/index.mjs.map +1 -1
  31. package/dist/showcase/index.js.map +1 -1
  32. package/dist/showcase/index.mjs.map +1 -1
  33. package/dist/transport-BeO_Brcu.d.mts +40 -0
  34. package/dist/transport-DLpd1v5_.d.ts +40 -0
  35. package/dist/{decision-Cl8OFYzu.d.mts → types-A6pTxIZV.d.mts} +1 -77
  36. package/dist/{decision-CFvGY6nP.d.ts → types-BhpXJGlg.d.ts} +1 -77
  37. package/llms.txt +7 -7
  38. package/metro/index.d.ts +3 -3
  39. package/metro/index.js +3 -3
  40. package/package.json +15 -1
  41. package/src/analytics/contextEnvelope.ts +72 -0
  42. package/src/analytics/eventQueue.ts +331 -0
  43. package/src/analytics/index.ts +47 -0
  44. package/src/analytics/screenTracking.ts +122 -0
  45. package/src/analytics/useScreenTracking.ts +48 -0
  46. package/src/coachmarks/index.ts +2 -2
  47. package/src/features/WireFeaturesProvider.tsx +1 -1
  48. package/src/features/index.ts +2 -2
  49. package/src/index.ts +18 -1
  50. package/src/questionnaire/index.ts +2 -2
  51. package/src/reviews/index.ts +2 -2
  52. package/src/session-analytics/index.ts +20 -0
  53. package/src/session-analytics/lifecycle.ts +236 -0
  54. package/src/session-analytics/reportSessionStart.ts +27 -7
  55. package/src/session-analytics/useLifecycleEvents.ts +184 -0
  56. package/src/showcase/index.ts +2 -2
  57. package/src/types.ts +1 -1
package/dist/index.d.ts CHANGED
@@ -1,230 +1,14 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import * as wireai_rn from 'wireai-rn';
4
- import { Message, WireAIComponent, WireAIResponse } from 'wireai-rn';
3
+ import { k as WireOnboardingProps, l as WireOnboardingConfig, O as OnboardingResult, S as StepValidator, n as OnboardingEvent, o as OnboardingCopy, a as ClientEventTarget, D as DeviceContext, C as ClientEvent, E as EnvelopeSource } from './eventQueue-rV1dtJJR.js';
4
+ export { A as AnalyticsEvent, b as ClientEventType, p as DeviceFormFactor, q as OnboardingProgress, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, s as collectDeviceContext, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from './eventQueue-rV1dtJJR.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
- import { b as WireOnboardingStorage, a as WireFeaturesConfig, W as WireFeatures } from './types-CMuOexw0.js';
8
- export { D as DEFAULT_SESSION_TTL_MS, L as LoadedSession, O as OnboardingMode, c as clearPersistedSession, l as loadPersistedSession, p as peekPersistedSession, s as savePersistedSession, d as sessionStorageKey } from './types-CMuOexw0.js';
9
7
  export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-Cdgns6--.js';
10
- import { Platform } from 'react-native';
11
-
12
- /** Transport + tenant config for the managed Wire AI onboarding backend (A2A). */
13
- type WireOnboardingConfig = {
14
- /** Tenant API key (resolves the app server-side). */
15
- apiKey: string;
16
- /** Base server URL; the kit appends `/a2a`. */
17
- serverUrl: string;
18
- /** App id — passed as the A2A `model` (informational; the key resolves the app). */
19
- appId: string;
20
- /**
21
- * Extra metadata merged into every A2A request (e.g. install attribution).
22
- * NOTE: the kit reserves `sessionId` (correlation seed) and `supportedComponents`
23
- * (the renderable card names this device advertises to the backend) — it sets both
24
- * automatically, so don't override them here.
25
- */
26
- metadata?: Record<string, unknown>;
27
- /**
28
- * Host app version string (e.g. "1.4.2"). HOST-INJECTED — the kit adds no dependency to
29
- * read it; hosts typically pass it from `expo-constants`
30
- * (`Constants.expoConfig?.version`). Forwarded to the backend on the session metadata and
31
- * on client events (merged into the `device` snapshot as `device.appVersion`) so analytics
32
- * can segment the funnel by app version. Optional; omit if unknown.
33
- */
34
- appVersion?: string;
35
- };
36
- /**
37
- * A per-step validator, keyed by a base-question `key` (e.g. `username`). Runs
38
- * before the answer is sent to the backend. Return `{ ok: false, error }` to
39
- * block advancing and show the error inline on the card.
40
- */
41
- type StepValidator = (value: string) => Promise<{
42
- ok: boolean;
43
- error?: string;
44
- }>;
45
- /** Result handed to `onComplete` when the flow reaches its terminal StatusCard. */
46
- type OnboardingResult = {
47
- /** Question-key → captured value, derived from the thread. */
48
- answers: Record<string, unknown>;
49
- /** The raw message thread, for custom downstream parsing. */
50
- raw: Message[];
51
- };
52
- /**
53
- * Lifecycle events emitted as the flow runs, for host-side analytics. The kit owns
54
- * the thread loop, so this is how a host recovers per-turn telemetry it used to get
55
- * by driving the loop itself.
56
- * - `started`: the very first message was sent to the backend. Carries `contextId` (the
57
- * A2A session id). Capture it if you may need to bind a user AFTER the flow
58
- * finishes (see `identifyOnboarding` and the `userId` prop).
59
- * - `resumed`: a persisted session was restored after an app kill (fires INSTEAD of
60
- * `started`, so host funnels don't double-count the same session). Also carries
61
- * `contextId`. Requires the `storage` prop.
62
- * - `turn`: a new assistant card arrived (`step` = 1-based index of cards seen).
63
- * - `error`: the backend errored or the first-card watchdog timed out.
64
- * - `retry`: a transient failure is being auto-retried (`attempt` = 1-based).
65
- * - `fallback`: retries are exhausted; the kit degraded to the static `fallbackFlow`
66
- * (or handed off to `onError`). This is the client-side mirror of the
67
- * backend's `llm_fallback` reliability event.
68
- */
69
- type OnboardingEvent = {
70
- type: "started";
71
- contextId: string;
72
- } | {
73
- type: "resumed";
74
- contextId: string;
75
- } | {
76
- type: "turn";
77
- step: number;
78
- component?: string;
79
- } | {
80
- type: "error";
81
- reason: "backend" | "timeout";
82
- } | {
83
- type: "retry";
84
- reason: "backend" | "timeout";
85
- attempt: number;
86
- } | {
87
- type: "fallback";
88
- reason: "backend" | "timeout";
89
- };
90
- /**
91
- * Copy overrides for the kit's built-in (English) strings, so a host can localize
92
- * the loaders / completion fallback via its own i18n. Any field left unset keeps
93
- * the kit default.
94
- */
95
- type OnboardingCopy = {
96
- /** First "thinking" screen, before any card arrives. */
97
- startingTitle: string;
98
- startingHint: string;
99
- /** While the persisted session id is being restored from `storage` (pre-mount gate). */
100
- restoringTitle: string;
101
- restoringHint: string;
102
- /** Between-turns loader, while the next card is generated. */
103
- thinkingTitle: string;
104
- thinkingHint: string;
105
- /** While a per-step validator is running. */
106
- checkingTitle: string;
107
- checkingHint: string;
108
- /** Completion fallback when the backend omits a title/CTA. */
109
- completeTitle: string;
110
- completeCta: string;
111
- };
112
- type WireOnboardingProps = {
113
- config: WireOnboardingConfig;
114
- /** Partial theme merged over the neutral default. */
115
- theme?: Partial<OnboardingTheme>;
116
- /** Override the registered cards (defaults to the kit's `onboardingComponents`). */
117
- components?: wireai_rn.WireAIComponent[];
118
- /**
119
- * App-supplied artwork for InterstitialCard, keyed by name. The backend names one
120
- * via `illustration`; the kit slots the matching node (keeping itself dependency-free).
121
- */
122
- illustrations?: Record<string, React.ReactNode>;
123
- /** Per-step validators keyed by base-question key, e.g. `{ username: checkUsername }`. */
124
- validators?: Record<string, StepValidator>;
125
- /** Fired once the flow reaches its terminal StatusCard. */
126
- onComplete: (result: OnboardingResult) => void;
127
- /**
128
- * Retained for back-compat. NOTE: per-question Skip is now INTERNAL — the kit shows
129
- * a Skip control only on screens the backend marks `skippable`, and it advances ONE
130
- * question (it does not exit the flow). This callback is no longer wired to that control.
131
- */
132
- onSkip?: () => void;
133
- /**
134
- * Fired on a backend error or first-card timeout, AFTER retries are exhausted.
135
- * When `fallbackFlow` is NOT supplied and this is provided, the host owns recovery
136
- * (e.g. navigate to its own static onboarding) instead of the kit's inline retry.
137
- */
138
- onError?: (err: unknown) => void;
139
- /**
140
- * Your predefined STATIC onboarding, rendered in place when the AI flow fails and
141
- * retries are exhausted — so a generation/backend/timeout error degrades to your
142
- * static flow instead of breaking the app. Takes precedence over `onError`. This
143
- * is the "it can never break your onboarding" guarantee: supply the same flow you
144
- * shipped before adding Wire AI and the user always keeps moving.
145
- */
146
- fallbackFlow?: React.ReactNode;
147
- /**
148
- * Consecutive failures to auto-retry before degrading to `fallbackFlow`/`onError`.
149
- * Default 1 (one silent retry, then degrade). 0 = degrade on the first failure.
150
- */
151
- maxRetries?: number;
152
- /** Lifecycle hook for host-side analytics (started / per-turn / error). */
153
- onEvent?: (event: OnboardingEvent) => void;
154
- /**
155
- * Host-injected, non-PII context the app already knows about the user — signup method,
156
- * referral source, plan tier, a HASHED user id, etc. Same host-injection philosophy as
157
- * `storage`: the kit collects nothing here; the host passes what it wants. Forwarded to the
158
- * backend on the session metadata AND on client events so analytics can segment the funnel.
159
- *
160
- * MUST NOT contain PII such as raw emails, names, or phone numbers — pass a hash if you need
161
- * a user key. Values are limited to primitives (`string | number | boolean`); the server caps
162
- * key count / size and drops deep nesting. Old servers ignore it (backward compatible).
163
- */
164
- userContext?: Record<string, string | number | boolean>;
165
- /**
166
- * The host's own user id, so onboarding sessions can be reconciled to real users later
167
- * (console sessions ↔ your user table / GA4 users). First-class alongside `userContext`.
168
- *
169
- * An OPAQUE PSEUDONYMOUS string YOU own — your internal user id, NOT an email/name/phone.
170
- * Trimmed and capped at 128 chars (longer is truncated). **No PII.**
171
- *
172
- * LATE BINDING: users often register DURING or AFTER onboarding, so this is fully optional
173
- * and can arrive late:
174
- * - present at MOUNT → rides the A2A session-start metadata (server binds it at session start);
175
- * - CHANGES mid-session (e.g. the user just registered) → the kit emits an `identify` event
176
- * that attaches the id to the LIVE session;
177
- * - available only AFTER completion → capture the `contextId` from the `started`/`resumed`
178
- * `onEvent` while the flow runs, then call `identifyOnboarding({ contextId, userId })` once
179
- * the user registers. Completion clears the persisted session, so the captured `contextId`
180
- * is the reliable post-flow handle.
181
- *
182
- * Backward compatible: omit it and nothing changes; old servers ignore the extra field.
183
- */
184
- userId?: string;
185
- /** Localized overrides for the kit's built-in English strings. */
186
- copy?: Partial<OnboardingCopy>;
187
- /**
188
- * Approximate total number of screens, if known (e.g. the backend screen budget).
189
- * Paces the progress bar as `step / approxScreens` (capped, never shown as a
190
- * number) instead of the asymptotic default. A backend-supplied `progress.total`
191
- * takes precedence when present.
192
- */
193
- approxScreens?: number;
194
- /** First message that kicks off the backend flow. Default `"start"`. */
195
- startMessage?: string;
196
- /** Ms to wait for the first card before showing the error/retry state. Default 15000. */
197
- startTimeoutMs?: number;
198
- /**
199
- * Host-injected storage for session-id persistence (AsyncStorage-compatible subset:
200
- * pass `@react-native-async-storage/async-storage` as-is, or a small MMKV wrapper).
201
- * When set, the kit caches its session id so an app KILL mid-onboarding resumes the
202
- * SAME backend session instead of minting a new one — keeping the analytics funnel's
203
- * `started` count honest (no phantom drops). Omit for the previous per-mount behavior.
204
- * This persists the kit's own correlation seed only — never answers.
205
- */
206
- storage?: WireOnboardingStorage;
207
- /**
208
- * How long a persisted session id stays resumable, in ms. Default 3 600 000 (1h),
209
- * matching the backend's session TTL. Only meaningful with `storage`.
210
- */
211
- sessionTtlMs?: number;
212
- /**
213
- * Override the storage key (default `wireai:session:<config.appId>`). Scope it
214
- * per-user (e.g. append a user id) if one device can run onboarding for multiple
215
- * accounts mid-flow. Only meaningful with `storage`.
216
- */
217
- persistKey?: string;
218
- };
219
- /** Backend-supplied progress, read off `response.props.progress` when present. */
220
- type OnboardingProgress = {
221
- step: number;
222
- total: number;
223
- /** Base-question key for the CURRENT screen, when known (used to pick a validator). */
224
- key?: string;
225
- /** Whether the CURRENT screen may be skipped (backend-marked; default false → no Skip shown). */
226
- skippable?: boolean;
227
- };
8
+ import { WireAIComponent, Message, WireAIResponse } from 'wireai-rn';
9
+ import { a as WireFeaturesConfig, W as WireFeatures, b as WireOnboardingStorage } from './types-CMuOexw0.js';
10
+ export { D as DEFAULT_SESSION_TTL_MS, L as LoadedSession, O as OnboardingMode, c as clearPersistedSession, l as loadPersistedSession, p as peekPersistedSession, s as savePersistedSession, d as sessionStorageKey } from './types-CMuOexw0.js';
11
+ import 'react-native';
228
12
 
229
13
  /**
230
14
  * WireOnboarding — the public host component. Drop it into a screen and it runs
@@ -764,160 +548,6 @@ declare const InterstitialCard: WireAIComponent;
764
548
 
765
549
  declare const onboardingComponents: WireAIComponent[];
766
550
 
767
- /**
768
- * deviceContext — collect a small, privacy-label-neutral snapshot of the device so
769
- * onboarding analytics can segment the funnel (platform / form factor / locale) WITHOUT
770
- * adding a single dependency to the kit or changing a host app's App Privacy / Data Safety
771
- * declarations.
772
- *
773
- * HARD RULE (why this file has no imports beyond React Native built-ins):
774
- * The kit stays dependency-free. Everything here comes from `Platform`, `Dimensions`,
775
- * `I18nManager`, and the standard `Intl` global. There are NO advertising IDs, NO
776
- * `getUniqueId`/IDFA/GAID/fingerprinting APIs, and nothing that would require a new
777
- * privacy-label entry. A host can adopt this without touching its store declarations.
778
- *
779
- * DEFENSIVE BY DESIGN: `collectDeviceContext()` never throws. Every read is guarded and
780
- * a missing/unavailable field is simply omitted (Hermes may ship without full `Intl`,
781
- * `Platform.constants` differs per OS and RN version, etc.). Analytics must never be able
782
- * to break onboarding.
783
- */
784
-
785
- /** Coarse device class. iOS uses the reported interface idiom; else a screen-size heuristic. */
786
- type DeviceFormFactor = "phone" | "tablet";
787
- /**
788
- * A privacy-label-neutral device snapshot. ALL fields except `platform` are optional and are
789
- * omitted when unavailable. Nothing here identifies a user or device uniquely.
790
- */
791
- type DeviceContext = {
792
- /** `Platform.OS` — "ios" | "android" | "windows" | "macos" | "web". Always present. */
793
- platform: typeof Platform.OS;
794
- /** OS version string (iOS `osVersion`/`Platform.Version`, Android `Release`). */
795
- osVersion?: string;
796
- /** Android device brand (e.g. "samsung"). Android only. */
797
- brand?: string;
798
- /** Android device model (e.g. "SM-G991B"). Android only. */
799
- model?: string;
800
- /** iOS interface idiom ("phone" | "pad" | …), when reported. iOS only. */
801
- interfaceIdiom?: string;
802
- /** Derived device class. */
803
- formFactor?: DeviceFormFactor;
804
- /** `Dimensions.get('screen')` width in dp. */
805
- screenWidth?: number;
806
- /** `Dimensions.get('screen')` height in dp. */
807
- screenHeight?: number;
808
- /** Screen pixel density (`scale`). */
809
- screenScale?: number;
810
- /** Right-to-left layout (`I18nManager.isRTL`). */
811
- isRTL?: boolean;
812
- /** Resolved locale (e.g. "en-US"), from `Intl` when available. */
813
- locale?: string;
814
- /** IANA time zone (e.g. "Europe/Berlin"), from `Intl` when available. */
815
- timeZone?: string;
816
- /**
817
- * Host app version (e.g. "1.4.2). HOST-INJECTED — NOT collected here. `WireOnboarding`
818
- * merges `config.appVersion` into the snapshot; `collectDeviceContext()` never sets it.
819
- * Hosts typically pass it from `expo-constants` (the kit itself adds no dependency).
820
- */
821
- appVersion?: string;
822
- };
823
- /**
824
- * Collect the device snapshot. Pure, synchronous, and never throws — call it once per
825
- * onboarding session. Missing fields are omitted rather than sent as null/undefined so the
826
- * payload (and the server's stored dict) stays compact.
827
- */
828
- declare const collectDeviceContext: () => DeviceContext;
829
-
830
- /**
831
- * reportClientEvent — forward DEVICE-ONLY onboarding events to the Wire AI analytics
832
- * backend (`POST {serverUrl}/v1/events`), completing the funnel for events the server
833
- * can't observe on its own.
834
- *
835
- * The backend already records the server-observable funnel during the A2A flow
836
- * (`session_started`, `screen_shown`, `answer_submitted`, `completed`, `llm_fallback`,
837
- * and even `screen_skipped` — it derives that from the kit's skip sentinel). The one
838
- * event no server request can capture is `dropped`: the user closing the app / unmounting
839
- * the flow without finishing. That's what this reporter is for.
840
- *
841
- * Contract (server: routers/onboarding.py → analytics/events.py):
842
- * POST {serverUrl}/v1/events
843
- * Authorization: Bearer {apiKey}
844
- * { "events": [ { event_type, session_id, screen_index?, component?, question_key?,
845
- * latency_ms?, meta?, device?, user_context? } ] }
846
- * The server fills `app_id` + `environment` from the resolving key (never send app_id),
847
- * and silently skips malformed events — one bad payload never fails the batch.
848
- *
849
- * ⚠️ Correlation: `session_id` MUST equal the A2A `contextId` the server uses to key the
850
- * server-side events, or the funnel report (which groups by `session_id`) treats this as a
851
- * phantom session. See `makeSessionId` + WireOnboarding for how the kit seeds it.
852
- *
853
- * Fire-and-forget: this never throws into the UI and never awaits — analytics must never
854
- * be able to break onboarding.
855
- */
856
-
857
- /** Event types a CLIENT may report. The rest of the funnel is server-side; sending those
858
- * here would double-count. `screen_skipped` is included for completeness, but the kit does
859
- * NOT emit it — the backend already derives it from the skip sentinel (see OnboardingFlow).
860
- * `client_fallback` is emitted by the kit when the AI flow degrades to the static fallback,
861
- * so the dashboard's fallback-rate counts the whole-flow case (distinct from the server's
862
- * per-turn `llm_fallback`). The server back-fills a `session_started` for it if unseen.
863
- * This is the SINGLE fallback signal — hosts must NOT also report their own.
864
- * `identify` binds the host's opaque `user_id` to this `session_id` (late binding — the user
865
- * registered during/after onboarding). It carries no funnel weight; the server maps the
866
- * session to the user and back-fills a `session_started` if it never saw the session. */
867
- type ClientEventType = "screen_skipped" | "dropped" | "client_fallback" | "identify";
868
- /** One client-reported event. Mirrors the server's `OnboardingEvent` (client-settable fields). */
869
- type ClientEvent = {
870
- event_type: ClientEventType;
871
- /** Must match the server-side A2A contextId for this onboarding (see makeSessionId). */
872
- session_id: string;
873
- /** 0-based index of the screen the event refers to (matches server `screen_shown`). */
874
- screen_index?: number;
875
- component?: string;
876
- question_key?: string;
877
- latency_ms?: number;
878
- /** JSON-stringified extras; the server stores it verbatim. */
879
- meta?: string;
880
- /**
881
- * Privacy-label-neutral device snapshot (platform / form factor / locale / host appVersion).
882
- * Sent as an object; the server sanitizes + persists it and derives a coarse country. Old
883
- * servers ignore this unknown field — fully backward compatible. See device/deviceContext.ts.
884
- */
885
- device?: DeviceContext;
886
- /**
887
- * Host-injected, non-PII context (signup method, referral, plan, hashed user id). Old servers
888
- * ignore it. MUST NOT contain PII like raw emails — see the README `userContext` section.
889
- */
890
- user_context?: Record<string, string | number | boolean>;
891
- /**
892
- * The host's OPAQUE PSEUDONYMOUS user id (their internal id, NOT an email/name). Required on
893
- * `identify`, optional (rides along) on other events. Trimmed + capped at 128 chars host-side.
894
- * Lets the backend reconcile onboarding sessions to real users. Old servers ignore it.
895
- */
896
- user_id?: string;
897
- };
898
- /** Where to POST. Derived from `WireOnboardingConfig` (`serverUrl` + `apiKey`). */
899
- type ClientEventTarget = {
900
- /** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
901
- serverUrl: string;
902
- /** Tenant API key; sent as `Authorization: Bearer`. */
903
- apiKey: string;
904
- };
905
- /**
906
- * A unique-per-onboarding session id. Used both as the client event `session_id` AND as the
907
- * seed the kit forwards to the backend so the SERVER adopts it as the A2A `contextId` — making
908
- * client and server agree (see WireOnboarding + the SDK-correlation note in the kit docs).
909
- * No crypto dependency: timestamp + random is collision-safe for a single device's onboarding.
910
- */
911
- declare const makeSessionId: () => string;
912
- /**
913
- * POST one or more client events, fire-and-forget. A missing/invalid target, a build error,
914
- * a missing `fetch`, or a network failure is swallowed — the call returns immediately and the
915
- * request (if any) runs in the background.
916
- */
917
- declare const reportClientEvents: (target: ClientEventTarget | undefined, events: ClientEvent[]) => void;
918
- /** Convenience single-event wrapper around {@link reportClientEvents}. */
919
- declare const reportClientEvent: (target: ClientEventTarget | undefined, event: ClientEvent) => void;
920
-
921
551
  /**
922
552
  * OnboardingFlow — the inner engine. Mirrors the proven production-app usage of the SDK
923
553
  * (useWireAIThread + useWireAIAction + ComponentRenderer) and layers on the kit's
@@ -1034,7 +664,7 @@ declare const useWireFeatures: (config?: WireFeaturesConfig) => WireFeatures;
1034
664
  *
1035
665
  * NOTE on the built npm subpaths: React context is shared across the kit's subpath entries in the
1036
666
  * SOURCE-consumption path (the monorepo apps) and in this test suite — one module instance, one
1037
- * context. If you consume the built `wireai-onboarding/*` subpaths as separate bundles, pass the
667
+ * context. If you consume the built `@wireai/activation/*` subpaths as separate bundles, pass the
1038
668
  * resolved flags (or a `featuresConfig`) to each surface explicitly instead of relying on context.
1039
669
  */
1040
670
 
@@ -1156,46 +786,6 @@ type OnboardingFlagOptions = {
1156
786
  };
1157
787
  declare const isOnboardingEnabled: (opts?: OnboardingFlagOptions) => boolean;
1158
788
 
1159
- /**
1160
- * Canonical analytics names for the onboarding funnel. The kit already emits a typed
1161
- * `OnboardingEvent` (`started | turn | error | retry | fallback`) — but one app logged them as
1162
- * `onboarding_*` and another as `AI_ONBOARDING_*`, so the same funnel reads differently per app.
1163
- * This maps the kit event to ONE canonical `wire_onboarding_*` name + params, and the app logs
1164
- * it through whatever transport it already has (Firebase, Amplitude, console). The app still
1165
- * owns the logger; only the NAMES are standardized.
1166
- *
1167
- * <WireOnboarding
1168
- * onEvent={(e) => { const a = toAnalyticsEvent(e); logEvent(a.name, a.params); }}
1169
- * onComplete={(r) => { logEvent(WIRE_ONBOARDING_EVENTS.completed, { answers: Object.keys(r.answers).length }); persist(r); }}
1170
- * />
1171
- *
1172
- * `completed` has no kit `OnboardingEvent` (the kit signals completion via `onComplete`, not
1173
- * `onEvent`) — the app logs it explicitly on `onComplete` using the constant below, so the
1174
- * funnel name stays canonical.
1175
- */
1176
-
1177
- declare const WIRE_ONBOARDING_EVENTS: {
1178
- readonly started: "wire_onboarding_started";
1179
- /** A persisted session was restored after an app kill (fires instead of `started`). */
1180
- readonly resumed: "wire_onboarding_resumed";
1181
- readonly turn: "wire_onboarding_turn";
1182
- readonly error: "wire_onboarding_error";
1183
- readonly retry: "wire_onboarding_retry";
1184
- readonly fallback: "wire_onboarding_fallback";
1185
- /** Logged by the host on `onComplete` (no matching kit `OnboardingEvent`). */
1186
- readonly completed: "wire_onboarding_completed";
1187
- };
1188
- type WireOnboardingEventName = (typeof WIRE_ONBOARDING_EVENTS)[keyof typeof WIRE_ONBOARDING_EVENTS];
1189
- type AnalyticsEvent = {
1190
- name: WireOnboardingEventName;
1191
- params?: Record<string, unknown>;
1192
- };
1193
- /**
1194
- * Map a kit `OnboardingEvent` to its canonical `{ name, params }`. Exhaustive over the union
1195
- * (the `never` default makes a new event type a compile error here — intentional).
1196
- */
1197
- declare const toAnalyticsEvent: (event: OnboardingEvent) => AnalyticsEvent;
1198
-
1199
789
  /**
1200
790
  * attribution — forward install/ad attribution to the onboarding agent so it can tailor the
1201
791
  * first question by acquisition source (e.g. a paid-campaign install vs an organic one). The
@@ -1347,6 +937,15 @@ interface ReportSessionStartOptions {
1347
937
  meta?: Record<string, unknown>;
1348
938
  /** Set `false` to bypass the once-per-open guard (default on). See {@link resetSessionStartGuard}. */
1349
939
  once?: boolean;
940
+ /**
941
+ * OPTIONAL transport sink. When provided, the built `app.session_started` event is routed HERE
942
+ * (e.g. the offline-first event queue's `enqueue`) INSTEAD of this emitter's own direct `fetch`,
943
+ * while KEEPING the once-per-open guard above. This is how the lifecycle wiring
944
+ * (`useLifecycleEvents` / `wireLifecycleEvents`) offline-buffers session-start WITHOUT adding a
945
+ * second session emitter: one emitter, one guard, now durable. Omit it for the direct-POST path.
946
+ * A throwing sink is swallowed — analytics must never surface into the UI.
947
+ */
948
+ sink?: (event: ClientEvent) => void;
1350
949
  }
1351
950
  /** Test-only: forget every emitted session id so a unit test starts from a clean guard. */
1352
951
  declare const resetSessionStartGuard: () => void;
@@ -1387,4 +986,149 @@ interface UseSessionStartOptions {
1387
986
  */
1388
987
  declare const useSessionStart: (config: SessionStartConfig | undefined, options?: UseSessionStartOptions) => void;
1389
988
 
1390
- export { type AnalyticsEvent, AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardHandoff, type CardHandoffProps, type CardHandoffVariant, ChipSelectCard, type ClientEvent, type ClientEventTarget, type ClientEventType, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, type DeviceContext, type DeviceFormFactor, DoneBlock, ErrorBlock, type IdentifyOnboardingOptions, IllustrationProvider, type IllustrationRegistry, InterstitialCard, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, type OnboardingCopy, type OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, type OnboardingProgress, type OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, type ReportSessionStartOptions, type ResolveFeaturesOptions, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, type StepValidator, TextInputCard, type ThemeFromBrandInput, USER_ID_MAX_LENGTH, type UseSessionStartOptions, WIRE_ONBOARDING_EVENTS, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, WireOnboarding, type WireOnboardingConfig, type WireOnboardingEventName, type WireOnboardingProps, WireOnboardingStorage, attributionMetadata, collectDeviceContext, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, featuresCacheKey, featuresEqual, fetchWireFeatures, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, makeSessionId, mergeTheme, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportClientEvent, reportClientEvents, reportSessionStart, resetSessionStartGuard, sanitizeUserId, themeFromBrand, toAnalyticsEvent, useIllustration, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, writeCachedFeatures };
989
+ /**
990
+ * lifecycle — the TOP-OF-FUNNEL app lifecycle events that sit ABOVE onboarding: `app.first_open`
991
+ * (once ever per install) and `app.session_started` (per app-open). They compose with, and never
992
+ * duplicate, the events the funnel already records.
993
+ *
994
+ * WHERE THIS FITS (one funnel, no double counting):
995
+ * • `app.first_open` → THIS module, once ever (persisted flag). The in-app "install" proxy.
996
+ * • `app.session_started` → the EXISTING {@link reportSessionStart} emitter (per app-open). This
997
+ * module REUSES it (routed through the offline queue), never re-emits.
998
+ * • onboarding started / completed / activated → ALREADY recorded: the server writes
999
+ * `session_started` + `completed` during the A2A flow, and the client reports `dropped` /
1000
+ * `client_fallback` / `identify` via `reportClientEvent`. This module does NOT touch them —
1001
+ * emitting them here would double-count. It only ADDS the two lifecycle events above them.
1002
+ *
1003
+ * WIRE CONTRACT (identical to `app.session_started`): both lifecycle events are stored as
1004
+ * `event_type='app_event'` with the name in `question_key` (the server's `_event_name` returns
1005
+ * `question_key` for an `app_event`, which a trigger matches). `app.first_open` uses the SAME
1006
+ * `app.*` namespace + the SAME event shape as `app.session_started` — no bespoke `event_type`,
1007
+ * no invented contract. `device_key` rides in the non-PII `user_context` bucket; `app_id` +
1008
+ * `environment` are filled server-side.
1009
+ *
1010
+ * TWO FIRST-CLASS ENTRY POINTS (mirrors reportSessionStart vs useSessionStart):
1011
+ * • `reportFirstOpen(...)` / `wireLifecycleEvents(...)` — pure, React-free, for a host that owns
1012
+ * its own app-open path.
1013
+ * • `useLifecycleEvents(...)` — the batteries-included React hook (in `useLifecycleEvents.ts`).
1014
+ *
1015
+ * OFFLINE-FIRST: pass the Brief-01 event queue's `enqueue` as the `sink` and every lifecycle event
1016
+ * is buffered + persisted + retried instead of a blind fetch. Without a sink they degrade to a
1017
+ * direct fire-and-forget POST.
1018
+ *
1019
+ * PRIVACY (hard rule, same as the rest of the kit): NO GPS/location, NO advertising id. Nothing
1020
+ * here carries a coordinate or an ad id — a host adopting it changes no store privacy declaration.
1021
+ *
1022
+ * FIRE-AND-FORGET: like every analytics path in the kit, nothing here throws into the UI, awaits
1023
+ * in the caller, or hangs the app — storage reads race a short timeout, writes swallow errors.
1024
+ */
1025
+
1026
+ /** The canonical event name for the first-ever app open. Same `app.*` namespace as
1027
+ * {@link SESSION_STARTED_EVENT}; a trigger keys off this exact string. */
1028
+ declare const FIRST_OPEN_EVENT: "app.first_open";
1029
+ /** Storage key for the once-ever first-open flag, e.g. `wireai:first_open:myelino`. Mirrors the
1030
+ * `wireai:<concern>:<appId>` namespacing of {@link sessionStorageKey}. */
1031
+ declare const firstOpenStorageKey: (appId: string) => string;
1032
+ /** Test-only: forget the first-open latch so a unit test starts from a clean process state. */
1033
+ declare const resetFirstOpenLatch: () => void;
1034
+ /** Shared inputs for a lifecycle event. Everything is optional except a transport (`target` for the
1035
+ * direct-POST fallback, or a `sink`). A pre-auth open (no user yet) is a valid device-only event. */
1036
+ interface LifecycleEventInput {
1037
+ /** Where to POST if no `sink` is wired (the tenant transport, same as `WireOnboardingConfig`). */
1038
+ target?: ClientEventTarget;
1039
+ /** Preferred transport: route the built event HERE (the offline queue's `enqueue`). */
1040
+ sink?: (event: ClientEvent) => void;
1041
+ /** The per-open session id. Defaults to a fresh `makeSessionId()`. */
1042
+ sessionId?: string;
1043
+ /** The host's OPAQUE pseudonymous user id (NOT PII). Sanitized + capped; omitted pre-auth. */
1044
+ userId?: string;
1045
+ /** A stable, non-PII device id the host owns. Rides in `user_context.device_key`. */
1046
+ deviceKey?: string;
1047
+ /** The host's local open-counter value. Drives `returning` + "Nth session". */
1048
+ sessionCount?: number;
1049
+ /** Host app version (e.g. "1.4.2"), if cheaply available. */
1050
+ appVersion?: string;
1051
+ /** Platform string (e.g. "ios"), if cheaply available. */
1052
+ platform?: string;
1053
+ /** An optional richer device snapshot (from `collectDeviceContext()`); the hook fills this. */
1054
+ device?: DeviceContext;
1055
+ /** Small non-PII extras, stored as a JSON string in the event `meta`. */
1056
+ meta?: Record<string, unknown>;
1057
+ }
1058
+ /** Options for {@link reportFirstOpen}. Adds the once-ever persistence inputs on top of the shared
1059
+ * lifecycle inputs. Without `storage` it degrades to the in-memory latch (once per process). */
1060
+ interface ReportFirstOpenOptions extends LifecycleEventInput {
1061
+ /** Host persistence (AsyncStorage subset). The once-ever flag lives here — it is what survives an
1062
+ * app kill. Omit it for the documented degraded (in-memory, once-per-process) mode. */
1063
+ storage?: WireOnboardingStorage;
1064
+ /** Tenant/app id — namespaces the persisted flag (`wireai:first_open:<appId>`). */
1065
+ appId?: string;
1066
+ }
1067
+ /**
1068
+ * Emit `app.first_open` EXACTLY ONCE EVER per install. Fire-and-forget; returns immediately.
1069
+ *
1070
+ * • With `storage`: reads the persisted flag (`wireai:first_open:<appId>`). Absent → emit, then
1071
+ * write the flag (survives app kills, so a second launch is a no-op). Present → no-op.
1072
+ * • Race guard: an in-memory latch is set SYNCHRONOUSLY before the async read, so two
1073
+ * near-simultaneous calls fire at most once.
1074
+ * • Without `storage`: degraded mode — fires once per PROCESS via the latch only (documented).
1075
+ */
1076
+ declare const reportFirstOpen: (opts: ReportFirstOpenOptions) => void;
1077
+ /** Options for {@link wireLifecycleEvents}: the shared lifecycle inputs + first-open persistence. */
1078
+ interface WireLifecycleOptions extends ReportFirstOpenOptions {
1079
+ }
1080
+ /**
1081
+ * Wire BOTH lifecycle events in one call for a host that owns its own app-open path (the non-hook
1082
+ * counterpart to {@link useLifecycleEvents}). Fires `app.first_open` (once ever) and one
1083
+ * `app.session_started` for THIS open through the EXISTING {@link reportSessionStart} emitter (so
1084
+ * the once-per-open guard still applies — pass the same `sessionId` and it never double-fires).
1085
+ * Route both through the same `sink` (the offline queue) to buffer them. Fire-and-forget.
1086
+ */
1087
+ declare const wireLifecycleEvents: (opts: WireLifecycleOptions) => void;
1088
+
1089
+ /** Tenant transport + host persistence for the lifecycle wiring. Same creds as `WireOnboardingConfig`. */
1090
+ interface LifecycleConfig {
1091
+ /** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
1092
+ serverUrl?: string;
1093
+ /** Tenant API key; sent as `Authorization: Bearer`. */
1094
+ apiKey?: string;
1095
+ /** Host app version (e.g. "1.4.2"), forwarded for release segmentation. Optional. */
1096
+ appVersion?: string;
1097
+ /** Tenant/app id — namespaces the first-open flag AND the hook's internal queue storage key. */
1098
+ appId?: string;
1099
+ /** Host storage (AsyncStorage subset). Enables the persisted once-ever first-open flag AND the
1100
+ * offline durability of the hook's internal queue. Omit it and both degrade to in-memory. */
1101
+ storage?: WireOnboardingStorage;
1102
+ }
1103
+ /** Per-open identity + wiring the host supplies. All optional: a pre-auth open is device-only. */
1104
+ interface UseLifecycleEventsOptions {
1105
+ /** The host's opaque pseudonymous user id (NOT PII). Omitted before the user authenticates. */
1106
+ userId?: string;
1107
+ /** The host's local open-counter value for this open. Drives `returning` + "Nth session". */
1108
+ sessionCount?: number;
1109
+ /** A stable, non-PII device id the host owns. Groups this device's sessions server-side. */
1110
+ deviceKey?: string;
1111
+ /** Small non-PII extras forwarded on the event `meta`. */
1112
+ meta?: Record<string, unknown>;
1113
+ /** Set `false` to disable firing (e.g. behind a consent gate). Default enabled. */
1114
+ enabled?: boolean;
1115
+ /**
1116
+ * Explicit transport sink (e.g. an existing shared `EventQueue.enqueue`). When provided, BOTH
1117
+ * lifecycle events route here and the hook does NOT create its own queue — pass this to share ONE
1118
+ * offline queue across the kit's analytics (screen tracking + lifecycle).
1119
+ */
1120
+ sink?: (event: ClientEvent) => void;
1121
+ /**
1122
+ * Context envelope (or provider) for the hook's internally-created queue. Ignored when `sink` is
1123
+ * supplied (the host's queue owns envelope stamping).
1124
+ */
1125
+ envelope?: EnvelopeSource;
1126
+ }
1127
+ /**
1128
+ * Fire `app.first_open` (once ever) + `app.session_started` (per open), offline-buffered. Returns
1129
+ * nothing — a side-effecting hook. Safe to call with inline options (read through a ref, so
1130
+ * changing `userId`/`sessionCount` never re-fires a session).
1131
+ */
1132
+ declare const useLifecycleEvents: (config: LifecycleConfig | undefined, options?: UseLifecycleEventsOptions) => void;
1133
+
1134
+ export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardHandoff, type CardHandoffProps, type CardHandoffVariant, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, DeviceContext, DoneBlock, ErrorBlock, FIRST_OPEN_EVENT, type IdentifyOnboardingOptions, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, USER_ID_MAX_LENGTH, type UseLifecycleEventsOptions, type UseSessionStartOptions, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, attributionMetadata, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, mergeTheme, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportFirstOpen, reportSessionStart, resetFirstOpenLatch, resetSessionStartGuard, sanitizeUserId, themeFromBrand, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };