@appilots/sdk 0.11.3 → 0.13.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.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,891 @@
1
- import { P as PartialThemeTokens, A as AppilotsThemeTokens, a as AppilotsLocale, b as AgentAction, I as InteractionElementEntry, C as ComponentRegistry, c as AgentPermissions, S as ScreenSnapshot, F as FieldEntry, d as AppilotsEvent } from './index-BR-33WMN.js';
2
- export { e as AgentActionType, f as AgentMessage, g as AppilotsClient, h as AppilotsClientOptions, i as AppilotsConfig, j as AppilotsEventHandler, k as AppilotsEventType, l as AppilotsProvider, m as AppilotsProviderProps, n as AppilotsTraceEntry, o as AppilotsUser, B as BreadcrumbItem, p as BreadcrumbState, q as ButtonSnapshot, r as ChatMessage, s as ChoiceGroupSnapshot, t as ChoiceOptionSnapshot, u as ComponentEntry, v as ComponentKind, E as EscalationState, w as FormFillPayload, x as InputSnapshot, y as InteractionElementListContext, z as InteractionElementSnapshot, L as ListItemSnapshot, D as ListSnapshot, M as MessageRole, N as NavigationPayload, R as RateLimitedError, G as RemotePersonalization, H as SliderEntry, J as SuggestedPrompt, T as TargetEntry, K as ToggleEntry, O as ToggleSnapshot, U as UIInteractionPayload, Q as UseAppilotsFieldOptions, V as UseAppilotsSliderOptions, W as UseAppilotsTargetOptions, X as UseAppilotsToggleOptions, Y as UseSuggestedPromptsOptions, Z as UseSuggestedPromptsReturn, _ as clearAppilotsDebugTraces, $ as componentRegistry, a0 as createComponentRegistry, a1 as defaultDarkTheme, a2 as defaultLightTheme, a3 as describeAction, a4 as getAppilotsDebugTraces, a5 as humanizeError, a6 as mergeThemeTokens, a7 as recordAppilotsDebugTrace, a8 as subscribeAppilotsDebugTraces, a9 as useAppilots, aa as useAppilotsActions, ab as useAppilotsChat, ac as useAppilotsContext, ad as useAppilotsField, ae as useAppilotsNavigation, af as useAppilotsSlider, ag as useAppilotsTarget, ah as useAppilotsToggle, ai as useSuggestedPrompts } from './index-BR-33WMN.js';
1
+ import { A as AppilotsTraceEntry, a as AgentAction, b as AppilotsLocale, P as PartialThemeTokens, c as AppilotsThemeTokens, C as ComponentRegistry, d as AppilotsSessionStorage, e as AgentPermissions, F as FieldEntry, f as AppilotsEvent } from './index-CI4jmSoP.js';
2
+ export { g as AgentActionType, h as AgentMessage, i as AppilotsClient, j as AppilotsClientOptions, k as AppilotsConfig, l as AppilotsElementProps, m as AppilotsEventHandler, n as AppilotsEventType, o as AppilotsProvider, p as AppilotsProviderProps, q as AppilotsUser, r as ChatMessage, s as ComponentEntry, t as ComponentKind, E as EscalationState, u as FormFillPayload, M as MessageRole, N as NavigationPayload, R as RateLimitedError, v as RemotePersonalization, S as SliderEntry, w as SuggestedPrompt, T as TargetEntry, x as ToggleEntry, U as UIInteractionPayload, y as UseAppilotsFieldOptions, z as UseAppilotsSliderOptions, B as UseAppilotsTargetOptions, D as UseAppilotsToggleOptions, G as UseSuggestedPromptsOptions, H as UseSuggestedPromptsReturn, I as componentRegistry, J as createComponentRegistry, K as defaultDarkTheme, L as defaultLightTheme, O as mergeThemeTokens, Q as useAppilots, V as useAppilotsActions, W as useAppilotsChat, X as useAppilotsContext, Y as useAppilotsField, Z as useAppilotsNavigation, _ as useAppilotsSlider, $ as useAppilotsTarget, a0 as useAppilotsToggle, a1 as useSuggestedPrompts } from './index-CI4jmSoP.js';
3
3
  import * as React$1 from 'react';
4
4
  import React__default from 'react';
5
5
  export { AppilotsNavigationContainer, NavigationConfig, ScreenMetadata, registerScreen, setCurrentScreen } from './navigation/index.js';
6
6
 
