@wireai/activation 0.1.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 (142) hide show
  1. package/AGENTS.md +73 -0
  2. package/CHANGELOG.md +572 -0
  3. package/INTEGRATION_PROMPT.md +55 -0
  4. package/LICENSE +21 -0
  5. package/README.md +923 -0
  6. package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
  7. package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
  8. package/dist/coachmarks/index.d.mts +352 -0
  9. package/dist/coachmarks/index.d.ts +352 -0
  10. package/dist/coachmarks/index.js +944 -0
  11. package/dist/coachmarks/index.js.map +1 -0
  12. package/dist/coachmarks/index.mjs +918 -0
  13. package/dist/coachmarks/index.mjs.map +1 -0
  14. package/dist/decision-CFvGY6nP.d.ts +228 -0
  15. package/dist/decision-Cl8OFYzu.d.mts +228 -0
  16. package/dist/index.d.mts +1390 -0
  17. package/dist/index.d.ts +1390 -0
  18. package/dist/index.js +3260 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.mjs +3186 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/questionnaire/index.d.mts +249 -0
  23. package/dist/questionnaire/index.d.ts +249 -0
  24. package/dist/questionnaire/index.js +922 -0
  25. package/dist/questionnaire/index.js.map +1 -0
  26. package/dist/questionnaire/index.mjs +906 -0
  27. package/dist/questionnaire/index.mjs.map +1 -0
  28. package/dist/reviews/index.d.mts +213 -0
  29. package/dist/reviews/index.d.ts +213 -0
  30. package/dist/reviews/index.js +1001 -0
  31. package/dist/reviews/index.js.map +1 -0
  32. package/dist/reviews/index.mjs +971 -0
  33. package/dist/reviews/index.mjs.map +1 -0
  34. package/dist/showcase/index.d.mts +119 -0
  35. package/dist/showcase/index.d.ts +119 -0
  36. package/dist/showcase/index.js +660 -0
  37. package/dist/showcase/index.js.map +1 -0
  38. package/dist/showcase/index.mjs +651 -0
  39. package/dist/showcase/index.mjs.map +1 -0
  40. package/dist/types-BKfpdZzX.d.mts +84 -0
  41. package/dist/types-BKfpdZzX.d.ts +84 -0
  42. package/dist/types-CMuOexw0.d.mts +116 -0
  43. package/dist/types-CMuOexw0.d.ts +116 -0
  44. package/dist/types-GL_hQ0TN.d.mts +46 -0
  45. package/dist/types-GL_hQ0TN.d.ts +46 -0
  46. package/llms.txt +36 -0
  47. package/metro/index.d.ts +32 -0
  48. package/metro/index.js +83 -0
  49. package/package.json +171 -0
  50. package/src/DemoOnboarding.tsx +133 -0
  51. package/src/OnboardingFlow.tsx +504 -0
  52. package/src/WireOnboarding.tsx +248 -0
  53. package/src/analytics/analyticsEvent.ts +69 -0
  54. package/src/analytics/reportClientEvent.ts +119 -0
  55. package/src/analytics/sendPreview.ts +73 -0
  56. package/src/attribution/attribution.ts +46 -0
  57. package/src/cards/ChipSelectCard.tsx +265 -0
  58. package/src/cards/InterstitialCard.tsx +299 -0
  59. package/src/cards/NumberStepperCard.tsx +121 -0
  60. package/src/cards/SelectionCard.tsx +270 -0
  61. package/src/cards/StatusCard.tsx +170 -0
  62. package/src/cards/TextInputCard.tsx +147 -0
  63. package/src/cards/index.ts +23 -0
  64. package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
  65. package/src/coachmarks/CoachmarkProvider.tsx +108 -0
  66. package/src/coachmarks/GestureHint.tsx +331 -0
  67. package/src/coachmarks/SpotlightOverlay.tsx +254 -0
  68. package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
  69. package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
  70. package/src/coachmarks/index.ts +62 -0
  71. package/src/coachmarks/runtime.ts +103 -0
  72. package/src/coachmarks/selectTourSteps.ts +45 -0
  73. package/src/coachmarks/types.ts +58 -0
  74. package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
  75. package/src/coachmarks/useCoachmarkTour.ts +200 -0
  76. package/src/components/AnimatedSparkle.tsx +101 -0
  77. package/src/components/Button.tsx +129 -0
  78. package/src/components/CardHandoff.tsx +212 -0
  79. package/src/components/CardLayout.tsx +103 -0
  80. package/src/components/CenteredModal.tsx +184 -0
  81. package/src/components/CompletionView.tsx +186 -0
  82. package/src/components/DoneBlock.tsx +37 -0
  83. package/src/components/ErrorBlock.tsx +42 -0
  84. package/src/components/Illustration.tsx +36 -0
  85. package/src/components/LoadingBlock.tsx +182 -0
  86. package/src/components/LoadingScreen.tsx +35 -0
  87. package/src/components/OnboardingScaffold.tsx +105 -0
  88. package/src/components/StepProgress.tsx +85 -0
  89. package/src/components/loaderChrome.ts +28 -0
  90. package/src/config/onboardingFlag.ts +39 -0
  91. package/src/config/wireConfigFromEnv.ts +52 -0
  92. package/src/device/deviceContext.ts +158 -0
  93. package/src/features/WireFeaturesProvider.tsx +81 -0
  94. package/src/features/cache.ts +92 -0
  95. package/src/features/defaults.ts +67 -0
  96. package/src/features/fetchWireFeatures.ts +86 -0
  97. package/src/features/index.ts +39 -0
  98. package/src/features/types.ts +61 -0
  99. package/src/features/useWireFeatures.ts +57 -0
  100. package/src/identity/userIdentity.ts +100 -0
  101. package/src/illustrations/defaultIllustrations.tsx +99 -0
  102. package/src/index.ts +159 -0
  103. package/src/motion/centeredModalMotion.ts +45 -0
  104. package/src/motion/motionSpec.ts +133 -0
  105. package/src/motion/useReducedMotion.ts +43 -0
  106. package/src/questionnaire/QuestionnaireGate.tsx +235 -0
  107. package/src/questionnaire/decision.ts +63 -0
  108. package/src/questionnaire/index.ts +54 -0
  109. package/src/questionnaire/runtime.ts +20 -0
  110. package/src/questionnaire/transport.ts +83 -0
  111. package/src/questionnaire/types.ts +182 -0
  112. package/src/questionnaire/useQuestionnaireGate.ts +159 -0
  113. package/src/reviews/ReviewGate.tsx +316 -0
  114. package/src/reviews/ReviewModal.tsx +13 -0
  115. package/src/reviews/decision.ts +120 -0
  116. package/src/reviews/equality.ts +54 -0
  117. package/src/reviews/expo-store-review.d.ts +16 -0
  118. package/src/reviews/feedbackForm.ts +41 -0
  119. package/src/reviews/index.ts +78 -0
  120. package/src/reviews/presentation.ts +29 -0
  121. package/src/reviews/runtime.ts +45 -0
  122. package/src/reviews/storeReview.ts +62 -0
  123. package/src/reviews/transport.ts +89 -0
  124. package/src/reviews/types.ts +172 -0
  125. package/src/reviews/useReviewGate.ts +137 -0
  126. package/src/session/persistedSession.ts +143 -0
  127. package/src/session-analytics/index.ts +18 -0
  128. package/src/session-analytics/reportSessionStart.ts +144 -0
  129. package/src/session-analytics/useSessionStart.ts +114 -0
  130. package/src/showcase/FeatureShowcase.tsx +233 -0
  131. package/src/showcase/index.ts +19 -0
  132. package/src/showcase/selectShowcaseSlides.ts +45 -0
  133. package/src/showcase/showcaseColors.ts +91 -0
  134. package/src/showcase/types.ts +74 -0
  135. package/src/theme/ThemeContext.tsx +38 -0
  136. package/src/theme/defaultTheme.ts +70 -0
  137. package/src/theme/themeFromBrand.ts +93 -0
  138. package/src/theme/types.ts +88 -0
  139. package/src/theme/typography.ts +46 -0
  140. package/src/types.ts +215 -0
  141. package/src/utils/deriveAnswers.ts +43 -0
  142. package/src/utils/readProgress.ts +33 -0