7
+ /** Source facts, not an executable locator or proof of a business outcome. */
8
+ interface ControlEvidence {
9
+ version: 1;
10
+ siteId: string;
11
+ component: string;
12
+ icons: string[];
13
+ handler?: string;
14
+ calls: string[];
15
+ argumentBindings: string[];
16
+ conditions: string[];
17
+ nativeConfirmation?: {
18
+ title?: string;
19
+ destructiveOption: boolean;
20
+ };
21
+ }
22
+
23
+ type ActionPresentationState = 'pending' | 'running' | 'success' | 'failed';
24
+
25
+ /**
26
+ * Snapshot types — the platform-agnostic "view model" of what the user
27
+ * is currently seeing on screen, sent to the AI agent so it has
28
+ * accurate visible state.
29
+ *
30
+ * Every platform walker (React Native fiber walk, web DOM walk, ...)
31
+ * produces this same shape; the wire contract it feeds is
32
+ * `agentSnapshotSchema` in `@appilots/shared`.
33
+ */
34
+ /**
35
+ * How much the walker actually knows about an element's identifier.
36
+ *
37
+ * The observation used to say nothing here, so `btn-3` — an ordinal the
38
+ * walker invented — read to the model exactly like `testID="submit-order"`,
39
+ * which a developer wrote down on purpose. That is how a confident press on
40
+ * the wrong control happens.
41
+ *
42
+ * - `declared` the app named it: `testID` / `data-testid` /
43
+ * `accessibilityLabel` / `aria-label`. Stable across renders
44
+ * and across releases; safe to copy into a tool call.
45
+ * - `derived` folded from what the user can see — the button's own text,
46
+ * a `label` or `placeholder` prop. Stable as long as the copy
47
+ * does not change, and it changes with the app's language.
48
+ * - `positional` nothing but an ordinal in the current snapshot. Valid for
49
+ * this observation only: a re-render, a scroll or an inserted
50
+ * sibling moves it.
51
+ *
52
+ * ABSENT means unknown, NOT `declared` — clients published before this field
53
+ * existed send no provenance at all, and a missing value must never be read
54
+ * as the most trustworthy one.
55
+ */
56
+ type IdentityProvenance = 'declared' | 'derived' | 'positional';
57
+ interface InputSnapshot {
58
+ /** Best identifier — testID/data-testid, accessibilityLabel/aria-label, or placeholder */
59
+ id?: string;
60
+ /** How `id` was obtained. See {@link IdentityProvenance}. */
61
+ provenance?: IdentityProvenance;
62
+ /**
63
+ * False when the control is mounted but currently outside the window —
64
+ * below the fold of a ScrollView, scrolled off the top, pushed sideways
65
+ * by a carousel.
66
+ *
67
+ * ABSENT MEANS UNKNOWN. The old React Native architecture cannot report a
68
+ * synchronous window-relative rect, and no SDK published before this
69
+ * field existed reports one at all; reading absence as `false` would make
70
+ * the agent refuse to press a control the user is looking at.
71
+ */
72
+ onScreen?: boolean;
73
+ /** Human-readable label inferred from accessibility metadata or sibling text */
74
+ label?: string;
75
+ /** Current value (only present for controlled inputs) */
76
+ value?: string;
77
+ /** Placeholder text */
78
+ placeholder?: string;
79
+ /** Whether the input is editable */
80
+ editable?: boolean;
81
+ /** Whether the input is secure (password field) */
82
+ secure?: boolean;
83
+ /**
84
+ * Tipo do campo.
85
+ *
86
+ * Os cinco primeiros vêm da metadata de input da plataforma. Os quatro
87
+ * últimos vêm do registry: um select, um date picker, um toggle ou um
88
+ * controle custom registrado por `useAppilotsField` não é `TextInput`,
89
+ * então o walker não o coleta — e sem eles a observação não dizia o que
90
+ * estava preenchido num formulário que os exige. O schema de wire já
91
+ * aceitava (`type` é string livre lá); só o tipo local era estrito.
92
+ */
93
+ type?: 'text' | 'email' | 'number' | 'phone' | 'password' | 'select' | 'date' | 'toggle' | 'custom';
94
+ /**
95
+ * True when pressing return on this field submits — RN's
96
+ * `onSubmitEditing`, the web's implicit form submit.
97
+ *
98
+ * On a search box, a one-field login or a chat composer this IS the
99
+ * submit; the screen has no button and never will. Absent means the
100
+ * field declares no such handler, so the form needs a button.
101
+ */
102
+ submitsOnReturn?: boolean;
103
+ /**
104
+ * The app is asking for this field before the form can be submitted.
105
+ *
106
+ * ABSENT MEANS UNKNOWN, not optional: most apps never declare it, and
107
+ * reading absence as "not required" would let the agent submit a form
108
+ * it could have known was incomplete.
109
+ */
110
+ required?: boolean;
111
+ /**
112
+ * The app is currently rejecting this field's value.
113
+ *
114
+ * This is the fact the agent used to recover by READING — the error
115
+ * copy next to the input ("Campo obrigatório", "Formato inválido").
116
+ * Carrying the fact as a boolean is what lets a redacted observation
117
+ * still support recovery, and it is cheaper than the sentence even
118
+ * when nothing is redacted.
119
+ *
120
+ * Absent means the app declares no validity state — never that the
121
+ * field is valid.
122
+ */
123
+ invalid?: boolean;
124
+ /**
125
+ * Whether the field currently holds anything.
126
+ *
127
+ * Deliberately its own field rather than an inference over `value`.
128
+ * The value belongs to the user and is the first thing a privacy
129
+ * policy withholds; the EXISTENCE of a value belongs to the agent and
130
+ * is what stops it from filling the same field twice on a later hop.
131
+ * Separating them is what lets the second survive without the first.
132
+ */
133
+ filled?: boolean;
134
+ /**
135
+ * This input has keyboard focus right now.
136
+ *
137
+ * Tells apart "empty because nobody typed" from "empty because the
138
+ * user is typing in it at this moment" — the difference between
139
+ * completing a form and interrupting someone mid-sentence.
140
+ */
141
+ focused?: boolean;
142
+ /**
143
+ * True when this input lives inside a currently-visible modal. When a
144
+ * modal is open, only inModal elements can actually receive input —
145
+ * everything else is behind the overlay.
146
+ */
147
+ inModal?: boolean;
148
+ }
149
+ interface ButtonSnapshot {
150
+ controlEvidence?: ControlEvidence;
151
+ /** Best identifier — testID/data-testid, accessibilityLabel/aria-label, or inferred from child text */
152
+ id?: string;
153
+ /** How `id` was obtained. See {@link IdentityProvenance}. */
154
+ provenance?: IdentityProvenance;
155
+ /** False when the client can observe this button but cannot bind its id to an exact control. */
156
+ dispatchable?: boolean;
157
+ /**
158
+ * False when the control is mounted but currently outside the window —
159
+ * below the fold of a ScrollView, scrolled off the top, pushed sideways
160
+ * by a carousel.
161
+ *
162
+ * ABSENT MEANS UNKNOWN. The old React Native architecture cannot report a
163
+ * synchronous window-relative rect, and no SDK published before this
164
+ * field existed reports one at all; reading absence as `false` would make
165
+ * the agent refuse to press a control the user is looking at.
166
+ */
167
+ onScreen?: boolean;
168
+ /** Human-readable label — usually the visible text inside the button */
169
+ label?: string;
170
+ /** Whether the button is disabled */
171
+ disabled?: boolean;
172
+ /**
173
+ * Whether this control is currently in its ON state.
174
+ *
175
+ * Covers both accessibility states that mean it, because the agent
176
+ * needs the same thing from either: `accessibilityState.selected`
177
+ * (grouped, mutually-exclusive controls — segmented controls, radio
178
+ * groups, each option its own pressable target) and
179
+ * `accessibilityState.checked` (an independent checkbox).
180
+ *
181
+ * Absent means "not on OR not observable" — the walker cannot tell a
182
+ * custom checkbox that declares no a11y state from an ordinary button,
183
+ * so absence is never proof that a box is unticked.
184
+ */
185
+ selected?: boolean;
186
+ /** True when this button lives inside a currently-visible modal. */
187
+ inModal?: boolean;
188
+ }
189
+ interface ToggleSnapshot {
190
+ /** Best identifier */
191
+ id?: string;
192
+ /** How `id` was obtained. See {@link IdentityProvenance}. */
193
+ provenance?: IdentityProvenance;
194
+ /**
195
+ * False when the control is mounted but currently outside the window —
196
+ * below the fold of a ScrollView, scrolled off the top, pushed sideways
197
+ * by a carousel.
198
+ *
199
+ * ABSENT MEANS UNKNOWN. The old React Native architecture cannot report a
200
+ * synchronous window-relative rect, and no SDK published before this
201
+ * field existed reports one at all; reading absence as `false` would make
202
+ * the agent refuse to press a control the user is looking at.
203
+ */
204
+ onScreen?: boolean;
205
+ /** Human-readable label */
206
+ label?: string;
207
+ /** Current on/off state */
208
+ value?: boolean;
209
+ /** True when this toggle lives inside a currently-visible modal. */
210
+ inModal?: boolean;
211
+ }
212
+ interface SliderSnapshot {
213
+ /** Registry id — the executable handle. */
214
+ id?: string;
215
+ /** How `id` was obtained. See {@link IdentityProvenance}. */
216
+ provenance?: IdentityProvenance;
217
+ /**
218
+ * False when the control is mounted but currently outside the window —
219
+ * below the fold of a ScrollView, scrolled off the top, pushed sideways
220
+ * by a carousel.
221
+ *
222
+ * ABSENT MEANS UNKNOWN. The old React Native architecture cannot report a
223
+ * synchronous window-relative rect, and no SDK published before this
224
+ * field existed reports one at all; reading absence as `false` would make
225
+ * the agent refuse to press a control the user is looking at.
226
+ */
227
+ onScreen?: boolean;
228
+ /** Human-readable label */
229
+ label?: string;
230
+ /** Current numeric value */
231
+ value?: number;
232
+ /** Lower bound the executor clamps to */
233
+ min?: number;
234
+ /** Upper bound the executor clamps to */
235
+ max?: number;
236
+ /** Step the executor snaps to (omitted = continuous) */
237
+ step?: number;
238
+ /** Whether the slider is currently disabled */
239
+ disabled?: boolean;
240
+ /** True when this slider lives inside a currently-visible modal. */
241
+ inModal?: boolean;
242
+ }
243
+ interface ListItemSnapshot {
244
+ /** Whether this row intersects the list viewport, when geometry is available. */
245
+ onScreen?: boolean;
246
+ /** 1-indexed position within the parent list (matches "selecione o terceiro"). */
247
+ index: number;
248
+ /**
249
+ * 0-based index of this row in the list's backing DATA, when known.
250
+ * For scrolled/virtualized lists this differs from `index`.
251
+ */
252
+ dataIndex?: number;
253
+ /** Framework key of the row, if present (typically the item's domain id). */
254
+ reactKey?: string;
255
+ /** Runtime key supplied by list tracking, if available. */
256
+ itemKey?: string;
257
+ /** Texts captured from this item's subtree, preserving row association. */
258
+ texts: string[];
259
+ /** Buttons inside this item — e.g. an inline "Edit" button on a row. */
260
+ buttons: ButtonSnapshot[];
261
+ /** Inputs inside this item — rare but possible (inline edit row). */
262
+ inputs: InputSnapshot[];
263
+ /** Toggles inside this item. */
264
+ toggles: ToggleSnapshot[];
265
+ /**
266
+ * Synthetic id assigned by the walker. Stable within a single
267
+ * snapshot; format `list-<L>-item-<I>` (0-indexed L, 1-indexed I).
268
+ * Used by tap-by-ordinal resolution in the executor.
269
+ */
270
+ syntheticId: string;
271
+ }
272
+ interface ListSnapshot {
273
+ /** Measured rows intersecting the list viewport; absent means unmeasurable. */
274
+ viewportItemCount?: number;
275
+ /** Mission-local exploration metadata; positions are 0-based, never action targets. */
276
+ exploration?: {
277
+ revision: number;
278
+ observedItemCount: number;
279
+ observedRanges: Array<{
280
+ start: number;
281
+ end: number;
282
+ }>;
283
+ rangesTruncated: boolean;
284
+ coverage: 'partial' | 'all-loaded';
285
+ pagination: 'possible' | 'not-declared';
286
+ scrollSteps: number;
287
+ remainingScrollSteps: number;
288
+ consecutiveNoProgress: number;
289
+ lastScroll?: 'moved' | 'no-progress' | 'boundary' | 'unverified';
290
+ };
291
+ /** 0-indexed list ordinal — multiple lists on one screen get 0, 1, 2... */
292
+ index: number;
293
+ /** Runtime list id when tracking or explicit props provide one. */
294
+ id?: string;
295
+ /** The container component/tag name as observed by the walker. */
296
+ containerType: string;
297
+ /**
298
+ * Source that identified this list. `'fiber'`/`'auto-tracked'` are
299
+ * the walker's own discovery tags (a DOM walker reports
300
+ * `'auto-tracked'` for app-annotated lists and omits the field for
301
+ * heuristically-detected ones); `'registry'` means the list came
302
+ * from the SDK's list registry rather than the tree walk.
303
+ */
304
+ source?: 'fiber' | 'auto-tracked' | 'registry';
305
+ /**
306
+ * How many items the list renders FROM — its `data.length`, which for
307
+ * a paginated list is the page and not the collection.
308
+ */
309
+ itemCount?: number;
310
+ /**
311
+ * Size of the whole collection when the app declared it.
312
+ *
313
+ * The three counts answer three different questions and a list that
314
+ * pages needs all three: `visibleItemCount` is what is mounted,
315
+ * `itemCount` is what is loaded, `totalItemCount` is what exists.
316
+ * Undefined means unknown — it is never a copy of `itemCount`.
317
+ */
318
+ totalItemCount?: number;
319
+ /** Number of row items captured in this snapshot. */
320
+ visibleItemCount?: number;
321
+ /** True when the list reports a refresh/loading state. */
322
+ refreshing?: boolean;
323
+ /** True when total item count is known and zero. */
324
+ empty?: boolean;
325
+ /** Human-readable label if the app supplied one. */
326
+ label?: string;
327
+ /** Items in visible order. */
328
+ items: ListItemSnapshot[];
329
+ /**
330
+ * Lightweight text projection of the list's FULL data set (capped),
331
+ * so the agent can see/search rows that virtualization keeps
332
+ * unmounted. Each entry: 0-based data index, stable key, short text.
333
+ */
334
+ dataPreview?: ListDataPreviewEntry$1[];
335
+ /** Current vertical scroll offset in px, when readable. */
336
+ scrollOffsetY?: number;
337
+ /** True when there is scrollable content above the viewport. */
338
+ canScrollUp?: boolean;
339
+ /** True when there is scrollable content below the viewport. */
340
+ canScrollDown?: boolean;
341
+ }
342
+ interface ListDataPreviewEntry$1 {
343
+ /** 0-based index in the list's backing data. */
344
+ index: number;
345
+ /** Stable key from the item's domain id when available. */
346
+ key?: string;
347
+ /** Short human-readable projection of the item (capped length). */
348
+ text: string;
349
+ }
350
+ /**
351
+ * A scrollable surface that is NOT a collection — a long form, a detail
352
+ * page, a settings screen inside a `ScrollView`.
353
+ *
354
+ * It exists as its own concept, and not as a `ListSnapshot` with zero
355
+ * rows, because everything the agent knows how to do with a list
356
+ * (ordinals, totals, "select the third one") is meaningless here. The
357
+ * only affordance is paging toward content below the fold — and without
358
+ * it, a screen taller than the viewport ends at the fold.
359
+ */
360
+ interface ScrollableSnapshot {
361
+ /** Runtime id — the handle `scroll_list` addresses. */
362
+ id: string;
363
+ /** The container component as observed: ScrollView, ... */
364
+ containerType: string;
365
+ /** Human-readable label if the app supplied one. */
366
+ label?: string;
367
+ /** Current scroll offset in px. */
368
+ scrollOffsetY?: number;
369
+ /** True when there is content above the viewport. */
370
+ canScrollUp?: boolean;
371
+ /** True when there is content below the viewport. */
372
+ canScrollDown?: boolean;
373
+ /** True when the surface scrolls sideways rather than vertically. */
374
+ horizontal?: boolean;
375
+ }
376
+ /**
377
+ * A platform dialog covering the screen — React Native's `Alert.alert`,
378
+ * an action sheet, an OS permission prompt.
379
+ *
380
+ * It renders outside React, so no amount of tree walking finds it. It
381
+ * has to be reported separately or the agent keeps operating the app
382
+ * underneath a dialog that blocks every real finger.
383
+ */
384
+ interface NativeDialogSnapshot {
385
+ /** Stable for as long as this dialog is open. */
386
+ id: string;
387
+ title?: string;
388
+ message?: string;
389
+ /** The buttons the user can press. Never empty. */
390
+ buttons: Array<{
391
+ label: string;
392
+ style?: 'default' | 'cancel' | 'destructive';
393
+ }>;
394
+ }
395
+ interface ChoiceOptionSnapshot {
396
+ /** 1-indexed option position within this group. */
397
+ index: number;
398
+ /** Stable-enough id for the current snapshot/action turn. */
399
+ syntheticId: string;
400
+ /** Best target id if this option is backed by a pressable component. */
401
+ targetId?: string;
402
+ /** Human-readable option label. */
403
+ label?: string;
404
+ /** Text segments that belong to this option. */
405
+ texts: string[];
406
+ /** Whether this option appears selected. */
407
+ selected?: boolean;
408
+ /** Whether this option appears disabled. */
409
+ disabled?: boolean;
410
+ }
411
+ interface ChoiceGroupSnapshot {
412
+ /** 0-indexed group ordinal on the current screen. */
413
+ index: number;
414
+ /** Runtime id when known, usually inherited from a list/collection. */
415
+ id?: string;
416
+ /** Human-readable label when known. */
417
+ label?: string;
418
+ /** Source that produced this choice group. */
419
+ source?: 'list' | 'buttons' | 'heuristic';
420
+ /** Visible options in order. */
421
+ options: ChoiceOptionSnapshot[];
422
+ }
423
+ interface InteractionElementListContext {
424
+ listIndex?: number;
425
+ listId?: string;
426
+ listLabel?: string;
427
+ itemIndex?: number;
428
+ itemKey?: string;
429
+ reactKey?: string;
430
+ syntheticId?: string;
431
+ }
432
+ interface InteractionElementSnapshot {
433
+ /** False means observation-only; absence preserves legacy client behavior. */
434
+ dispatchable?: boolean;
435
+ /** Stable id for the current screen/content, preferred for tool calls. */
436
+ id: string;
437
+ /** Semantic UI role. */
438
+ role: 'option' | 'button' | 'input' | 'toggle' | 'slider' | 'listItem';
439
+ /** Human-readable label. */
440
+ label?: string;
441
+ /** Text segments associated with this element. */
442
+ texts: string[];
443
+ /** Actions supported by this element. */
444
+ actions: Array<'press' | 'focus' | 'toggle' | 'setValue'>;
445
+ /** Whether the element is currently disabled. */
446
+ disabled?: boolean;
447
+ /** Whether the element appears selected. */
448
+ selected?: boolean;
449
+ /** Source that produced the element. */
450
+ source?: 'list' | 'button' | 'input' | 'toggle' | 'slider' | 'choice';
451
+ /**
452
+ * How the underlying control's identifier was obtained. Carried up from
453
+ * the snapshot entry this element was derived from, so a consumer that
454
+ * only reads `elements` still knows what it is trusting. See
455
+ * {@link IdentityProvenance}.
456
+ */
457
+ provenance?: IdentityProvenance;
458
+ /**
459
+ * False when the control is mounted but currently outside the window —
460
+ * below the fold of a ScrollView, scrolled off the top, pushed sideways
461
+ * by a carousel.
462
+ *
463
+ * ABSENT MEANS UNKNOWN. The old React Native architecture cannot report a
464
+ * synchronous window-relative rect, and no SDK published before this
465
+ * field existed reports one at all; reading absence as `false` would make
466
+ * the agent refuse to press a control the user is looking at.
467
+ */
468
+ onScreen?: boolean;
469
+ /** Legacy/fallback target id, if any. */
470
+ targetId?: string;
471
+ /** Context for row/list options. */
472
+ listContext?: InteractionElementListContext;
473
+ /**
474
+ * True when the underlying component lives inside a currently-visible
475
+ * modal — the only targets actually touchable while the overlay is up.
476
+ */
477
+ inModal?: boolean;
478
+ }
479
+ /** Window-relative box, in the coordinate space the platform reports. */
480
+ interface ElementRect {
481
+ x: number;
482
+ y: number;
483
+ width: number;
484
+ height: number;
485
+ }
486
+ /**
487
+ * What the CLIENT keeps about an element, which is strictly more than what
488
+ * the model is told about it.
489
+ *
490
+ * The wire shape (`InteractionElementSnapshot`) is sized for a language
491
+ * model: names, roles, state, everything it needs to decide. This one adds
492
+ * what only the executor needs — coordinates it will never read aloud —
493
+ * and it exists because that data was being measured and then discarded
494
+ * for want of somewhere to put it that wasn't the wire.
495
+ *
496
+ * The split is the point. Anything added here is free: it never reaches
497
+ * `agentSnapshotSchema`, never crosses the network, and never costs a
498
+ * token. Anything added to the wire type is paid for on every observation
499
+ * of every session, so it has to earn the model's attention.
500
+ */
501
+ interface InteractionElementEntry extends InteractionElementSnapshot {
502
+ /**
503
+ * Where the control was on screen when the snapshot was taken. Absent
504
+ * when the platform cannot measure synchronously (React Native's old
505
+ * architecture) or when the element came from a source with no fiber
506
+ * behind it.
507
+ */
508
+ rect?: ElementRect;
509
+ }
510
+ /**
511
+ * What changed between the previous observation and this one.
512
+ *
513
+ * The client holds both snapshots; the server only ever sees one. So the
514
+ * question the agent loop asks most often — "did the action I just took
515
+ * do anything?" — is one the client can answer as a FACT and the server
516
+ * can only guess at. Today it guesses: `effect: 'none'` on the turn trail
517
+ * is derived server-side from heuristics over a single snapshot.
518
+ *
519
+ * Everything here is shape, never content: counts, booleans, and field
520
+ * ids that the app declared itself. That is not an accident of design —
521
+ * it is what lets this survive a redacted or content-free observation
522
+ * intact, and it is why the most useful signal in the observation is also
523
+ * the cheapest one to send.
524
+ *
525
+ * ABSENT vs `unchanged: true` is the load-bearing distinction, and it is
526
+ * the same one `truncated` exists to make. Absent means there was no
527
+ * previous observation to compare against (the first capture of a
528
+ * mission). `unchanged: true` means we DID compare and nothing moved —
529
+ * which is the strongest evidence there is that an action did nothing.
530
+ */
531
+ interface SnapshotDelta {
532
+ /** The active route is not the one the previous observation reported. */
533
+ routeChanged?: boolean;
534
+ /** A modal or native dialog is up now and was not before. */
535
+ modalOpened?: boolean;
536
+ /** A modal or native dialog was up before and is gone now. */
537
+ modalClosed?: boolean;
538
+ /** A loading indicator appeared. */
539
+ loadingStarted?: boolean;
540
+ /** A loading indicator that was showing is gone. */
541
+ loadingFinished?: boolean;
542
+ /** How many distinct visible texts are present now that were not before. */
543
+ textsAdded?: number;
544
+ /** How many distinct visible texts are gone. */
545
+ textsRemoved?: number;
546
+ /** Zero-based indexes of newly visible buttons in this snapshot, capped at 12.
547
+ * No derived ids or copy travel in the delta; render only from current buttons.
548
+ */
549
+ buttonsAddedIndices?: number[];
550
+ /** Number of previously visible button identities no longer visible. */
551
+ buttonsRemoved?: number;
552
+ /** Net change in mounted list rows, summed across every list. */
553
+ visibleRowsDelta?: number;
554
+ /**
555
+ * Net change in the lists' reported DATA totals, summed. This is the
556
+ * one that answers "did the record actually get created", because it
557
+ * moves even when virtualization keeps the new row unmounted.
558
+ */
559
+ totalRowsDelta?: number;
560
+ /**
561
+ * Fields that went from empty to filled. Declared ids only — a
562
+ * `derived` id is folded from visible copy, so publishing it here
563
+ * would smuggle content through a field that claims to carry none.
564
+ */
565
+ fieldsNewlyFilled?: string[];
566
+ /** Fields that went from filled to empty. Declared ids only. */
567
+ fieldsCleared?: string[];
568
+ /** Some field is reporting invalid that was not reporting it before. */
569
+ invalidAppeared?: boolean;
570
+ /**
571
+ * Nothing above fired: we compared two observations and the screen is
572
+ * structurally identical. Present only when every other field is
573
+ * absent, so a reader never has to check all of them to know.
574
+ */
575
+ unchanged?: boolean;
576
+ }
577
+ interface ScreenSnapshot {
578
+ /** The currently active route name, if known */
579
+ route: string | null;
580
+ /** All visible static text */
581
+ texts: string[];
582
+ /** All text inputs currently rendered (and visible) */
583
+ inputs: InputSnapshot[];
584
+ /** All button-like components */
585
+ buttons: ButtonSnapshot[];
586
+ /** All toggle components */
587
+ toggles: ToggleSnapshot[];
588
+ /** Sliders / adjustable numeric controls. */
589
+ sliders: SliderSnapshot[];
590
+ /** Whether a loading indicator is visible */
591
+ loading: boolean;
592
+ /** Whether a modal is currently open */
593
+ modalOpen: boolean;
594
+ /**
595
+ * A native dialog covering the screen, when the SDK could observe one.
596
+ *
597
+ * Its presence also forces `modalOpen`, because for every purpose the
598
+ * agent cares about it IS a modal — nothing behind it is touchable.
599
+ * Absence is not proof there is no dialog: an OS permission prompt or
600
+ * a share sheet cannot be instrumented at all.
601
+ */
602
+ nativeDialog?: NativeDialogSnapshot;
603
+ /**
604
+ * Lists detected in the visible tree. Each list's items have their
605
+ * own per-item texts/buttons/inputs/toggles, NOT duplicated in the
606
+ * flat top-level arrays — preserving the row association the flat
607
+ * shape destroys.
608
+ */
609
+ lists: ListSnapshot[];
610
+ /**
611
+ * Choice groups detected from visible cards/rows/chips/buttons, used
612
+ * for select-like flows where no text input exists.
613
+ */
614
+ choiceGroups: ChoiceGroupSnapshot[];
615
+ /**
616
+ * Scrollable surfaces that carry no rows. Absent/empty means either
617
+ * the screen does not scroll or the SDK could not observe that it
618
+ * does — never that the visible content is all the content.
619
+ */
620
+ scrollables?: ScrollableSnapshot[];
621
+ /**
622
+ * Interaction graph: visible actionable elements with stable ids,
623
+ * semantic roles, labels, and execution fallbacks. Agents should
624
+ * prefer these ids over synthetic ordinal handles.
625
+ */
626
+ elements: InteractionElementSnapshot[];
627
+ /**
628
+ * Set when the walker's output exceeded the wire contract and
629
+ * `clampSnapshotToWireLimits` dropped part of it (see
630
+ * `introspection/wireLimits.ts`). Absent means the observation is
631
+ * complete.
632
+ *
633
+ * The server surfaces this to the model: a screen whose text was cut
634
+ * at 500 entries must not be read as a screen with only 500 things on
635
+ * it. Without the flag a truncated observation is indistinguishable
636
+ * from a short one.
637
+ */
638
+ truncated?: boolean;
639
+ /**
640
+ * What moved since the previous observation. See {@link SnapshotDelta}
641
+ * — absent means there was nothing to compare against, which is a
642
+ * different statement from "nothing changed".
643
+ *
644
+ * Attached by the platform adapter rather than by the walker: the
645
+ * baseline has to be the last snapshot that was actually SENT, and
646
+ * `captureSnapshot` is also called for internal probes that never
647
+ * leave the device.
648
+ */
649
+ delta?: SnapshotDelta;
650
+ /** Diagnostic counts for debugging */
651
+ stats?: {
652
+ visitedFibers: number;
653
+ skippedHidden: number;
654
+ /**
655
+ * How many controls the walker could name, and how well.
656
+ *
657
+ * `WalkDetectorCounts` counts RECOGNITIONS, not emissions, so a screen
658
+ * whose controls were all silently dropped still reported healthy
659
+ * detector numbers. This is the emission side: `positional` climbing is
660
+ * the signal that an app needs annotating, and comparing it between a
661
+ * debug and a release build is the first real measurement of what
662
+ * minification costs the agent.
663
+ */
664
+ identity?: {
665
+ declared: number;
666
+ derived: number;
667
+ positional: number;
668
+ };
669
+ };
670
+ }
671
+
672
+ type TraceListener = (entries: AppilotsTraceEntry[]) => void;
673
+ declare function recordAppilotsDebugTrace(entry: Omit<AppilotsTraceEntry, 'id'> & {
674
+ id?: string;
675
+ }): void;
676
+ declare function getAppilotsDebugTraces(): AppilotsTraceEntry[];
677
+ declare function clearAppilotsDebugTraces(): void;
678
+ declare function subscribeAppilotsDebugTraces(listener: TraceListener): () => void;
679
+
680
+ /** Customer action labels. Raw payloads remain in the action/debug records. */
681
+
682
+ type BreadcrumbState = ActionPresentationState;
683
+ interface BreadcrumbItem {
684
+ label: string;
685
+ state: BreadcrumbState;
686
+ }
687
+ declare function describeAction(action: AgentAction, locale?: AppilotsLocale): BreadcrumbItem;
688
+ /**
689
+ * Convert the executor's raw error string into a short, user-facing
690
+ * reason that fits inside parentheses on the breadcrumb line. Returns
691
+ * `undefined` when there's no useful information to show — caller can
692
+ * then drop the parenthetical entirely.
693
+ *
694
+ * Heuristics:
695
+ * - Apply known-pattern rewrites (rejected, not found, network, etc.)
696
+ * - If the result looks technical (stack frame, HTTP status, JSON
697
+ * dump) or is suspiciously long, fall back to "algo deu errado".
698
+ * - Trim and lowercase the first letter so it reads naturally inside
699
+ * the parens after the action verb.
700
+ */
701
+ declare function humanizeError(raw: string | undefined, _action?: AgentAction): string | undefined;
702
+
703
+ /**
704
+ * Shared position type.
705
+ *
706
+ * Extracted from AppilotsChat so `AgentPill` can accept it without
707
+ * importing the 1400-line chat module — which would pull the whole
708
+ * chat (and its hooks, and the client) into any test that renders just
709
+ * the pill.
710
+ */
711
+ type AppilotsChatPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
712
+
713
+ /**
714
+ * The speech seam.
715
+ *
716
+ * The SDK ships no speech engine and never will. Every usable one on
717
+ * React Native is a native module — `@react-native-voice/voice`,
718
+ * `expo-speech-recognition`, a vendor SDK — and a dependency here would
719
+ * force native linking on every integrator, including the majority who
720
+ * will never turn voice on. Worse, it would pick their engine for them:
721
+ * the right one depends on the locale they need, whether recognition
722
+ * may leave the device, and what their app store review already covers.
723
+ *
724
+ * So this is an interface and nothing else, injected the same way
725
+ * `GeometryHost` and the `Alert` in `confirmedDestructiveAlert` are.
726
+ * Pass an adapter and the mic button appears; pass nothing and
727
+ * `resolveExperience` reports `input: 'voice'` as an unmet request
728
+ * rather than rendering a button that does nothing.
729
+ *
730
+ * Everything here is transcription only. Playing audio back is not
731
+ * modelled, because nothing in the loop produces audio.
732
+ */
733
+ /** What the SDK needs to run a hold-to-talk button. */
734
+ interface SpeechHost {
735
+ /**
736
+ * Begin listening. Resolve once capture is actually running, so the
737
+ * UI can wait before telling the user to speak.
738
+ *
739
+ * Implementations own permissions: if the OS prompt has not been
740
+ * accepted, request it here and reject when refused.
741
+ */
742
+ start(options: SpeechStartOptions): Promise<void>;
743
+ /**
744
+ * Stop listening and resolve with the final transcript.
745
+ *
746
+ * Resolving with an empty string is the normal outcome for "the user
747
+ * pressed and said nothing" — it must not reject for that.
748
+ */
749
+ stop(): Promise<string>;
750
+ /** Abandon the capture. No transcript, no error. */
751
+ cancel(): Promise<void>;
752
+ /**
753
+ * Partial results while the user is still talking, if the engine has
754
+ * them. Return a disposer. Optional — a host without interim results
755
+ * simply shows the placeholder until `stop` resolves.
756
+ */
757
+ onPartial?(listener: (text: string) => void): () => void;
758
+ }
759
+ interface SpeechStartOptions {
760
+ /**
761
+ * BCP-47 tag resolved from the chat's locale, so a pt-BR chat does
762
+ * not transcribe against a US English model. Hosts that only support
763
+ * one locale may ignore it.
764
+ */
765
+ locale: string;
766
+ }
767
+
768
+ /**
769
+ * Pure resolution for the chat's *experience* config — the knobs that
770
+ * decide what the user sees WHILE the agent drives the app, and how a
771
+ * turn gets started.
772
+ *
773
+ * Same pattern as `personalization.ts`: no `react-native` import, no
774
+ * side effects, so the precedence and coherence rules are unit-testable
775
+ * without rendering a tree.
776
+ *
777
+ * ## Why this is a resolver and not five booleans
778
+ *
779
+ * These knobs are layers, not independent switches. A spotlight ring
780
+ * drawn while the sheet is up lands BEHIND a full-screen scrim; shared
781
+ * control behind a native `Modal` cannot exist at all, because the
782
+ * modal swallows every touch before the app sees it. Exposed as raw
783
+ * booleans, most of the combination space renders something broken, and
784
+ * the only way to find out is to run it.
785
+ *
786
+ * So the config is *requested*, not applied. Everything here narrows a
787
+ * request down to a combination that is coherent by construction, and
788
+ * reports what it had to change. The render code downstream never has
789
+ * to ask "is this combination possible" — by the time it sees a
790
+ * `ResolvedExperience`, it is.
791
+ *
792
+ * Downgrades are returned rather than logged so the caller decides the
793
+ * channel (dev warning, telemetry, both) — the same reason
794
+ * `probeNameMangling` returns its findings instead of printing them.
795
+ */
796
+ /**
797
+ * What the user sees while the agent is executing actions.
798
+ * - `sheet` — today's behavior: the chat stays up, app covered.
799
+ * - `pill` — the sheet collapses to a status line; app visible and
800
+ * interactive.
801
+ * - `hidden` — nothing but the app. The turn reports back when it ends
802
+ * or when it needs an answer.
803
+ */
804
+ type AppilotsPresence = 'sheet' | 'pill' | 'hidden';
805
+ /**
806
+ * Whether the element being acted on is marked on the app itself.
807
+ * - `off` — no overlay.
808
+ * - `ring` — outline the target; skip it when it is off-screen.
809
+ * - `guided` — scroll the target into view first, then outline it.
810
+ */
811
+ type AppilotsSpotlight = 'off' | 'ring' | 'guided';
812
+ /**
813
+ * Who may operate the app during a turn.
814
+ * - `agent` — the agent alone; a user touch is not treated specially.
815
+ * - `shared` — a user touch pauses the queue and invalidates the
816
+ * snapshot the model reasoned about.
817
+ */
818
+ type AppilotsControl = 'agent' | 'shared';
819
+ /**
820
+ * Ways a turn can be started that the SDK installs by itself.
821
+ *
822
+ * `commandBar` is deliberately NOT here: it is a component the host
823
+ * places in its own header, so a flag could not mount it. It ships as
824
+ * an ordinary export instead.
825
+ */
826
+ type AppilotsEntryPoint = 'fab' | 'longPress' | 'contextual';
827
+ /** Composer input modalities. `voice` needs an injected `SpeechHost`. */
828
+ type AppilotsInputMode = 'text' | 'voice';
829
+ interface AppilotsExperience {
830
+ presence?: AppilotsPresence;
831
+ spotlight?: AppilotsSpotlight;
832
+ control?: AppilotsControl;
833
+ entry?: AppilotsEntryPoint[];
834
+ input?: AppilotsInputMode[];
835
+ }
836
+ interface ResolvedExperience {
837
+ presence: AppilotsPresence;
838
+ spotlight: AppilotsSpotlight;
839
+ control: AppilotsControl;
840
+ entry: AppilotsEntryPoint[];
841
+ input: AppilotsInputMode[];
842
+ }
843
+ /**
844
+ * Runtime facts the resolver cannot read for itself — injected for the
845
+ * same reason `GeometryHost` is: this file must stay importable in a
846
+ * plain unit test with no React Native under it.
847
+ */
848
+ interface ExperienceCapabilities {
849
+ /**
850
+ * Fabric geometry is readable. Window-relative rects are the only
851
+ * thing that can place a ring over a real control, and the old
852
+ * architecture cannot report them at all — see `introspection/geometry.ts`.
853
+ */
854
+ geometry: boolean;
855
+ /** A `SpeechHost` was injected. Without one the mic button is inert. */
856
+ speech: boolean;
857
+ }
858
+ interface ExperienceDowngrade {
859
+ field: keyof ResolvedExperience;
860
+ from: string;
861
+ to: string;
862
+ /** Developer-facing, English, no i18n — this never reaches an end user. */
863
+ reason: string;
864
+ }
865
+ interface ExperienceResolution {
866
+ experience: ResolvedExperience;
867
+ /** Empty when the request was already coherent. */
868
+ downgrades: ExperienceDowngrade[];
869
+ }
870
+ /**
871
+ * Today's shipped behavior, exactly. The new presences are opt-in for
872
+ * now so no existing integration changes when it upgrades the SDK.
873
+ */
874
+ declare const DEFAULT_EXPERIENCE: ResolvedExperience;
875
+ /**
876
+ * Narrow a requested experience to one that can actually render.
877
+ *
878
+ * Rules are applied in dependency order: a knob that gets switched off
879
+ * for lack of a capability must not go on to force a change in another
880
+ * knob. Concretely — spotlight loses to missing geometry BEFORE it gets
881
+ * the chance to pull `presence` off the sheet, so an old-architecture
882
+ * app that asked for a spotlight keeps the sheet it already had rather
883
+ * than being moved to a pill it never requested.
884
+ */
885
+ declare function resolveExperience(requested: AppilotsExperience | undefined, caps: ExperienceCapabilities): ExperienceResolution;
886
+ /** One-line-per-downgrade text, for a dev-mode console warning. */
887
+ declare function formatDowngrades(downgrades: readonly ExperienceDowngrade[]): string[];
888
+
7
889
  interface AppilotsThemeProviderProps {
8
890
  /**
9
891
  * Partial token overrides — merged on top of the resolved base
@@ -153,6 +1035,24 @@ interface AppilotsStrings {
153
1035
  escalationPendingBanner: string;
154
1036
  /** Thin banner under the header while connected to an operator. */
155
1037
  escalationActiveBanner: string;
1038
+ /** Pill label while the turn is running but no action has started yet. */
1039
+ pillWorking: string;
1040
+ /** A11y label for the pill as a whole — tapping it opens the sheet. */
1041
+ pillExpandA11y: string;
1042
+ /** A11y label for the pill's stop button. */
1043
+ pillStopA11y: string;
1044
+ missionPause: string;
1045
+ missionResume: string;
1046
+ missionCancel: string;
1047
+ missionPaused: string;
1048
+ /** Pill label after the turn ends, before the user opens the sheet. */
1049
+ pillDone: string;
1050
+ /** A11y label for the hold-to-talk mic button at rest. */
1051
+ micStartA11y: string;
1052
+ /** A11y label while a capture is running. */
1053
+ micStopA11y: string;
1054
+ /** Placeholder shown in the composer while listening. */
1055
+ micListening: string;
156
1056
  }