@@ -0,0 +1,1390 @@
1
+ import * as React from 'react';
2
+ import React__default, { ReactNode } from 'react';
3
+ import * as wireai_rn from 'wireai-rn';
4
+ import { Message, WireAIComponent, WireAIResponse } from 'wireai-rn';
5
+ import { O as OnboardingTheme } from './types-BKfpdZzX.js';
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
+ 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
+ };
228
+
229
+ /**
230
+ * WireOnboarding — the public host component. Drop it into a screen and it runs
231
+ * the whole AI onboarding: builds the A2A transport from `config`, wires the SDK
232
+ * provider with the themed cards, applies the theme, and renders the flow.
233
+ *
234
+ * <WireOnboarding
235
+ * config={{ apiKey, serverUrl, appId }}
236
+ * theme={myBrandTheme}
237
+ * validators={{ username: checkUsernameAvailable }}
238
+ * onComplete={persist}
239
+ * onSkip={skip}
240
+ * />
241
+ */
242
+
243
+ declare const WireOnboarding: React__default.FC<WireOnboardingProps>;
244
+
245
+ type DemoOnboardingProps = {
246
+ /** The Wire AI config, or `null` when the app hasn't integrated Wire AI yet. */
247
+ config: WireOnboardingConfig | null;
248
+ theme?: WireOnboardingProps["theme"];
249
+ illustrations?: WireOnboardingProps["illustrations"];
250
+ validators?: WireOnboardingProps["validators"];
251
+ copy?: WireOnboardingProps["copy"];
252
+ /** Trigger button label. Default "Demo onboarding". */
253
+ label?: string;
254
+ /** Observe the demo result. By default the demo just closes (no persistence). */
255
+ onComplete?: (result: OnboardingResult) => void;
256
+ /** Render your own trigger instead of the default button: `(open) => <YourButton onPress={open} />`. */
257
+ renderTrigger?: (open: () => void) => ReactNode;
258
+ };
259
+ declare function DemoOnboarding({ config, theme, illustrations, validators, copy, label, onComplete, renderTrigger, }: DemoOnboardingProps): React.JSX.Element;
260
+
261
+ /**
262
+ * ThemeContext — provides the resolved OnboardingTheme to every shell element and
263
+ * themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:
264
+ * the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't
265
+ * pass them props directly — they must read the theme from context.
266
+ */
267
+
268
+ type OnboardingThemeProviderProps = {
269
+ /** Partial overrides merged over the neutral default theme. */
270
+ theme?: Partial<OnboardingTheme>;
271
+ children: React__default.ReactNode;
272
+ };
273
+ declare const OnboardingThemeProvider: React__default.FC<OnboardingThemeProviderProps>;
274
+ /**
275
+ * Read the active theme. Falls back to the default theme when used outside a
276
+ * provider so a stray card never crashes — it just renders neutral.
277
+ */
278
+ declare const useOnboardingTheme: () => OnboardingTheme;
279
+
280
+ /**
281
+ * defaultOnboardingTheme — a neutral, light, brand-agnostic baseline. Consumers
282
+ * pass a `Partial<OnboardingTheme>` that is deep-merged over this (see
283
+ * `mergeTheme`), so they only override what differs from neutral.
284
+ */
285
+
286
+ declare const defaultOnboardingTheme: OnboardingTheme;
287
+ /**
288
+ * Deep-merge a partial theme over the default. Two levels deep is enough — the
289
+ * theme is intentionally shallow (colors/fonts/radius/spacing), so we merge each
290
+ * known sub-object and leave scalars to overwrite.
291
+ */
292
+ declare const mergeTheme: (override?: Partial<OnboardingTheme>) => OnboardingTheme;
293
+
294
+ type ThemeFromBrandInput = {
295
+ /** Brand / accent color — drives selected states, primary buttons, progress fill. */
296
+ primary: string;
297
+ /** App background behind the whole flow. Default: the neutral light background. */
298
+ background?: string;
299
+ /** Card / elevated surface background. Default: the neutral surface. */
300
+ surface?: string;
301
+ /** Primary body text color. Default: the neutral near-black. */
302
+ text?: string;
303
+ /** Text/icon color on top of `primary`. Default: white. */
304
+ onPrimary?: string;
305
+ /**
306
+ * A single font family applied to regular/medium/bold (the kit derives weight
307
+ * from size when a family isn't weight-split). Pass `fonts` in `extra` for
308
+ * per-weight families.
309
+ */
310
+ font?: string;
311
+ /** Any further theme overrides, deep-merged last (radius/spacing/button/fonts/colors). */
312
+ extra?: Partial<OnboardingTheme>;
313
+ };
314
+ declare const themeFromBrand: (input: ThemeFromBrandInput) => Partial<OnboardingTheme>;
315
+
316
+ /**
317
+ * OnboardingScaffold — the themed shell that wraps every step:
318
+ * [ safe area ]
319
+ * header -> StepProgress (a numberless fill — never "Step X of N") + optional Skip
320
+ * body -> the rendered card (children), full-screen (flex: 1)
321
+ * footer -> optional Back link
322
+ *
323
+ * WHY a single scaffold: the body content changes every turn (it's an
324
+ * SDK-rendered card), but the chrome — progress, skip, back, padding, safe area —
325
+ * stays constant. Keeping it here means cards never re-implement the frame.
326
+ *
327
+ * The body no longer centers/scrolls the card: each card fills the body via the
328
+ * shared CardLayout, which owns its own scroll + bottom-pinned CTA (Cal-AI style).
329
+ * The safe area uses `react-native-safe-area-context` (top + bottom edges) — the
330
+ * RN core SafeAreaView is deprecated and iOS-only, so the kit takes the standard
331
+ * Expo/RN peer instead of shipping cropped layouts on Android + notched devices.
332
+ */
333
+
334
+ type OnboardingScaffoldProps = {
335
+ /** 1-based index of the current step; drives the (countless) progress bar. */
336
+ step: number;
337
+ /** When true, the progress bar fills to 100% (flow finished). */
338
+ complete?: boolean;
339
+ /** Approximate total screens, if known — paces the bar (never shown). */
340
+ approxScreens?: number;
341
+ /** Optional Back action — hidden when omitted (e.g. on the first step). */
342
+ onBack?: () => void;
343
+ /** Optional Skip action — hidden when omitted. */
344
+ onSkip?: () => void;
345
+ skipLabel?: string;
346
+ backLabel?: string;
347
+ children: React__default.ReactNode;
348
+ };
349
+ declare const OnboardingScaffold: React__default.NamedExoticComponent<OnboardingScaffoldProps>;
350
+
351
+ /**
352
+ * StepProgress — a single themed progress bar that conveys forward motion. It never
353
+ * shows a "Step X of N" number (the count would read as a promise the model may not
354
+ * keep), but it can be *paced* two ways:
355
+ *
356
+ * - Default (no `approxScreens`): an asymptotic curve of the step index — always
357
+ * advances, never reaches the end until the flow actually completes.
358
+ * - With `approxScreens`: when the host knows roughly how many screens the flow
359
+ * runs (e.g. the backend's screen budget), the fill tracks `step / approxScreens`
360
+ * (capped below 100% until `complete`). More truthful pacing, still no number.
361
+ */
362
+
363
+ type StepProgressProps = {
364
+ /** 1-based index of the current step. Drives the fill. */
365
+ step: number;
366
+ /** When true, fill to 100% (flow finished). */
367
+ complete?: boolean;
368
+ /**
369
+ * Approximate total number of screens, if known (e.g. the backend screen budget).
370
+ * Paces the bar as `step / approxScreens` instead of the asymptotic curve. Never
371
+ * shown as a number — it only shapes the fill. Omit for the asymptotic default.
372
+ */
373
+ approxScreens?: number;
374
+ };
375
+ declare const StepProgress: React__default.NamedExoticComponent<StepProgressProps>;
376
+
377
+ /**
378
+ * LoadingBlock — the "AI is thinking" indicator, shown before the first card and
379
+ * between every turn (each round-trip to the backend). The Wire Activation
380
+ * loading treatment: no generic spinner — the brand mark breathes inside a
381
+ * dashed ring while an accent dot orbits it, and skeleton lines appear (softly
382
+ * pulsing) only when the fetch runs past 300ms so a fast turn never flashes
383
+ * placeholder chrome (design-refs/EXTRACTED-SPEC.md rows 17-19).
384
+ *
385
+ * Reduce motion: static mark + copy, no orbit, no pulse.
386
+ */
387
+
388
+ type LoadingBlockProps = {
389
+ /** Primary line, e.g. "Thinking…". */
390
+ title?: string;
391
+ /** Supporting line under the title. */
392
+ hint?: string;
393
+ };
394
+ declare const LoadingBlock: React__default.NamedExoticComponent<LoadingBlockProps>;
395
+
396
+ /**
397
+ * LoadingScreen — the STANDALONE loader surface: a full-area safe frame that centers a
398
+ * LoadingBlock dead in the middle of the screen. Used for the loader slots that render on
399
+ * their own (no OnboardingScaffold around them): the cold-start "getting started" wait, the
400
+ * resumed-session "restoring" wait, and the transient error/timeout retry beat.
401
+ *
402
+ * WHY a dedicated frame: those slots used to return a bare <LoadingBlock/>, whose flex:1 only
403
+ * fills whatever the host happened to give it — with no safe area and, if the host didn't
404
+ * bound it, no height at all (top-anchored). This wraps them in the same SafeAreaView the flow
405
+ * uses, so the standalone loaders center exactly like the between-turns loader (which centers
406
+ * in the full flow area via the scaffold's loaderChrome offset). One consistent centered look.
407
+ */
408
+
409
+ declare const LoadingScreen: React__default.NamedExoticComponent<LoadingBlockProps>;
410
+
411
+ /**
412
+ * AnimatedSparkle — the themed "AI is thinking" glyph (✦), animated with RN's
413
+ * built-in `Animated` (native driver) so the kit needs neither reanimated nor a
414
+ * vector-icon dependency.
415
+ *
416
+ * Variants:
417
+ * - "spin" (default, unchanged): continuous rotation — the original loader.
418
+ * - "pulse": the Wire Activation loading treatment — the mark breathes
419
+ * (opacity 0.35 ↔ 1, 1600ms loop) instead of rotating.
420
+ *
421
+ * Reduce motion: no loop at all — the glyph renders static at full opacity
422
+ * (final frame), per design-refs/EXTRACTED-SPEC.md row 17.
423
+ */
424
+
425
+ type AnimatedSparkleProps = {
426
+ /** Glyph size in px. */
427
+ size?: number;
428
+ /** Override color; defaults to the theme primary. */
429
+ color?: string;
430
+ /** "spin" rotates (default, the original), "pulse" breathes (the loader treatment). */
431
+ variant?: "spin" | "pulse";
432
+ };
433
+ declare const AnimatedSparkle: React__default.NamedExoticComponent<AnimatedSparkleProps>;
434
+
435
+ /**
436
+ * ErrorBlock — shown on backend error/timeout. Carries a retry action so the flow
437
+ * never dead-ends. The host decides what retry does (re-send start, or fall back).
438
+ */
439
+
440
+ type ErrorBlockProps = {
441
+ title?: string;
442
+ message?: string;
443
+ retryLabel?: string;
444
+ onRetry?: () => void;
445
+ };
446
+ declare const ErrorBlock: React__default.NamedExoticComponent<ErrorBlockProps>;
447
+
448
+ /**
449
+ * DoneBlock — a brief themed "all set" state shown when the flow completes, while
450
+ * the host persists results and navigates away. Purely cosmetic; the real
451
+ * terminal signal is the SDK's StatusCard (handled in OnboardingFlow).
452
+ */
453
+
454
+ type DoneBlockProps = {
455
+ title?: string;
456
+ message?: string;
457
+ };
458
+ declare const DoneBlock: React__default.NamedExoticComponent<DoneBlockProps>;
459
+
460
+ /**
461
+ * CompletionView — the terminal "you're all set" screen, shown when the flow
462
+ * reaches its final StatusCard. It surfaces the backend's recap (what the app
463
+ * will do for the user, derived from their answers) and a single clear CTA.
464
+ *
465
+ * WHY a dedicated screen (not an instant hand-off): the backend spends the last
466
+ * turn summarizing the user's tastes and what their experience will now look
467
+ * like. The user should actually SEE that payoff and tap to continue — so
468
+ * `onContinue` fires only on the button press, never automatically.
469
+ *
470
+ * Motion (design-refs/EXTRACTED-SPEC.md rows 14-16, the peak-end beat): the
471
+ * badge pops in with a spring (scale 0.4→1, slight overshoot), ONE soft accent
472
+ * burst radiates once behind it (scale 0.4→1.5, opacity 0.5→0, 500ms) —
473
+ * celebratory without confetti — and the copy fades up 80ms apart. Everything
474
+ * settles well under a second. Reduce motion: final frame, no burst.
475
+ */
476
+
477
+ type CompletionViewProps = {
478
+ title: string;
479
+ message?: string;
480
+ ctaLabel?: string;
481
+ onContinue: () => void;
482
+ };
483
+ declare const CompletionView: React__default.NamedExoticComponent<CompletionViewProps>;
484
+
485
+ /**
486
+ * Illustration registry — lets the host app supply brand artwork (SVG / Lottie /
487
+ * Image nodes) keyed by name, which the backend references from an InterstitialCard
488
+ * by `illustration`. Keeping the registry in React context (rather than as a card
489
+ * prop) is what keeps the kit dependency-free: the kit never imports react-native-svg
490
+ * or Lottie — the app brings its own nodes and the kit just slots the matching one.
491
+ *
492
+ * <WireOnboarding illustrations={{ "before-after": <BeforeAfter/> }} ... />
493
+ *
494
+ * When the backend names an illustration the registry doesn't have, the
495
+ * InterstitialCard falls back to its `imageUrl` (a plain RN <Image>).
496
+ */
497
+
498
+ type IllustrationRegistry = Record<string, React__default.ReactNode>;
499
+ type IllustrationProviderProps = {
500
+ registry?: IllustrationRegistry;
501
+ children: React__default.ReactNode;
502
+ };
503
+ declare const IllustrationProvider: React__default.FC<IllustrationProviderProps>;
504
+ /** Look up an app-supplied illustration node by name. Returns undefined if absent. */
505
+ declare const useIllustration: (name?: string) => React__default.ReactNode | undefined;
506
+
507
+ /**
508
+ * defaultIllustrations — a tiny, dependency-free fallback registry so an app can
509
+ * render `InterstitialCard` with ZERO illustration code. These are plain
510
+ * <View>/<Text> glyphs (NO react-native-svg, NO Lottie, NO image assets) themed
511
+ * from the active OnboardingTheme, covering the common backend-emitted names.
512
+ *
513
+ * Spread it under your own artwork — your named nodes win:
514
+ *
515
+ * illustrations={{ ...defaultIllustrations, ...myIllustrations }}
516
+ *
517
+ * Names match what the onboarding backend commonly emits via an InterstitialCard's
518
+ * `illustration`: `momentum` (forward-motion arc) and `before-after` (two states).
519
+ * Add more by spreading your own — these are only sensible defaults.
520
+ */
521
+
522
+ /**
523
+ * The default registry. `WireOnboarding`'s `illustrations` prop is
524
+ * `Record<string, ReactNode>`, so these are concrete element nodes.
525
+ */
526
+ declare const defaultIllustrations: Record<string, React__default.ReactNode>;
527
+
528
+ /**
529
+ * Button — the kit's themed primary/outline button, replacing the SDK's hardcoded
530
+ * `Btn`. Every color/radius/font comes from the active theme.
531
+ *
532
+ * Motion (design-refs/EXTRACTED-SPEC.md row 20): "a footer button that answers
533
+ * the tap" — scale 1→0.975 spring on press-in, back on release, native driver.
534
+ * Reduce motion: an opacity dip to 0.85 while pressed, no scale. Disabled keeps
535
+ * the `disabled` token fill with no press handler (unchanged).
536
+ */
537
+
538
+ type Variant = "primary" | "outline";
539
+ type ButtonProps = {
540
+ title: string;
541
+ onPress?: () => void;
542
+ variant?: Variant;
543
+ disabled?: boolean;
544
+ /** Stretch to the container width — for form-style CTAs (e.g. the review form's
545
+ * "Send feedback"). Default false keeps the content-width pill used by cards. */
546
+ fullWidth?: boolean;
547
+ };
548
+ declare const Button: React__default.NamedExoticComponent<ButtonProps>;
549
+
550
+ /**
551
+ * CardHandoff — the card-to-card transition from the Wire Activation design:
552
+ * "cards hand off, they don't cut". When `transitionKey` changes, the outgoing
553
+ * child leaves left (translateX 0→-26, fade, 180ms) in an absolute layer while
554
+ * the incoming child arrives from the right (translateX +26→0, fade, 220ms)
555
+ * delayed by the 80ms exit lead — one moving surface, ~300ms total.
556
+ *
557
+ * Variants (per child):
558
+ * - "slide": questions — the directional stepper motion above.
559
+ * - "spring": the InterstitialCard value beat — scale 0.96→1 spring pop
560
+ * (the shell's springier variant, kept from AnimatedCard).
561
+ * - "fade": loaders — enter/exit as a plain 120ms fade so the "thinking"
562
+ * state hands off to the first card without a hard cut.
563
+ *
564
+ * Reduce motion: every handoff becomes a 120ms crossfade, no translate/scale.
565
+ * All values run on the native driver (transform/opacity only); in-flight
566
+ * animations are stopped when superseded and on unmount.
567
+ *
568
+ * This SUPERSEDES the enter-only AnimatedCard: same keyed-remount contract from
569
+ * OnboardingFlow (the child renderer stays keyed on the message id), plus the
570
+ * paired exit layer the design calls for.
571
+ */
572
+
573
+ type CardHandoffVariant = "slide" | "spring" | "fade";
574
+ type CardHandoffProps = {
575
+ /** Identity of the current child (e.g. the message id). A change runs the handoff. */
576
+ transitionKey: string;
577
+ /** How the CURRENT child enters (and, snapshotted, how it will later exit). */
578
+ variant?: CardHandoffVariant;
579
+ children: React__default.ReactNode;
580
+ };
581
+ declare const CardHandoff: React__default.NamedExoticComponent<CardHandoffProps>;
582
+
583
+ declare const useReducedMotion: () => boolean;
584
+
585
+ /**
586
+ * motionSpec — the single source of truth for every animation value in the kit,
587
+ * extracted 1:1 from the Wire Activation card-interactions design canvas
588
+ * (see design-refs/EXTRACTED-SPEC.md for the row-by-row mapping).
589
+ *
590
+ * Pure constants + pure helpers only — NO react-native import — so the spec is
591
+ * unit-testable under node:test and components can't drift from the design
592
+ * without a red test.
593
+ */
594
+ /** The design system's "ease-wire" curve: cubic-bezier(0.16, 1, 0.3, 1). */
595
+ declare const WIRE_BEZIER: readonly [0.16, 1, 0.3, 1];
596
+ /** Design-system duration tokens (colors_and_type.css --duration-*). */
597
+ declare const DURATION_FAST_MS = 150;
598
+ declare const DURATION_BASE_MS = 300;
599
+ declare const DURATION_SLOW_MS = 500;
600
+ /** Reduce-motion fallback: a short fade to the final frame, never a frozen mid-state. */
601
+ declare const REDUCED_FADE_MS = 120;
602
+ declare const ROW_PRESS_SCALE = 0.975;
603
+ declare const CHIP_PRESS_SCALE = 0.94;
604
+ declare const BUTTON_PRESS_SCALE = 0.975;
605
+ /** Reduce-motion press feedback: opacity dip instead of scale. */
606
+ declare const REDUCED_PRESS_OPACITY = 0.85;
607
+ declare const PRESS_SPRING: {
608
+ readonly friction: 8;
609
+ readonly tension: 220;
610
+ };
611
+ declare const BUTTON_PRESS_SPRING: {
612
+ readonly friction: 8;
613
+ readonly tension: 240;
614
+ };
615
+ declare const SELECT_FILL_MS = 180;
616
+ declare const CHIP_FILL_MS = 160;
617
+ declare const CHECK_SPRING: {
618
+ readonly friction: 6;
619
+ readonly tension: 180;
620
+ };
621
+ declare const CHECK_SCALE_FROM = 0.6;
622
+ declare const CARD_ENTER_MS = 220;
623
+ declare const CARD_EXIT_MS = 180;
624
+ /** The exit starts this long BEFORE the enter (enter is delayed by this). */
625
+ declare const CARD_EXIT_LEAD_MS = 80;
626
+ declare const CARD_SLIDE_PX = 26;
627
+ declare const CARD_ENTER_SCALE_FROM = 0.96;
628
+ /** The interstitial keeps the shell's springier value-beat (AnimatedCard idiom). */
629
+ declare const CARD_SPRING: {
630
+ readonly damping: 14;
631
+ readonly stiffness: 180;
632
+ readonly mass: 1;
633
+ };
634
+ /** Loader-to-card handoff: the loader just fades out. */
635
+ declare const LOADER_HANDOFF_FADE_MS = 120;
636
+ declare const INTERSTITIAL_HEAD_MS = 500;
637
+ declare const INTERSTITIAL_HEAD_STAGGER_MS = 100;
638
+ declare const INTERSTITIAL_ITEM_MS = 300;
639
+ declare const INTERSTITIAL_ITEM_STAGGER_MS = 90;
640
+ declare const INTERSTITIAL_ITEM_BASE_DELAY_MS = 200;
641
+ declare const INTERSTITIAL_CHECK_DELAY_MS = 200;
642
+ declare const INTERSTITIAL_CHECK_SCALE_FROM = 0.4;
643
+ /** Delay before work item `index` begins its reveal (row lands after the head). */
644
+ declare const interstitialItemDelay: (index: number) => number;
645
+ /** Delay before work item `index`'s check pops (200ms after its row starts). */
646
+ declare const interstitialCheckDelay: (index: number) => number;
647
+ declare const STATUS_POP_SPRING: {
648
+ readonly friction: 6;
649
+ readonly tension: 200;
650
+ };
651
+ declare const STATUS_POP_SCALE_FROM = 0.4;
652
+ declare const STATUS_BURST_MS = 500;
653
+ declare const STATUS_BURST_SCALE_FROM = 0.4;
654
+ declare const STATUS_BURST_SCALE_TO = 1.5;
655
+ declare const STATUS_BURST_OPACITY_FROM = 0.5;
656
+ declare const STATUS_COPY_MS = 300;
657
+ declare const STATUS_COPY_STAGGER_MS = 80;
658
+ /** Copy starts settling once the badge pop is visibly underway. */
659
+ declare const STATUS_COPY_BASE_DELAY_MS = 200;
660
+ /** Delay before payoff copy line `index` fades up. */
661
+ declare const statusCopyDelay: (index: number) => number;
662
+ declare const LOADER_PULSE_MS = 1600;
663
+ declare const LOADER_PULSE_MIN_OPACITY = 0.35;
664
+ declare const LOADER_ORBIT_MS = 2400;
665
+ /** Skeleton lines appear only when the fetch runs past this. */
666
+ declare const SKELETON_DELAY_MS = 300;
667
+ declare const SKELETON_PULSE_MS = 1500;
668
+ declare const SKELETON_MIN_OPACITY = 0.45;
669
+ declare const PROGRESS_MS = 400;
670
+ /** Set the global duration multiplier (clamped to 0.1-10; invalid resets to 1). */
671
+ declare const setMotionDurationScale: (scale: number) => void;
672
+ declare const getMotionDurationScale: () => number;
673
+ /** Apply the current duration scale to a spec duration/delay (rounded ms). */
674
+ declare const scaledMs: (ms: number) => number;
675
+ type EntranceSpec = {
676
+ /** Duration to animate with (falls back to the short fade under reduce motion). */
677
+ duration: number;
678
+ /** Whether transforms (translate/scale) may move; false = opacity-only fade. */
679
+ useTransform: boolean;
680
+ };
681
+ /** Entrances: full motion normally; a 120ms opacity-only fade under reduce motion. */
682
+ declare const entranceSpec: (reduced: boolean, durationMs: number) => EntranceSpec;
683
+ /** State changes (select fills etc.): snap instantly under reduce motion. */
684
+ declare const stateChangeDuration: (reduced: boolean, durationMs: number) => number;
685
+ /** Looping/ambient animations run only when motion is allowed. */
686
+ declare const loopEnabled: (reduced: boolean) => boolean;
687
+
688
+ declare const motionSpec_BUTTON_PRESS_SCALE: typeof BUTTON_PRESS_SCALE;
689
+ declare const motionSpec_BUTTON_PRESS_SPRING: typeof BUTTON_PRESS_SPRING;
690
+ declare const motionSpec_CARD_ENTER_MS: typeof CARD_ENTER_MS;
691
+ declare const motionSpec_CARD_ENTER_SCALE_FROM: typeof CARD_ENTER_SCALE_FROM;
692
+ declare const motionSpec_CARD_EXIT_LEAD_MS: typeof CARD_EXIT_LEAD_MS;
693
+ declare const motionSpec_CARD_EXIT_MS: typeof CARD_EXIT_MS;
694
+ declare const motionSpec_CARD_SLIDE_PX: typeof CARD_SLIDE_PX;
695
+ declare const motionSpec_CARD_SPRING: typeof CARD_SPRING;
696
+ declare const motionSpec_CHECK_SCALE_FROM: typeof CHECK_SCALE_FROM;
697
+ declare const motionSpec_CHECK_SPRING: typeof CHECK_SPRING;
698
+ declare const motionSpec_CHIP_FILL_MS: typeof CHIP_FILL_MS;
699
+ declare const motionSpec_CHIP_PRESS_SCALE: typeof CHIP_PRESS_SCALE;
700
+ declare const motionSpec_DURATION_BASE_MS: typeof DURATION_BASE_MS;
701
+ declare const motionSpec_DURATION_FAST_MS: typeof DURATION_FAST_MS;
702
+ declare const motionSpec_DURATION_SLOW_MS: typeof DURATION_SLOW_MS;
703
+ type motionSpec_EntranceSpec = EntranceSpec;
704
+ declare const motionSpec_INTERSTITIAL_CHECK_DELAY_MS: typeof INTERSTITIAL_CHECK_DELAY_MS;
705
+ declare const motionSpec_INTERSTITIAL_CHECK_SCALE_FROM: typeof INTERSTITIAL_CHECK_SCALE_FROM;
706
+ declare const motionSpec_INTERSTITIAL_HEAD_MS: typeof INTERSTITIAL_HEAD_MS;
707
+ declare const motionSpec_INTERSTITIAL_HEAD_STAGGER_MS: typeof INTERSTITIAL_HEAD_STAGGER_MS;
708
+ declare const motionSpec_INTERSTITIAL_ITEM_BASE_DELAY_MS: typeof INTERSTITIAL_ITEM_BASE_DELAY_MS;
709
+ declare const motionSpec_INTERSTITIAL_ITEM_MS: typeof INTERSTITIAL_ITEM_MS;
710
+ declare const motionSpec_INTERSTITIAL_ITEM_STAGGER_MS: typeof INTERSTITIAL_ITEM_STAGGER_MS;
711
+ declare const motionSpec_LOADER_HANDOFF_FADE_MS: typeof LOADER_HANDOFF_FADE_MS;
712
+ declare const motionSpec_LOADER_ORBIT_MS: typeof LOADER_ORBIT_MS;
713
+ declare const motionSpec_LOADER_PULSE_MIN_OPACITY: typeof LOADER_PULSE_MIN_OPACITY;
714
+ declare const motionSpec_LOADER_PULSE_MS: typeof LOADER_PULSE_MS;
715
+ declare const motionSpec_PRESS_SPRING: typeof PRESS_SPRING;
716
+ declare const motionSpec_PROGRESS_MS: typeof PROGRESS_MS;
717
+ declare const motionSpec_REDUCED_FADE_MS: typeof REDUCED_FADE_MS;
718
+ declare const motionSpec_REDUCED_PRESS_OPACITY: typeof REDUCED_PRESS_OPACITY;
719
+ declare const motionSpec_ROW_PRESS_SCALE: typeof ROW_PRESS_SCALE;
720
+ declare const motionSpec_SELECT_FILL_MS: typeof SELECT_FILL_MS;
721
+ declare const motionSpec_SKELETON_DELAY_MS: typeof SKELETON_DELAY_MS;
722
+ declare const motionSpec_SKELETON_MIN_OPACITY: typeof SKELETON_MIN_OPACITY;
723
+ declare const motionSpec_SKELETON_PULSE_MS: typeof SKELETON_PULSE_MS;
724
+ declare const motionSpec_STATUS_BURST_MS: typeof STATUS_BURST_MS;
725
+ declare const motionSpec_STATUS_BURST_OPACITY_FROM: typeof STATUS_BURST_OPACITY_FROM;
726
+ declare const motionSpec_STATUS_BURST_SCALE_FROM: typeof STATUS_BURST_SCALE_FROM;
727
+ declare const motionSpec_STATUS_BURST_SCALE_TO: typeof STATUS_BURST_SCALE_TO;
728
+ declare const motionSpec_STATUS_COPY_BASE_DELAY_MS: typeof STATUS_COPY_BASE_DELAY_MS;
729
+ declare const motionSpec_STATUS_COPY_MS: typeof STATUS_COPY_MS;
730
+ declare const motionSpec_STATUS_COPY_STAGGER_MS: typeof STATUS_COPY_STAGGER_MS;
731
+ declare const motionSpec_STATUS_POP_SCALE_FROM: typeof STATUS_POP_SCALE_FROM;
732
+ declare const motionSpec_STATUS_POP_SPRING: typeof STATUS_POP_SPRING;
733
+ declare const motionSpec_WIRE_BEZIER: typeof WIRE_BEZIER;
734
+ declare const motionSpec_entranceSpec: typeof entranceSpec;
735
+ declare const motionSpec_getMotionDurationScale: typeof getMotionDurationScale;
736
+ declare const motionSpec_interstitialCheckDelay: typeof interstitialCheckDelay;
737
+ declare const motionSpec_interstitialItemDelay: typeof interstitialItemDelay;
738
+ declare const motionSpec_loopEnabled: typeof loopEnabled;
739
+ declare const motionSpec_scaledMs: typeof scaledMs;
740
+ declare const motionSpec_setMotionDurationScale: typeof setMotionDurationScale;
741
+ declare const motionSpec_stateChangeDuration: typeof stateChangeDuration;
742
+ declare const motionSpec_statusCopyDelay: typeof statusCopyDelay;
743
+ declare namespace motionSpec {
744
+ export { motionSpec_BUTTON_PRESS_SCALE as BUTTON_PRESS_SCALE, motionSpec_BUTTON_PRESS_SPRING as BUTTON_PRESS_SPRING, motionSpec_CARD_ENTER_MS as CARD_ENTER_MS, motionSpec_CARD_ENTER_SCALE_FROM as CARD_ENTER_SCALE_FROM, motionSpec_CARD_EXIT_LEAD_MS as CARD_EXIT_LEAD_MS, motionSpec_CARD_EXIT_MS as CARD_EXIT_MS, motionSpec_CARD_SLIDE_PX as CARD_SLIDE_PX, motionSpec_CARD_SPRING as CARD_SPRING, motionSpec_CHECK_SCALE_FROM as CHECK_SCALE_FROM, motionSpec_CHECK_SPRING as CHECK_SPRING, motionSpec_CHIP_FILL_MS as CHIP_FILL_MS, motionSpec_CHIP_PRESS_SCALE as CHIP_PRESS_SCALE, motionSpec_DURATION_BASE_MS as DURATION_BASE_MS, motionSpec_DURATION_FAST_MS as DURATION_FAST_MS, motionSpec_DURATION_SLOW_MS as DURATION_SLOW_MS, type motionSpec_EntranceSpec as EntranceSpec, motionSpec_INTERSTITIAL_CHECK_DELAY_MS as INTERSTITIAL_CHECK_DELAY_MS, motionSpec_INTERSTITIAL_CHECK_SCALE_FROM as INTERSTITIAL_CHECK_SCALE_FROM, motionSpec_INTERSTITIAL_HEAD_MS as INTERSTITIAL_HEAD_MS, motionSpec_INTERSTITIAL_HEAD_STAGGER_MS as INTERSTITIAL_HEAD_STAGGER_MS, motionSpec_INTERSTITIAL_ITEM_BASE_DELAY_MS as INTERSTITIAL_ITEM_BASE_DELAY_MS, motionSpec_INTERSTITIAL_ITEM_MS as INTERSTITIAL_ITEM_MS, motionSpec_INTERSTITIAL_ITEM_STAGGER_MS as INTERSTITIAL_ITEM_STAGGER_MS, motionSpec_LOADER_HANDOFF_FADE_MS as LOADER_HANDOFF_FADE_MS, motionSpec_LOADER_ORBIT_MS as LOADER_ORBIT_MS, motionSpec_LOADER_PULSE_MIN_OPACITY as LOADER_PULSE_MIN_OPACITY, motionSpec_LOADER_PULSE_MS as LOADER_PULSE_MS, motionSpec_PRESS_SPRING as PRESS_SPRING, motionSpec_PROGRESS_MS as PROGRESS_MS, motionSpec_REDUCED_FADE_MS as REDUCED_FADE_MS, motionSpec_REDUCED_PRESS_OPACITY as REDUCED_PRESS_OPACITY, motionSpec_ROW_PRESS_SCALE as ROW_PRESS_SCALE, motionSpec_SELECT_FILL_MS as SELECT_FILL_MS, motionSpec_SKELETON_DELAY_MS as SKELETON_DELAY_MS, motionSpec_SKELETON_MIN_OPACITY as SKELETON_MIN_OPACITY, motionSpec_SKELETON_PULSE_MS as SKELETON_PULSE_MS, motionSpec_STATUS_BURST_MS as STATUS_BURST_MS, motionSpec_STATUS_BURST_OPACITY_FROM as STATUS_BURST_OPACITY_FROM, motionSpec_STATUS_BURST_SCALE_FROM as STATUS_BURST_SCALE_FROM, motionSpec_STATUS_BURST_SCALE_TO as STATUS_BURST_SCALE_TO, motionSpec_STATUS_COPY_BASE_DELAY_MS as STATUS_COPY_BASE_DELAY_MS, motionSpec_STATUS_COPY_MS as STATUS_COPY_MS, motionSpec_STATUS_COPY_STAGGER_MS as STATUS_COPY_STAGGER_MS, motionSpec_STATUS_POP_SCALE_FROM as STATUS_POP_SCALE_FROM, motionSpec_STATUS_POP_SPRING as STATUS_POP_SPRING, motionSpec_WIRE_BEZIER as WIRE_BEZIER, motionSpec_entranceSpec as entranceSpec, motionSpec_getMotionDurationScale as getMotionDurationScale, motionSpec_interstitialCheckDelay as interstitialCheckDelay, motionSpec_interstitialItemDelay as interstitialItemDelay, motionSpec_loopEnabled as loopEnabled, motionSpec_scaledMs as scaledMs, motionSpec_setMotionDurationScale as setMotionDurationScale, motionSpec_stateChangeDuration as stateChangeDuration, motionSpec_statusCopyDelay as statusCopyDelay };
745
+ }
746
+
747
+ declare const ChipSelectCard: WireAIComponent;
748
+
749
+ declare const TextInputCard: WireAIComponent;
750
+
751
+ declare const SelectionCard: WireAIComponent;
752
+
753
+ declare const StatusCard: WireAIComponent;
754
+
755
+ declare const NumberStepperCard: WireAIComponent;
756
+
757
+ declare const InterstitialCard: WireAIComponent;
758
+
759
+ /**
760
+ * onboardingComponents — the constrained set of themed cards the kit registers
761
+ * with the SDK. The backend's `allowed_components` must be a subset of these
762
+ * names so every card the agent asks for has a themed renderer.
763
+ */
764
+
765
+ declare const onboardingComponents: WireAIComponent[];
766
+
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
+ /**
922
+ * OnboardingFlow — the inner engine. Mirrors the proven production-app usage of the SDK
923
+ * (useWireAIThread + useWireAIAction + ComponentRenderer) and layers on the kit's
924
+ * shell, progress, and per-step validators.
925
+ *
926
+ * Lifecycle per turn:
927
+ * 1. Auto-send `startMessage` once → backend returns the first card.
928
+ * 2. Show LoadingBlock until the first card arrives AND between every turn
929
+ * (while `isLoading`); on error/timeout, auto-retry up to `maxRetries`, then
930
+ * degrade to the host's static `fallbackFlow` (preferred), else `onError`, else
931
+ * an inline ErrorBlock — so a generation failure never breaks onboarding.
932
+ * 3. Render the latest card inside OnboardingScaffold. The progress bar follows
933
+ * an asymptotic curve of the step index — it never reveals a total.
934
+ * 4. Before any answer is sent, if the current screen's question key has a
935
+ * validator, run it; on failure show the error inline and DON'T advance.
936
+ * 5. On the terminal StatusCard, show CompletionView (the recap of what the app
937
+ * will do for the user) and fire onComplete only when they tap its CTA.
938
+ *
939
+ * Lifecycle events (started / per-turn / error) are surfaced via `onEvent` so a
940
+ * host can keep its analytics even though the kit owns the thread loop.
941
+ */
942
+
943
+ type OnboardingFlowProps = {
944
+ validators?: Record<string, StepValidator>;
945
+ onComplete: (result: OnboardingResult) => void;
946
+ onSkip?: () => void;
947
+ onError?: (err: unknown) => void;
948
+ onEvent?: (event: OnboardingEvent) => void;
949
+ copy?: Partial<OnboardingCopy>;
950
+ approxScreens?: number;
951
+ startMessage: string;
952
+ startTimeoutMs: number;
953
+ /** Static onboarding rendered when the AI flow fails and retries are exhausted. */
954
+ fallbackFlow?: React__default.ReactNode;
955
+ /** Consecutive failures to auto-retry before degrading. */
956
+ maxRetries: number;
957
+ /** Where to POST device-only client events (`dropped`). Omit to disable reporting. */
958
+ reportTarget?: ClientEventTarget;
959
+ /** Session id shared with the backend (A2A contextId), so `dropped` correlates. */
960
+ sessionId: string;
961
+ /**
962
+ * Device snapshot + host-injected user context, attached to every client event so the
963
+ * backend can segment the funnel by device. Old servers ignore the extra fields.
964
+ */
965
+ clientContext?: {
966
+ device?: DeviceContext;
967
+ userContext?: Record<string, string | number | boolean>;
968
+ /** The host's opaque user id (see WireOnboarding `userId`), rides `dropped`/`client_fallback`. */
969
+ userId?: string;
970
+ };
971
+ /**
972
+ * True when `sessionId` was restored from persisted storage (app killed mid-flow).
973
+ * The kickoff then sends the skip sentinel instead of `startMessage` — the server
974
+ * would record the literal start text as the ANSWER to the pending question, while
975
+ * the sentinel advances one card recording nothing — and fires `resumed` instead of
976
+ * `started` so host funnels don't double-count the session.
977
+ */
978
+ resumed?: boolean;
979
+ };
980
+ declare const OnboardingFlow: React__default.FC<OnboardingFlowProps>;
981
+
982
+ /**
983
+ * deriveAnswers — read the user's structured choices off the thread.
984
+ *
985
+ * The cards send answers back as natural-language strings (the SDK's
986
+ * useWireAIAction wraps them, e.g. `I selected: ["Food","Travel"]`,
987
+ * `My answer is: "Casablanca"`). We pair each rendered question with the reply
988
+ * that followed it and key the answer by the backend's progress `key` when
989
+ * present (so `username`/`interests`/`city` come back under their real keys),
990
+ * else by the question text. No second LLM call — the values ARE the data.
991
+ */
992
+
993
+ declare const deriveAnswers: (messages: Message[]) => Record<string, unknown>;
994
+
995
+ /**
996
+ * readProgress — pull the backend's progress metadata off a render response.
997
+ *
998
+ * The managed backend rides `{ step, total, key }` inside the Wire envelope's
999
+ * `props.progress`. That's the only field that survives the SDK's strict client
1000
+ * validation (extra top-level keys get folded into props; the card's own schema
1001
+ * silently strips `progress` from its render props, but it stays readable on
1002
+ * `response.props`). All fields are optional so an older backend just falls back
1003
+ * to the local counter.
1004
+ */
1005
+
1006
+ type PartialProgress = {
1007
+ step?: number;
1008
+ total?: number;
1009
+ key?: string;
1010
+ skippable?: boolean;
1011
+ };
1012
+ declare const readProgress: (response?: WireAIResponse) => PartialProgress;
1013
+
1014
+ /**
1015
+ * Resolve the tenant's feature flags. Attempts the network once; on ANY failure returns the
1016
+ * cached value (any age), else the all-on defaults. On success, persists the fresh value to the
1017
+ * cache (fire-and-forget) and returns it. Never throws.
1018
+ *
1019
+ * const flags = await fetchWireFeatures({ serverUrl, apiKey, appId, storage });
1020
+ * if (!flags.coachmarks.enabled) { /* skip the tour * / }
1021
+ */
1022
+ declare const fetchWireFeatures: (config?: WireFeaturesConfig) => Promise<WireFeatures>;
1023
+
1024
+ declare const useWireFeatures: (config?: WireFeaturesConfig) => WireFeatures;
1025
+
1026
+ /**
1027
+ * WireFeaturesProvider + useResolvedFeatures — the OPTIONAL one-fetch-serves-all-modules layer.
1028
+ *
1029
+ * Mount `WireFeaturesProvider` once near your app root with the tenant `config` and it fetches the
1030
+ * flags ONCE; every gated surface below it (coachmarks / showcase / reviews) reads the same
1031
+ * resolved value from context instead of each fetching its own. It is entirely optional: with no
1032
+ * provider, every surface falls back to its own lazy fetch (when given a config) or to the all-on
1033
+ * defaults, so modules work with zero setup and a host that never adopts flags is unchanged.
1034
+ *
1035
+ * NOTE on the built npm subpaths: React context is shared across the kit's subpath entries in the
1036
+ * 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
1038
+ * resolved flags (or a `featuresConfig`) to each surface explicitly instead of relying on context.
1039
+ */
1040
+
1041
+ interface WireFeaturesProviderProps {
1042
+ /** Tenant creds (+ optional storage) to fetch the flags once for the whole tree. */
1043
+ config?: WireFeaturesConfig;
1044
+ /**
1045
+ * Pre-resolved flags to provide directly instead of fetching (e.g. you already have them, or
1046
+ * you drive them from your own remote-config). Takes precedence over `config`.
1047
+ */
1048
+ flags?: WireFeatures;
1049
+ children: React__default.ReactNode;
1050
+ }
1051
+ /**
1052
+ * Fetch (or accept) the flags once and broadcast them to descendants. The provider VALUE is
1053
+ * memoized (performance rule #6) so a fresh object each render doesn't re-render every consumer.
1054
+ */
1055
+ declare const WireFeaturesProvider: React__default.FC<WireFeaturesProviderProps>;
1056
+ /** Raw context read — `null` when no `WireFeaturesProvider` is mounted above. */
1057
+ declare const useWireFeaturesContext: () => WireFeatures | null;
1058
+ /** Options for `useResolvedFeatures`: an explicit override and/or a lazy-fetch config. */
1059
+ interface ResolveFeaturesOptions {
1060
+ /** Explicit flags — win over everything (a surface's own `features` prop). */
1061
+ flags?: WireFeatures;
1062
+ /** Lazy-fetch config, used ONLY when there is no explicit `flags` and no provider above. */
1063
+ config?: WireFeaturesConfig;
1064
+ }
1065
+ /**
1066
+ * The resolution every gated surface uses. Precedence:
1067
+ * 1. explicit `options.flags` (a surface's own prop) →
1068
+ * 2. the nearest `WireFeaturesProvider` (context) →
1069
+ * 3. a lazy fetch from `options.config` (serverUrl + apiKey) →
1070
+ * 4. the all-on defaults.
1071
+ * Always fail-open, always a stable reference for a given value (so it's safe in effect deps).
1072
+ */
1073
+ declare const useResolvedFeatures: (options?: ResolveFeaturesOptions) => WireFeatures;
1074
+
1075
+ /**
1076
+ * The fail-open defaults + a tolerant parser for the server's flags payload.
1077
+ *
1078
+ * THE CONTRACT: fail-open, never fail-dark. An absent server object, a missing module key, a
1079
+ * malformed value, or an unreachable endpoint must resolve to the MOST PERMISSIVE state — every
1080
+ * module ON, onboarding `ai` — so a control-plane outage can only ever be more permissive than
1081
+ * the dashboard intends. It can never dark a tenant's activation. Matches the server's own
1082
+ * documented fail-open default (FEATURES.md: "local defaults — every module on, onboarding ai").
1083
+ */
1084
+
1085
+ /**
1086
+ * ALL ON, onboarding `ai`. Frozen + module-level so its reference is stable across every call
1087
+ * (consumers that list resolved flags in an effect dep array never loop on a fresh identity).
1088
+ */
1089
+ declare const defaultWireFeatures: WireFeatures;
1090
+ /**
1091
+ * Narrow the server's (untrusted) JSON to a complete `WireFeatures`. Every field is filled with
1092
+ * its fail-open default when the server omits it or sends the wrong type, so a partial or
1093
+ * malformed payload degrades PERMISSIVELY rather than throwing. A non-object input (null, array,
1094
+ * string) → the full defaults. Never throws.
1095
+ */
1096
+ declare const parseWireFeatures: (raw: unknown) => WireFeatures;
1097
+ /** Value-equality of two resolved flags — lets the hook hold a stable reference across churn. */
1098
+ declare const featuresEqual: (a: WireFeatures, b: WireFeatures) => boolean;
1099
+
1100
+ /**
1101
+ * TTL for a cached flags entry, aligned to the server's `Cache-Control: private, max-age=300`.
1102
+ * A cache younger than this is "fresh"; older is "stale" (still served on a fetch error).
1103
+ */
1104
+ declare const DEFAULT_FEATURES_TTL_MS = 300000;
1105
+ /** Storage key for a tenant's cached flags, e.g. `wireai:features:myelino`. */
1106
+ declare const featuresCacheKey: (appId?: string) => string;
1107
+ /** A cached flags entry: the resolved flags + the epoch-ms they were fetched. */
1108
+ interface CachedFeatures {
1109
+ features: WireFeatures;
1110
+ /** Age of the entry in ms at read time. */
1111
+ ageMs: number;
1112
+ }
1113
+ /** Whether a cache entry is still within the TTL (server max-age aligned). */
1114
+ declare const isFeaturesFresh: (ageMs: number, ttlMs?: number) => boolean;
1115
+ /**
1116
+ * Read the cached flags for `key`, or `undefined` when absent/corrupt/timed-out. Returns the
1117
+ * value AND its age so a caller can apply a TTL (fresh) or ignore it (stale-while-error). Never
1118
+ * throws; never hangs past the read timeout. The stored value is re-parsed through
1119
+ * `parseWireFeatures`, so a partially-corrupt entry still degrades fail-open.
1120
+ */
1121
+ declare const readCachedFeatures: (storage: WireOnboardingStorage, key: string) => Promise<CachedFeatures | undefined>;
1122
+ /** Persist `{ features, ts }` under `key` — fire-and-forget, all errors swallowed. */
1123
+ declare const writeCachedFeatures: (storage: WireOnboardingStorage, key: string, features: WireFeatures) => void;
1124
+
1125
+ /**
1126
+ * wireConfigFromEnv — stop re-authoring env gating in every app. Reads the Wire AI
1127
+ * tenant key + server URL from the standard Expo public env vars and returns a
1128
+ * ready `WireOnboardingConfig`, or `null` when the key/URL are missing (so a host
1129
+ * can fall through to a static onboarding without writing the `if (!key)` itself).
1130
+ *
1131
+ * EXPO_PUBLIC_WIREAI_API_KEY=... (required)
1132
+ * EXPO_PUBLIC_WIREAI_SERVER_URL=... (required)
1133
+ *
1134
+ * const config = wireConfigFromEnv({ appId: "morrow" });
1135
+ * if (!config) return <StaticOnboarding />;
1136
+ * return <WireOnboarding config={config} ... />;
1137
+ *
1138
+ * `appId` defaults to `EXPO_PUBLIC_WIREAI_APP_ID` (or "default"); pass overrides to
1139
+ * set `appId`/`metadata` or to substitute the key/URL programmatically.
1140
+ */
1141
+
1142
+ type WireConfigOverrides = Partial<WireOnboardingConfig>;
1143
+ declare const wireConfigFromEnv: (overrides?: WireConfigOverrides) => WireOnboardingConfig | null;
1144
+
1145
+ type OnboardingFlagOptions = {
1146
+ /**
1147
+ * Optional remote kill-switch the app already read (Firebase/LD/etc.). When `false`, it
1148
+ * disables onboarding even though transport is present. Omit (or `true`) to leave it on.
1149
+ */
1150
+ remote?: boolean;
1151
+ /**
1152
+ * Config overrides forwarded to `wireConfigFromEnv` for the transport-present check
1153
+ * (e.g. a programmatic `apiKey`/`serverUrl`). Usually omitted.
1154
+ */
1155
+ config?: WireConfigOverrides;
1156
+ };
1157
+ declare const isOnboardingEnabled: (opts?: OnboardingFlagOptions) => boolean;
1158
+
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
+ /**
1200
+ * attribution — forward install/ad attribution to the onboarding agent so it can tailor the
1201
+ * first question by acquisition source (e.g. a paid-campaign install vs an organic one). The
1202
+ * channel already exists: `WireOnboardingConfig.metadata` is merged into every A2A request by
1203
+ * `WireOnboarding`. No production app populated it before; this gives every app a standard shape.
1204
+ *
1205
+ * The app reads its raw attribution (AppsFlyer / Branch / deferred deep link — an app concern,
1206
+ * not the kit's) and passes the normalized result as `metadata`:
1207
+ *
1208
+ * const config = wireConfigFromEnv({
1209
+ * appId: "morrow-self",
1210
+ * metadata: attributionMetadata({ source: conv.mediaSource, campaign: conv.campaign, isOrganic: conv.isOrganic }),
1211
+ * });
1212
+ *
1213
+ * The backend sees `metadata.attribution = { source, campaign, ... }`.
1214
+ */
1215
+ /** Normalized acquisition attribution. All fields optional — pass what the SDK gives you. */
1216
+ type OnboardingAttribution = {
1217
+ /** Media source / network, e.g. "appsflyer", "branch", "organic". */
1218
+ source?: string;
1219
+ campaign?: string;
1220
+ adset?: string;
1221
+ ad?: string;
1222
+ /** Deferred deep-link slug / path that routed the install. */
1223
+ deepLink?: string;
1224
+ isFirstLaunch?: boolean;
1225
+ isOrganic?: boolean;
1226
+ /** Escape hatch for SDK-specific fields not covered above. */
1227
+ raw?: Record<string, unknown>;
1228
+ };
1229
+ /**
1230
+ * Wrap attribution into the `{ attribution: {...} }` metadata the agent reads. Drops `undefined`
1231
+ * fields so the request payload stays compact. Returns `{ attribution: {} }` for empty input
1232
+ * (still a valid, harmless metadata object).
1233
+ */
1234
+ declare const attributionMetadata: (a: OnboardingAttribution) => {
1235
+ attribution: Record<string, unknown>;
1236
+ };
1237
+
1238
+ /** Max accepted user-id length. Longer strings are truncated (never rejected). Keep in sync
1239
+ * with the server's `USER_ID_MAX_LENGTH` (analytics/events.py). */
1240
+ declare const USER_ID_MAX_LENGTH = 128;
1241
+ /**
1242
+ * Normalize a host-supplied user id: trim, drop empty, and cap at {@link USER_ID_MAX_LENGTH}.
1243
+ * Returns `undefined` for a missing/blank/non-string value so callers can `if (id)`-gate.
1244
+ * PII is a host concern — this only bounds length, it does not (and cannot) detect an email.
1245
+ */
1246
+ declare const sanitizeUserId: (raw: unknown) => string | undefined;
1247
+ /** Options for {@link identifyOnboarding}. */
1248
+ type IdentifyOnboardingOptions = {
1249
+ /** Tenant transport, same shape as `WireOnboardingConfig` (only these two fields are used). */
1250
+ config: {
1251
+ serverUrl: string;
1252
+ apiKey: string;
1253
+ };
1254
+ /** The host's opaque user id to bind. Trimmed + capped; NO PII. */
1255
+ userId: string;
1256
+ /**
1257
+ * The onboarding session id to bind to (the A2A contextId) — the `contextId` field carried on
1258
+ * the `started`/`resumed` `onEvent`. Pass this when you captured it there. Required after the
1259
+ * flow COMPLETED, since completion clears the persisted session. Wins over the storage lookup.
1260
+ */
1261
+ contextId?: string;
1262
+ /**
1263
+ * The SAME host storage you passed to `<WireOnboarding storage={…} />`. When `contextId` is
1264
+ * omitted, the helper reads the persisted contextId from it (works while the session is still
1265
+ * persisted — i.e. dropped or mid-flow, before completion clears it).
1266
+ */
1267
+ storage?: WireOnboardingStorage;
1268
+ /** App id, to derive the default storage key `wireai:session:<appId>` when reading from storage. */
1269
+ appId?: string;
1270
+ /** Storage key override — pass the same `persistKey` you gave `<WireOnboarding>`, if any. */
1271
+ persistKey?: string;
1272
+ };
1273
+ /**
1274
+ * Attach a host user id to an onboarding session AFTER the fact (post-registration), by sending
1275
+ * an `identify` client event to `/v1/events`. Resolves the contextId from an explicit
1276
+ * `contextId` or, failing that, from the persisted session in the host `storage`.
1277
+ *
1278
+ * Fire-and-forget under the hood (never throws, never blocks onboarding). Resolves to `true`
1279
+ * when an identify event was dispatched, `false` when it couldn't (no user id, no server url,
1280
+ * or no resolvable contextId).
1281
+ */
1282
+ declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
1283
+
1284
+ /**
1285
+ * reportSessionStart — the ONE standard emitter for "the user opened the app again".
1286
+ *
1287
+ * Every app-open posts a single `app.session_started` event to the Wire analytics backend
1288
+ * (`POST {serverUrl}/v1/events`). That event does two jobs at once, with ZERO server changes:
1289
+ *
1290
+ * 1. SESSION MAPPING. It carries the host's opaque `userId` + a stable `deviceKey`, so the
1291
+ * backend can group a user's (or a pre-auth device's) opens over time — "when did this
1292
+ * user last use the app, and how many times". The device_key links the pre-auth opens to
1293
+ * the user once `userId` arrives (here or via `identifyOnboarding`).
1294
+ * 2. QUESTIONNAIRE / RETENTION FIRING. It flows into the SAME event stream the server's
1295
+ * decision engines read, so a questionnaire trigger `{event:"app.session_started", min_count:N}`
1296
+ * (matched within a session) or a questionnaire `min_sessions:N` (distinct device sessions)
1297
+ * fires on the user's Nth open with no new server endpoint.
1298
+ *
1299
+ * WIRE CONTRACT (server: analytics/events.py + routers/onboarding.py `POST /v1/events`):
1300
+ * The event is stored as `event_type='app_event'`, `question_key='app.session_started'` — the
1301
+ * generic app.* namespace the reviews wave shipped. The server's `_event_name(ev)` returns the
1302
+ * `question_key` for an `app_event`, which is what a trigger's `event` string matches. We do NOT
1303
+ * send `event_type:'app.session_started'` — that name is not in the server's EVENT_TYPES and
1304
+ * would be rejected. `device_key` rides in the non-PII `user_context` bucket, where the server's
1305
+ * `_event_device_key(ev)` reads it to group a device's sessions. `app_id` + `environment` are
1306
+ * filled server-side from the resolving key (never sent here).
1307
+ *
1308
+ * SESSION ID DISTINCTION (important): the `session_id` on this event is a PER-OPEN id (a fresh
1309
+ * `makeSessionId()` each app-open) — it is NOT the onboarding A2A `contextId`. Onboarding runs
1310
+ * ONCE (first launch) and owns its own context id; session-start fires on EVERY open, so it needs
1311
+ * its own per-open id. Grouping over time is done by `device_key`/`user_id`, not by session_id.
1312
+ *
1313
+ * Fire-and-forget: like every analytics path in the kit, this never throws into the UI, never
1314
+ * awaits, and swallows a missing target / bad URL / missing fetch / network error. Analytics must
1315
+ * never be able to break the app.
1316
+ */
1317
+
1318
+ /** The canonical event name for an app-open. A trigger keys off this exact string. */
1319
+ declare const SESSION_STARTED_EVENT: "app.session_started";
1320
+ /** Options for {@link reportSessionStart}. Everything except `target` is optional so a pre-auth
1321
+ * open (no user yet) is a valid, device-only session. */
1322
+ interface ReportSessionStartOptions {
1323
+ /** Where to POST — the tenant transport (`serverUrl` + `apiKey`), same as `WireOnboardingConfig`. */
1324
+ target: ClientEventTarget | undefined;
1325
+ /**
1326
+ * The per-open session id. Defaults to a fresh `makeSessionId()`. Pass your own ONLY to
1327
+ * dedupe re-renders of the same open (the {@link useSessionStart} hook does exactly that).
1328
+ * This is NOT the onboarding contextId — see the file header.
1329
+ */
1330
+ sessionId?: string;
1331
+ /** The host's OPAQUE pseudonymous user id (their internal id, NOT an email/PII). Sanitized +
1332
+ * capped here; omitted on a pre-auth open (device-only session). */
1333
+ userId?: string;
1334
+ /** A stable, non-PII device id the host owns. Rides in `user_context.device_key` so the server
1335
+ * groups this device's sessions (the min_sessions signal). Host-supplied, same as reviews. */
1336
+ deviceKey?: string;
1337
+ /** The host's own local open-counter value for this open (1 on the first ever open). Drives the
1338
+ * `returning` flag and lets analytics read "Nth session" without server-side counting. */
1339
+ sessionCount?: number;
1340
+ /** Host app version (e.g. "1.4.2"), if cheaply available. Segments retention by release. */
1341
+ appVersion?: string;
1342
+ /** Platform string (e.g. "ios"), if cheaply available. */
1343
+ platform?: string;
1344
+ /** An optional richer device snapshot (from `collectDeviceContext()`); the hook fills this. */
1345
+ device?: DeviceContext;
1346
+ /** Small non-PII extras, stored as a JSON string in the event `meta`. */
1347
+ meta?: Record<string, unknown>;
1348
+ /** Set `false` to bypass the once-per-open guard (default on). See {@link resetSessionStartGuard}. */
1349
+ once?: boolean;
1350
+ }
1351
+ /** Test-only: forget every emitted session id so a unit test starts from a clean guard. */
1352
+ declare const resetSessionStartGuard: () => void;
1353
+ /**
1354
+ * Emit one `app.session_started` event for this app-open. Fire-and-forget; returns immediately.
1355
+ * Idempotent per `sessionId` (unless `once:false`) so a re-render can never double-fire.
1356
+ */
1357
+ declare const reportSessionStart: (opts: ReportSessionStartOptions) => void;
1358
+
1359
+ /** A foreground after at least this long in the background counts as a NEW app-open (30 min). */
1360
+ declare const BACKGROUND_SESSION_MS: number;
1361
+ /** Tenant transport + optional host app version. Same creds as `WireOnboardingConfig`. */
1362
+ interface SessionStartConfig {
1363
+ /** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
1364
+ serverUrl?: string;
1365
+ /** Tenant API key; sent as `Authorization: Bearer`. */
1366
+ apiKey?: string;
1367
+ /** Host app version (e.g. "1.4.2"), forwarded for release segmentation. Optional. */
1368
+ appVersion?: string;
1369
+ }
1370
+ /** Per-open identity the host supplies. All optional: a pre-auth open is device-only. */
1371
+ interface UseSessionStartOptions {
1372
+ /** The host's opaque pseudonymous user id (NOT PII). Omitted before the user authenticates. */
1373
+ userId?: string;
1374
+ /** The host's local open-counter value for this open. Drives `returning` + "Nth session". */
1375
+ sessionCount?: number;
1376
+ /** A stable, non-PII device id the host owns. Groups this device's sessions server-side. */
1377
+ deviceKey?: string;
1378
+ /** Small non-PII extras forwarded on the event `meta`. */
1379
+ meta?: Record<string, unknown>;
1380
+ /** Set `false` to disable firing (e.g. behind a consent gate). Default enabled. */
1381
+ enabled?: boolean;
1382
+ }
1383
+ /**
1384
+ * Fire `app.session_started` on mount and on every real foreground-after-background. Returns
1385
+ * nothing — it is a side-effecting hook. Safe to call with inline options (values are read
1386
+ * through a ref, so changing `userId`/`sessionCount` never re-fires a session).
1387
+ */
1388
+ declare const useSessionStart: (config: SessionStartConfig | undefined, options?: UseSessionStartOptions) => void;
1389
+
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 };