157
1057
  type AppilotsStringKey = keyof AppilotsStrings;
158
1058
 
@@ -188,7 +1088,7 @@ interface AppilotsI18nProviderProps {
188
1088
  overrides?: Partial<AppilotsStrings> | null;
189
1089
  children: React__default.ReactNode;
190
1090
  }
191
- declare function AppilotsI18nProvider({ locale, overrides, children, }: AppilotsI18nProviderProps): React__default.JSX.Element;
1091
+ declare function AppilotsI18nProvider({ locale, overrides, children }: AppilotsI18nProviderProps): React__default.JSX.Element;
192
1092
  /**
193
1093
  * Read the locale + strings from context. Falls back to the default
194
1094
  * locale bundle if no provider is mounted, so components don't need a
@@ -204,7 +1104,7 @@ declare function useAppilotsI18n(): I18nContextValue;
204
1104
  * - `inline` — render inline where it's mounted, no modal at all
205
1105
  */
206
1106
  type AppilotsChatMode = 'bubble' | 'fullscreen' | 'sidebar' | 'inline';
207
- type AppilotsChatPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
1107
+
208
1108
  interface AppilotsChatProps {
209
1109
  /** Controlled visibility — when set, the FAB is hidden. */
210
1110
  visible?: boolean;
@@ -273,6 +1173,28 @@ interface AppilotsChatProps {
273
1173
  * Ignored when `triggerIcon` is set.
274
1174
  */
275
1175
  triggerButtonImage?: string | number;
1176
+ /**
1177
+ * What the user sees WHILE the agent drives the app, and how a turn
1178
+ * can be started.
1179
+ *
1180
+ * Omit it and nothing changes: the default is `presence: 'sheet'`,
1181
+ * which is the behavior every version of this component has had —
1182
+ * the chat stays up and the app is covered while actions run.
1183
+ *
1184
+ * The knobs are layers rather than independent switches, so the
1185
+ * request is normalised by `resolveExperience` before anything
1186
+ * renders; an impossible combination is narrowed to the nearest
1187
+ * possible one and reported via a dev-mode warning instead of
1188
+ * rendering something broken. See `components/experience.ts`.
1189
+ */
1190
+ experience?: AppilotsExperience;
1191
+ /**
1192
+ * Speech-to-text adapter. Required for `experience.input: ['voice']` —
1193
+ * the SDK ships no engine of its own, on purpose (see
1194
+ * `components/speech.ts`). Without one, a voice request is reported as
1195
+ * unmet instead of rendering a mic button that cannot listen.
1196
+ */
1197
+ speech?: SpeechHost;
276
1198
  /** Force a locale. Falls through to device detection when omitted. */
277
1199
  locale?: AppilotsLocale;
278
1200
  }
@@ -365,26 +1287,429 @@ interface ConfirmDialogProps {
365
1287
  declare function ConfirmDialog(_props?: ConfirmDialogProps): React__default.ReactElement | null;
366
1288
 
367
1289
  /**
368
- * Pure presentation logic for ActionBreadcrumb, extracted from the RN
369
- * component (same pattern as `humanizeAction.ts` / `chatTimeline.ts`)
370
- * so it can be unit tested without a renderer:
371
- * - lifecycle state → tone (colors) and icon glyph;
372
- * - destructive detection (payload flag or action-level flag);
373
- * - confirm-card copy + colors derivation (title / message / button
374
- * labels / accent color / surface / border) with the
375
- * destructive-aware defaults.
1290
+ * AgentPill — the collapsed surface for `presence: 'pill' | 'hidden'`.
1291
+ *
1292
+ * One line saying what the agent is doing right now, floating over the
1293
+ * host app instead of covering it. This is the piece that makes the
1294
+ * rest of the experience layer possible: while it is up, the app is
1295
+ * fully visible AND fully touchable, because — unlike the sheet — the
1296
+ * pill is NOT inside a native `Modal`. It renders as an absolutely
1297
+ * positioned sibling, the same layer the FAB has always used.
1298
+ *
1299
+ * That placement is the whole design, so it is worth stating why it
1300
+ * cannot be otherwise: a React Native `Modal` is a real platform modal.
1301
+ * It takes every touch before the app below sees one, and no
1302
+ * `pointerEvents` setting on our side changes that. Anything that wants
1303
+ * the user to keep using their app has to live outside it.
1304
+ *
1305
+ * Presentational on purpose — it decides nothing about WHEN it shows
1306
+ * (that is `presence.ts`) and nothing about what the label says (that
1307
+ * is `describeAction` in client-core, the same source the breadcrumb
1308
+ * uses, so the pill and the expanded transcript can never disagree).
1309
+ */
1310
+
1311
+ interface AgentPillStrings {
1312
+ expandA11y: string;
1313
+ stopA11y: string;
1314
+ }
1315
+ interface AgentPillProps {
1316
+ /** What to say. Already humanised and localised by the caller. */
1317
+ label: string;
1318
+ /** Drives the leading glyph: spinner while running, check when done. */
1319
+ state: BreadcrumbState;
1320
+ /** Tap anywhere on the pill — raises the full sheet. */
1321
+ onExpand: () => void;
1322
+ /**
1323
+ * Stop button. Omit to hide it — there is nothing to cancel once the
1324
+ * turn has ended, and a dead button on a one-line surface is worse
1325
+ * than no button.
1326
+ */
1327
+ onStop?: () => void;
1328
+ /** Only the vertical half is read; the pill always spans the width. */
1329
+ position: AppilotsChatPosition;
1330
+ /** Safe-area insets, so the pill clears the notch and the home bar. */
1331
+ insets: {
1332
+ top: number;
1333
+ bottom: number;
1334
+ };
1335
+ colors: {
1336
+ surface: string;
1337
+ text: string;
1338
+ textSecondary: string;
1339
+ primary: string;
1340
+ border: string;
1341
+ success: string;
1342
+ error: string;
1343
+ };
1344
+ fontFamily?: string;
1345
+ strings: AgentPillStrings;
1346
+ }
1347
+ declare function AgentPill({ label, state, onExpand, onStop, position, insets, colors, fontFamily, strings, }: AgentPillProps): React__default.JSX.Element;
1348
+
1349
+ /**
1350
+ * Is this control actually on the screen right now?
1351
+ *
1352
+ * Nothing answered that until now. `shouldSkipSubtree` prunes what React
1353
+ * has HIDDEN — `display: 'none'`, a closed Modal, an inactive tab — but a
1354
+ * button sitting below the fold of a `ScrollView` is mounted, visible to the
1355
+ * walker, and reported as an ordinary pressable target. The agent presses
1356
+ * it, the press lands on a view the user cannot see, and the reply says the
1357
+ * work is done. `scrollables` tells the model the screen CAN scroll; it
1358
+ * never says which controls are currently off it.
1359
+ *
1360
+ * ## Why this is Fabric-only, and why that is not a cop-out
1361
+ *
1362
+ * Position in a comparable space is the whole problem. `onLayout` reports
1363
+ * coordinates relative to the PARENT, so composing them means measuring
1364
+ * every ancestor too — and ancestors are plain Views nobody tracks.
1365
+ * `measure`/`measureInWindow` are callback-based, and the introspection
1366
+ * layer is synchronous end to end (`captureSnapshot` returns a value;
1367
+ * making it async would change the signature of every caller).
1368
+ *
1369
+ * Fabric's `getBoundingClientRect(node, includeTransform)` is neither: it is
1370
+ * a synchronous JSI call returning `[x, y, width, height]` in WINDOW
1371
+ * coordinates, and React Native exposes it on host instances as
1372
+ * `unstable_getBoundingClientRect()`. That is the one API that fits, so the
1373
+ * old architecture gets no geometry and says so rather than guessing.
1374
+ *
1375
+ * ## The seam
1376
+ *
1377
+ * The reader is injected, not imported — the same reason
1378
+ * `confirmedDestructiveAlert` takes its Alert and `probeNameMangling` takes
1379
+ * its components. It also happens to be the only way this is testable at
1380
+ * all: the render tests drive `react-test-renderer`, which builds a genuine
1381
+ * fiber tree with NO host platform, so `stateNode` there is a test instance
1382
+ * with no rect to read. Everything below is policy over injected numbers;
1383
+ * the real-device half is what the Detox release lane is for.
1384
+ */
1385
+ interface Rect {
1386
+ x: number;
1387
+ y: number;
1388
+ width: number;
1389
+ height: number;
1390
+ }
1391
+ type GeometryAvailability =
1392
+ /** A host is installed and returning rects. */
1393
+ 'available'
1394
+ /** No host installed — old architecture, or the provider never wired it. */
1395
+ | 'unavailable'
1396
+ /** A host is installed but every instance refused to measure. */
1397
+ | 'silent';
1398
+
1399
+ /**
1400
+ * The ring drawn over the control the agent is acting on.
1401
+ *
1402
+ * This is the piece that makes the app the canvas instead of the chat:
1403
+ * with `presence: 'pill'` the user can see their app, and with a ring on
1404
+ * it they can see WHICH part of it the agent just touched — which is the
1405
+ * difference between watching a screen change and understanding why.
1406
+ *
1407
+ * ## Absolutely positioned Views, not SVG
1408
+ *
1409
+ * A stroked rounded rect is one `<Rect>` in `react-native-svg` and four
1410
+ * borders in plain RN. The SVG version would be marginally cleaner and
1411
+ * would put a peer dependency on every integrator who turns the
1412
+ * spotlight on, including the ones who have no other use for it. The SDK
1413
+ * has no runtime dependency beyond React Native today and this feature
1414
+ * is not worth being the first.
1415
+ *
1416
+ * ## Never interactive
1417
+ *
1418
+ * `pointerEvents="none"` throughout. The ring marks a control the user
1419
+ * can still press themselves — swallowing that press to draw decoration
1420
+ * over it would defeat the point of leaving the app touchable.
1421
+ */
1422
+
1423
+ interface SpotlightOverlayProps {
1424
+ rect: Rect;
1425
+ /** Changes on every publish, including a repeat of the same rect. */
1426
+ seq: number;
1427
+ /** Optional one-line caption. Omitted when there is no room above/below. */
1428
+ label?: string;
1429
+ color: string;
1430
+ /** Caption text/background. Unused when `label` is absent. */
1431
+ captionBackground: string;
1432
+ captionColor: string;
1433
+ fontFamily?: string;
1434
+ /** Window size, to keep the caption on screen. */
1435
+ window: {
1436
+ width: number;
1437
+ height: number;
1438
+ };
1439
+ }
1440
+ declare function SpotlightOverlay({ rect, seq, label, color, captionBackground, captionColor, fontFamily, window: win, }: SpotlightOverlayProps): React__default.JSX.Element | null;
1441
+
1442
+ /**
1443
+ * Where the agent is about to act — published by the executor, read by
1444
+ * whatever wants to draw on it.
1445
+ *
1446
+ * A tiny store rather than a prop or an event payload, because the two
1447
+ * ends are far apart and neither should learn about the other: the
1448
+ * executor resolves controls and knows nothing about rendering, and the
1449
+ * chat renders and knows nothing about how a target was resolved. Same
1450
+ * shape as `navigationState`, and for the same reason.
1451
+ *
1452
+ * ## Why the rect is measured by the publisher, not the subscriber
1453
+ *
1454
+ * By the time a React subscriber re-renders, the press has already
1455
+ * happened and the control may have moved, unmounted, or navigated away.
1456
+ * The only moment the rect is true is immediately before dispatch, so
1457
+ * the executor measures there and publishes a value. Subscribers get a
1458
+ * snapshot of a moment, not a live handle to a fiber.
1459
+ *
1460
+ * ## Why it clears itself
1461
+ *
1462
+ * A highlight with no expiry is a highlight that survives the action it
1463
+ * describes: press "Delete", navigate away, and the ring is still
1464
+ * sitting over whatever now occupies those coordinates. Every publish
1465
+ * therefore carries its own deadline and the store forgets on its own.
1466
+ */
1467
+
1468
+ interface SpotlightTarget {
1469
+ rect: Rect;
1470
+ /** Humanised description of the action, for an optional caption. */
1471
+ label?: string;
1472
+ /** Monotonic id so a subscriber can tell a re-publish from a re-render. */
1473
+ seq: number;
1474
+ }
1475
+ type Listener$2 = (target: SpotlightTarget | null) => void;
1476
+ /**
1477
+ * How long a ring stays up after the action it marks.
1478
+ *
1479
+ * Long enough to be seen — under ~400ms a highlight reads as a flicker —
1480
+ * and short enough that a fast queue does not stack rings from three
1481
+ * actions ago on top of the current one.
1482
+ */
1483
+ declare const SPOTLIGHT_TTL_MS = 1200;
1484
+ type SpotlightMode = 'off' | 'ring' | 'guided';
1485
+ declare function setSpotlightMode(next: SpotlightMode): void;
1486
+ declare function getSpotlightMode(): SpotlightMode;
1487
+ /**
1488
+ * Publish the control the agent is acting on.
1489
+ *
1490
+ * `null` clears immediately — used when an action resolves to nothing
1491
+ * measurable, so the previous ring does not linger over an unrelated
1492
+ * control and imply the agent is aiming at it.
1493
+ */
1494
+ declare function setSpotlightTarget(rect: Rect | null, label?: string): void;
1495
+ declare function getSpotlightTarget(): SpotlightTarget | null;
1496
+ /** Subscribe. Returns the unsubscribe. Does NOT fire on subscribe. */
1497
+ declare function subscribeSpotlight(listener: Listener$2): () => void;
1498
+
1499
+ /**
1500
+ * React binding over the spotlight store.
1501
+ *
1502
+ * `useSyncExternalStore` rather than a `useState` + `useEffect` pair:
1503
+ * the store is written from the executor, which runs OUTSIDE React's
1504
+ * dispatch — a press handler called directly on a fiber. Without the
1505
+ * external-store contract a publish landing mid-render can be missed
1506
+ * entirely, which for a highlight means an action nobody saw marked.
1507
+ */
1508
+
1509
+ /**
1510
+ * @param enabled When false the hook subscribes to nothing and always
1511
+ * returns null. Passing `false` rather than not calling the hook keeps
1512
+ * the call unconditional (hooks rules) while still costing nothing:
1513
+ * a disabled spotlight must not re-render the chat on every agent action.
1514
+ */
1515
+ declare function useSpotlight(enabled: boolean): SpotlightTarget | null;
1516
+
1517
+ /**
1518
+ * Bring a control into view before the agent presses it.
1519
+ *
1520
+ * The problem, restated: the executor dispatches on the fiber, so a
1521
+ * control below the fold is just as pressable as one in front of the
1522
+ * user. The app reacts, the screen changes, and nothing on screen ever
1523
+ * showed what was touched. `spotlight: 'guided'` is the setting that
1524
+ * says "do not do that" — scroll first, then act.
1525
+ *
1526
+ * The arithmetic lives in `scrollIntoView.ts`; this file is the part
1527
+ * that has to talk to registries and instances, and it is deliberately
1528
+ * conservative about when it will act at all.
1529
+ */
1530
+
1531
+ interface RevealResult {
1532
+ /** Where the control will be once the scroll lands. */
1533
+ rect: Rect;
1534
+ /** True when a scroll was actually issued. */
1535
+ scrolled: boolean;
1536
+ }
1537
+ /**
1538
+ * Scroll so `rect` is visible, and return where it will end up.
1539
+ *
1540
+ * Returns the rect UNCHANGED when nothing was or could be done, so the
1541
+ * caller always has something to publish and never has to branch on
1542
+ * "did this work".
1543
+ *
1544
+ * ## Why the returned rect is predicted rather than re-measured
1545
+ *
1546
+ * A scroll does not land within the synchronous call that starts it,
1547
+ * and the dispatch path is synchronous end to end (`captureSnapshot`
1548
+ * returns a value; making this async would change the signature of
1549
+ * every handler above it). Re-measuring here would read the position
1550
+ * BEFORE the scroll — worse than not measuring. The prediction is
1551
+ * exact, though: the offset delta is known, and a scroll translates its
1552
+ * content by exactly that.
1553
+ */
1554
+ declare function revealTarget(rect: Rect): RevealResult;
1555
+
1556
+ /**
1557
+ * How far to scroll so a control the agent is about to press is
1558
+ * actually on screen.
1559
+ *
1560
+ * ## Why this exists at all
1561
+ *
1562
+ * The executor dispatches at the fiber level: it calls `onPress` on the
1563
+ * component directly, which works whether or not the control is
1564
+ * visible. A button below the fold is mounted, reachable, and pressable
1565
+ * — so the agent presses it, the app reacts, and the user sees a screen
1566
+ * change with no visible cause. `geometry.ts` was written to detect
1567
+ * that; this is the half that fixes it.
1568
+ *
1569
+ * Pure on purpose. It takes numbers and returns a number, so every case
1570
+ * that matters — target above, below, partially cut off, a list that
1571
+ * cannot scroll far enough — is a table entry rather than a device.
1572
+ */
1573
+
1574
+ interface ScrollMetrics {
1575
+ /** Current offset, px. */
1576
+ offset: number;
1577
+ /** Viewport length, px. */
1578
+ visibleLength: number;
1579
+ /** Total content length, px. */
1580
+ contentLength: number;
1581
+ }
1582
+ /**
1583
+ * Breathing room left between the control and the viewport edge.
1584
+ *
1585
+ * A control flush against the edge is technically visible and reads as
1586
+ * cut off, and on a phone the edge is where the notch, the home
1587
+ * indicator and any sticky header actually live.
1588
+ */
1589
+ declare const REVEAL_MARGIN = 24;
1590
+ interface RevealInput {
1591
+ /** Target, in the same coordinate space as the viewport below. */
1592
+ rect: Rect;
1593
+ /**
1594
+ * The scrollable's viewport in WINDOW coordinates.
1595
+ *
1596
+ * Not the window itself: a list occupying the middle of the screen
1597
+ * under a header and above a tab bar has a viewport that starts below
1598
+ * zero-y, and scrolling a target to "y = 24" would put it under the
1599
+ * header.
1600
+ */
1601
+ viewport: {
1602
+ top: number;
1603
+ height: number;
1604
+ };
1605
+ metrics: ScrollMetrics;
1606
+ }
1607
+ /**
1608
+ * The offset to scroll to, or null when nothing needs to move.
1609
+ *
1610
+ * Null covers two different situations on purpose — already visible,
1611
+ * and no scroll that would help — because the caller does the same
1612
+ * thing in both: dispatch anyway and let the geometry policy report
1613
+ * what it can see. The distinction is the caller's to log, not ours to
1614
+ * encode in a magic number.
1615
+ */
1616
+ declare function offsetToReveal({ rect, viewport, metrics }: RevealInput): number | null;
1617
+
1618
+ /**
1619
+ * What the chat surface shows, given the experience config and where
1620
+ * the current turn is.
1621
+ *
1622
+ * Split out of AppilotsChat for the same reason as `chatTimeline.ts`:
1623
+ * the interesting part is a handful of precedence rules, and rules are
1624
+ * far cheaper to get right against a table of cases than against a
1625
+ * rendered tree.
1626
+ *
1627
+ * The one rule worth stating out loud is the safety rule in
1628
+ * `resolveSurface`: a question that blocks the turn is never hidden,
1629
+ * whatever the config asks for. Every other knob here trades visibility
1630
+ * for a better view of the app; that one would trade it for a turn that
1631
+ * hangs forever with nothing on screen explaining why.
1632
+ */
1633
+
1634
+ /** What the chat renders right now. */
1635
+ type SurfaceState =
1636
+ /** Full sheet / modal — the app is covered. */
1637
+ 'sheet'
1638
+ /** Collapsed status line — the app is visible and interactive. */
1639
+ | 'pill'
1640
+ /** Nothing but the trigger (if the entry config renders one). */
1641
+ | 'none';
1642
+ interface TurnActivity {
1643
+ /** Waiting on the model, or draining the action queue. */
1644
+ busy: boolean;
1645
+ /** An app action has started in this request, including continuation waits. */
1646
+ acting?: boolean;
1647
+ /**
1648
+ * An action is parked waiting for a human answer — a confirm gate, or
1649
+ * any pending action under `autoExecute: false`.
1650
+ */
1651
+ awaitingUser: boolean;
1652
+ /** The user wants the sheet up (tapped the trigger, or `visible` is set). */
1653
+ open: boolean;
1654
+ /** Host app still owns an open modal/sheet; defer automatic presentation. */
1655
+ hostModalOpen?: boolean;
1656
+ /** An explicit tap may expand the reply; automatic turn-end expansion may not. */
1657
+ explicitlyOpened?: boolean;
1658
+ /**
1659
+ * A turn has finished and nobody has looked at the result yet.
1660
+ *
1661
+ * Only `presence: 'hidden'` can reach this: it drops the sheet for
1662
+ * real when a turn starts, so without a resting pill the turn would
1663
+ * end with nothing on screen and the answer buried behind the
1664
+ * trigger. `pill` reopens instead, and `sheet` never left.
1665
+ */
1666
+ unread?: boolean;
1667
+ }
1668
+ /**
1669
+ * Resolve the surface for one render.
1670
+ *
1671
+ * `presence: 'sheet'` is deliberately the first branch and returns
1672
+ * before any other rule runs — it must reproduce the pre-experience
1673
+ * behavior byte for byte, so an app that never sets `experience` cannot
1674
+ * observe that this module was introduced at all.
1675
+ */
1676
+ declare function resolveSurface(presence: AppilotsPresence, activity: TurnActivity): SurfaceState;
1677
+ /**
1678
+ * Should the sheet come back by itself when the turn finishes?
1679
+ *
1680
+ * `pill` is the "watch it work, then read the answer" loop: the sheet
1681
+ * steps aside for the execution and returns with the reply. `hidden`
1682
+ * means the user asked not to be interrupted, so the reply waits behind
1683
+ * the pill until they ask for it.
1684
+ */
1685
+ declare function shouldReopenOnTurnEnd(presence: AppilotsPresence): boolean;
1686
+ /**
1687
+ * Does this presence ever collapse a live turn onto the pill?
1688
+ *
1689
+ * Callers use it to decide whether to pay for the pill's subscription
1690
+ * and animation at all — under `sheet` none of it can ever render.
1691
+ */
1692
+ declare function usesPill(presence: AppilotsPresence): boolean;
1693
+
1694
+ interface PillStatus {
1695
+ label: string;
1696
+ state: BreadcrumbState;
1697
+ }
1698
+ interface PillStatusStrings {
1699
+ /** Turn is live but no action has been dispatched yet. */
1700
+ working: string;
1701
+ locale?: AppilotsLocale;
1702
+ /** Turn is over and the result has not been read. */
1703
+ done: string;
1704
+ }
1705
+ /**
1706
+ * Pick the line for this frame.
376
1707
  *
377
- * BACKLOG 4.2 — theming: this module used to hardcode every color
378
- * (`#FFFFFF` card background, `#DC2626` destructive red, etc.), which is
379
- * why the theme's `secondary`/`success`/`warning`/`error` tokens had no
380
- * effect on the chat at all — nothing consumed them. Every color now
381
- * comes from a `BreadcrumbPalette` the caller passes in (ultimately
382
- * `useAppilotsTheme()` in AppilotsChat). `DEFAULT_BREADCRUMB_PALETTE` is
383
- * built from the SDK's bundled `defaultLightTheme`, and `tokens.ts` picks
384
- * its `success`/`warning`/`error` defaults to exactly reproduce the OLD
385
- * hardcoded hex values below — so a caller on the default theme renders
386
- * pixel-identical to before.
1708
+ * `actions` is the whole turn in emission order — the same array the
1709
+ * breadcrumb renders, unfiltered, so callers don't have to keep a
1710
+ * second one in sync.
387
1711
  */
1712
+ declare function resolvePillStatus(actions: readonly AgentAction[], busy: boolean, strings: PillStatusStrings): PillStatus;
388
1713
 
389
1714
  /**
390
1715
  * The subset of theme tokens the breadcrumb (rows + confirm card)
@@ -425,26 +1750,6 @@ interface ConfirmCardStrings {
425
1750
  confirmThisAction: string;
426
1751
  }
427
1752
 
428
- /**
429
- * ActionBreadcrumb — compact, line-per-action progress UI.
430
- *
431
- * Replaces the per-action "card" UI with something closer to a CI build
432
- * log: each action is one line, stamped with an icon that reflects its
433
- * lifecycle state. Spinners on running actions, checks on success, ✗
434
- * on failure. Failures show a humanised reason in parens.
435
- *
436
- * Why a separate component (instead of leaving render code inline in
437
- * AppilotsChat.tsx): the lifecycle state animation is non-trivial and
438
- * the same UI is now also rendered in three places (Mailing the spec
439
- * — empty state, between message bubbles, and inline footer). Keeping
440
- * it in one file makes the styling consistent.
441
- *
442
- * The component does NOT decide approve/reject visibility on its own —
443
- * the caller passes `requireApprovalFor(action)` so this component stays
444
- * presentational and the policy lives where the rest of the auto-execute
445
- * config does (AppilotsChat).
446
- */
447
-
448
1753
  interface ActionBreadcrumbProps {
449
1754
  /** Actions to render, ordered by emission. Caller dedupes / groups. */
450
1755
  actions: AgentAction[];
@@ -466,6 +1771,7 @@ interface ActionBreadcrumbProps {
466
1771
  * the pt-BR literals it always shipped (#402). AppilotsChat passes it.
467
1772
  */
468
1773
  confirmStrings?: ConfirmCardStrings;
1774
+ locale?: AppilotsLocale;
469
1775
  /**
470
1776
  * Approve/reject inline buttons appear when this returns true for an
471
1777
  * action in `pending` state. `confirm`-type actions are normally
@@ -476,7 +1782,7 @@ interface ActionBreadcrumbProps {
476
1782
  onApprove?: (actionId: string) => void;
477
1783
  onReject?: (actionId: string) => void;
478
1784
  }
479
- declare function ActionBreadcrumb({ actions, primaryColor, textColor, fontFamily, palette, confirmStrings, requireApprovalFor, onApprove, onReject, }: ActionBreadcrumbProps): React__default.JSX.Element | null;
1785
+ declare function ActionBreadcrumb({ actions, primaryColor, textColor, fontFamily, palette, confirmStrings, locale, requireApprovalFor, onApprove, onReject, }: ActionBreadcrumbProps): React__default.JSX.Element | null;
480
1786
 
481
1787
  interface ChatIconProps {
482
1788
  /** Outer width/height in points. Default 24. */
@@ -514,6 +1820,16 @@ interface HeadsetIconProps {
514
1820
  */
515
1821
  declare function HeadsetIcon({ size, color }: HeadsetIconProps): React$1.JSX.Element;
516
1822
 
1823
+ type Observation = NonNullable<ListSnapshot['exploration']>;
1824
+ /** Local, bounded metadata only. No text, records, callbacks or historical action targets. */
1825
+ declare function createListExploration(): {
1826
+ reset: () => void;
1827
+ update(nextKeys: Array<string | undefined>, nextCount: number | undefined, nextSource: unknown, nextScope: unknown, mayPaginate: boolean): void;
1828
+ observe(items: ListSnapshot["items"]): void;
1829
+ recordScroll(outcome: NonNullable<Observation["lastScroll"]>): void;
1830
+ snapshot(): Observation;
1831
+ };
1832
+
517
1833
  /**
518
1834
  * ListRegistry — runtime metadata for list/collection components.
519
1835
  *
@@ -553,12 +1869,43 @@ interface ListEntry {
553
1869
  * container that has no third anything.
554
1870
  */
555
1871
  kind: 'list' | 'scroll';
1872
+ /** Runtime-only mission memory. Never serialize this object. */
1873
+ exploration?: ReturnType<typeof createListExploration>;
556
1874
  /** Stable runtime id assigned from testID/accessibilityLabel or generated. */
557
1875
  id: string;
558
1876
  /** Component name: FlatList, SectionList, VirtualizedList, FlashList, etc. */
559
1877
  component: string;
560
- /** Total items in the backing data set when the component exposes it. */
1878
+ /**
1879
+ * How many items the list is CURRENTLY RENDERING FROM — `data.length`,
1880
+ * not the size of the collection it is a window onto.
1881
+ *
1882
+ * The distinction is the whole point and the old wording ("total items
1883
+ * in the backing data set") got it wrong. For a list that holds all its
1884
+ * data the two coincide, so nothing downstream noticed. For a PAGINATED
1885
+ * list they never coincide: a screen showing page 1 of 36 vehicles has
1886
+ * `itemCount === 20`, and every consumer that read this as the total
1887
+ * told the agent the list had twenty items in it.
1888
+ *
1889
+ * The agent then answers "you have 20 vehicles" — not a hedge, not a
1890
+ * partial reading, a wrong count stated plainly — and it has no way to
1891
+ * know better, because nothing in the observation distinguishes "this
1892
+ * is all of them" from "this is the first page". Where the app knows
1893
+ * the real total, `totalItemCount` below carries it.
1894
+ */
561
1895
  itemCount?: number;
1896
+ /**
1897
+ * Size of the full collection, when the APP knows it and the list
1898
+ * cannot: a server-reported total behind pagination, a filtered count,
1899
+ * an infinite feed's header figure.
1900
+ *
1901
+ * Only the app can supply this — it lives in the response the screen
1902
+ * fetched, never in the RN component, which by construction sees only
1903
+ * the page it was handed. Left undefined it means exactly "unknown",
1904
+ * which is honest and different from "equal to itemCount": a consumer
1905
+ * must not fill it in from `itemCount`, because that is precisely the
1906
+ * conflation this field exists to break.
1907
+ */
1908
+ totalItemCount?: number;
562
1909
  /** Human-readable label if the app supplied one. */
563
1910
  label?: string;
564
1911
  /** Screen where this list is registered, when available. */
@@ -579,6 +1926,22 @@ interface ListEntry {
579
1926
  scrollToOffset?: (offset: number) => boolean;
580
1927
  /** Read live scroll metrics off the component instance, when possible. */
581
1928
  getScrollMetrics?: () => ListScrollMetrics | undefined;
1929
+ /**
1930
+ * The scrollable's own rect in WINDOW coordinates, when geometry is
1931
+ * available.
1932
+ *
1933
+ * Needed because the viewport is not the window: a list sitting under
1934
+ * a header and above a tab bar has a usable band that starts well
1935
+ * below y=0, and scrolling a control to the top of the SCREEN would
1936
+ * park it under that header — visible to geometry, invisible to the
1937
+ * person watching.
1938
+ */
1939
+ measureViewport?: () => {
1940
+ x: number;
1941
+ y: number;
1942
+ width: number;
1943
+ height: number;
1944
+ } | null;
582
1945
  }
583
1946
  type Listener$1 = (id: string, entry: ListEntry | null) => void;
584
1947
  type ListRegistry = ListRegistryImpl;
@@ -651,33 +2014,41 @@ declare function AppilotsRegistryProvider({ value, children, }: {
651
2014
  declare function useResolvedRegistry(): ComponentRegistry;
652
2015
 
653
2016
  /**
654
- * initAppilots — SDK initialisation & auto-config resolution.
655
- *
656
- * The preferred setup is fully automatic:
657
- *
658
- * 1. Add `withAppilots()` to your metro.config.js (one-time):
659
- * ```js
660
- * const { withAppilots } = require('@appilots/sdk/metro');
661
- * module.exports = withAppilots(mergeConfig(defaultConfig, config));
662
- * ```
663
- *
664
- * 2. Create a `.appilotsrc` JSON file at the project root.
2017
+ * Declare the size of the collection a list is a WINDOW onto.
665
2018
  *
666
- * 3. Use AppilotsProvider with zero props — config is loaded automatically:
667
2019
  * ```tsx
668
- * <AppilotsProvider>
669
- * <App />
670
- * <AppilotsChat />
671
- * </AppilotsProvider>
2020
+ * <FlatList data={page} {...appilotsListTotal(total)} … />
672
2021
  * ```
673
2022
  *
674
- * You can also call initAppilots() manually if you prefer explicit control:
675
- * ```tsx
676
- * initAppilots({ projectId: 'my-app', apiKey: 'ak_...' });
677
- * ```
2023
+ * A list that pages cannot be asked this: its `data` is the page, so
2024
+ * every count derived from the component describes the window and not
2025
+ * what the window looks onto. The app is the only party holding the
2026
+ * number — it arrived in the same response as the page — so the SDK
2027
+ * takes it as a declaration and never infers it.
2028
+ *
2029
+ * Pass `undefined` when the total is genuinely unknown (still loading,
2030
+ * an endless feed). Undefined is a different fact from "the same as what
2031
+ * is loaded", and the SDK keeps them apart: a list with no declared
2032
+ * total reports only what it has, which is exactly what is known.
2033
+ *
2034
+ * ── Why a helper and not just the prop ────────────────────────
2035
+ *
2036
+ * `FlatListProps` is React Native's type, not ours. Augmenting it
2037
+ * globally would add an Appilots prop to every list in every consumer's
2038
+ * app, including apps that never integrated — a package reaching into
2039
+ * another package's public types. A JSX spread carries the extra key
2040
+ * without an excess-property error, so this stays narrow, explicit, and
2041
+ * greppable: someone reading the screen sees where the number came from.
678
2042
  */
2043
+ declare function appilotsListTotal(total: number | undefined): {
2044
+ appilotsTotalItemCount: number | undefined;
2045
+ };
679
2046
 
680
2047
  interface AppilotsRC {
2048
+ /** Restore a session handle using the host's storage; no UI snapshots are stored. */
2049
+ sessionStorage?: AppilotsSessionStorage;
2050
+ /** Metro reuses installed AsyncStorage by default. Set false to keep sessions in memory. */
2051
+ sessionPersistence?: boolean;
681
2052
  /** Project ID from the Appilots dashboard */
682
2053
  projectId: string;
683
2054
  /** SDK API key (ak_...) */
@@ -903,14 +2274,6 @@ interface NameManglingProbe {
903
2274
  observed: Record<string, string | null>;
904
2275
  }
905
2276
 
906
- type GeometryAvailability =
907
- /** A host is installed and returning rects. */
908
- 'available'
909
- /** No host installed — old architecture, or the provider never wired it. */
910
- | 'unavailable'
911
- /** A host is installed but every instance refused to measure. */
912
- | 'silent';
913
-
914
2277
  /**
915
2278
  * walkFiber — Depth-first traversal of a React fiber tree.
916
2279
  *
@@ -1261,8 +2624,16 @@ interface ActionResult {
1261
2624
  interface ExecutorContext {
1262
2625
  /** React Navigation ref for navigate actions */
1263
2626
  navigationRef: React.MutableRefObject<any>;
1264
- /** SDK permissions from AppilotsConfig */
1265
- permissions: AgentPermissions;
2627
+ /**
2628
+ * SDK permissions from AppilotsConfig.
2629
+ *
2630
+ * PARTIAL on purpose: `.appilotsrc` is JSON and nothing on the way
2631
+ * here types it, so a config naming three of the four flags is the
2632
+ * everyday case. Every flag it omits takes the default —
2633
+ * `resolveAgentPermissions` is what guarantees that, and the reason
2634
+ * it has to is in its own header.
2635
+ */
2636
+ permissions?: Partial<AgentPermissions>;
1266
2637
  /** Event emitter from AppilotsProvider */
1267
2638
  emit: (event: AppilotsEvent) => void;
1268
2639
  /**
@@ -1284,6 +2655,6 @@ declare function executeAction(action: AgentAction, context: ExecutorContext): P
1284
2655
  * (`X-Appilots-Sdk-Version`). Kept identical to package.json by the
1285
2656
  * release flow; `version.test.ts` fails if the two ever disagree.
1286
2657
  */
1287
- declare const SDK_VERSION = "0.11.3";
2658
+ declare const SDK_VERSION = "0.13.0";
1288
2659
 
1289
- export { ActionBreadcrumb, type ActionBreadcrumbProps, type ActionResult, AgentAction, AgentPermissions, type AppilotsButtonProps, AppilotsChat, type AppilotsChatMode, type AppilotsChatPosition, type AppilotsChatProps, type AppilotsChatTheme, AppilotsEvent, AppilotsI18nProvider, type AppilotsI18nProviderProps, type AppilotsInputProps, AppilotsLocale, type AppilotsRC, AppilotsRegistryProvider, type AppilotsSelfCheckReport, type AppilotsStringKey, type AppilotsStrings, type AppilotsSwitchProps, AppilotsThemeProvider, type AppilotsThemeProviderProps, AppilotsThemeTokens, type AutoTrackingState, ChatIcon, type ChatIconProps, ComponentRegistry, ConfirmDialog, type ConfirmDialogProps, type ElementRegistry, type ExecutorContext, FieldEntry, HeadsetIcon, type HeadsetIconProps, InteractionElementEntry, type IntrospectionDiagnostics, type IntrospectionFailureReason, type ListEntry, type ListRegistry, type NameManglingProbe, PartialThemeTokens, SDK_VERSION, ScreenSnapshot, type SelfCheckRegistry, type SelfCheckWalk, type TailwindResolvedConfig, type WalkDetectorCounts, _patchJsxRuntimes, appilotsSelfCheck, appilotsTheme, appilotsThemeFromTailwind, captureSnapshot, createAppilotsButton, createAppilotsInput, createAppilotsSwitch, createElementRegistry, createListRegistry, detectDeviceLocale, elementRegistry, enableAppilotsAutoTracking, executeAction, getAutoTrackingState, getGlobalConfig, getIntrospectionDiagnostics, initAppilots, isAutoTrackingEnabled, listRegistry, resolveLocale, useAppilotsI18n, useAppilotsTheme, useResolvedRegistry };
2660
+ export { ActionBreadcrumb, type ActionBreadcrumbProps, type ActionResult, AgentAction, AgentPermissions, AgentPill, type AgentPillProps, type AgentPillStrings, type AppilotsButtonProps, AppilotsChat, type AppilotsChatMode, type AppilotsChatPosition, type AppilotsChatProps, type AppilotsChatTheme, type AppilotsControl, type AppilotsEntryPoint, AppilotsEvent, type AppilotsExperience, AppilotsI18nProvider, type AppilotsI18nProviderProps, type AppilotsInputMode, type AppilotsInputProps, AppilotsLocale, type AppilotsPresence, type AppilotsRC, AppilotsRegistryProvider, type AppilotsSelfCheckReport, type AppilotsSpotlight, type AppilotsStringKey, type AppilotsStrings, type AppilotsSwitchProps, AppilotsThemeProvider, type AppilotsThemeProviderProps, AppilotsThemeTokens, AppilotsTraceEntry, type AutoTrackingState, type BreadcrumbItem, type BreadcrumbState, type ButtonSnapshot, ChatIcon, type ChatIconProps, type ChoiceGroupSnapshot, type ChoiceOptionSnapshot, ComponentRegistry, ConfirmDialog, type ConfirmDialogProps, DEFAULT_EXPERIENCE, type ElementRegistry, type ExecutorContext, type ExperienceCapabilities, type ExperienceDowngrade, type ExperienceResolution, FieldEntry, HeadsetIcon, type HeadsetIconProps, type InputSnapshot, type InteractionElementEntry, type InteractionElementListContext, type InteractionElementSnapshot, type IntrospectionDiagnostics, type IntrospectionFailureReason, type ListEntry, type ListItemSnapshot, type ListRegistry, type ListSnapshot, type NameManglingProbe, PartialThemeTokens, type PillStatus, type PillStatusStrings, REVEAL_MARGIN, type ResolvedExperience, type RevealInput, type RevealResult, SDK_VERSION, SPOTLIGHT_TTL_MS, type ScreenSnapshot, type ScrollMetrics, type SelfCheckRegistry, type SelfCheckWalk, type SpotlightMode, SpotlightOverlay, type SpotlightOverlayProps, type SpotlightTarget, type SurfaceState, type TailwindResolvedConfig, type ToggleSnapshot, type TurnActivity, type WalkDetectorCounts, _patchJsxRuntimes, appilotsListTotal, appilotsSelfCheck, appilotsTheme, appilotsThemeFromTailwind, captureSnapshot, clearAppilotsDebugTraces, createAppilotsButton, createAppilotsInput, createAppilotsSwitch, createElementRegistry, createListRegistry, describeAction, detectDeviceLocale, elementRegistry, enableAppilotsAutoTracking, executeAction, formatDowngrades, getAppilotsDebugTraces, getAutoTrackingState, getGlobalConfig, getIntrospectionDiagnostics, getSpotlightMode, getSpotlightTarget, humanizeError, initAppilots, isAutoTrackingEnabled, listRegistry, offsetToReveal, recordAppilotsDebugTrace, resolveExperience, resolveLocale, resolvePillStatus, resolveSurface, revealTarget, setSpotlightMode, setSpotlightTarget, shouldReopenOnTurnEnd, subscribeAppilotsDebugTraces, subscribeSpotlight, useAppilotsI18n, useAppilotsTheme, useResolvedRegistry, useSpotlight, usesPill };