@appilots/sdk 0.11.1 → 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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkKZ2BLMS4_js = require('./chunk-KZ2BLMS4.js');
3
+ var chunkUZ2D4BVX_js = require('./chunk-UZ2D4BVX.js');
4
4
  var React = require('react');
5
5
  var reactNative = require('react-native');
6
6
 
@@ -8,6 +8,261 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
8
 
9
9
  var React__default = /*#__PURE__*/_interopDefault(React);
10
10
 
11
+ // src/i18n/bundles/pt-BR.ts
12
+ var ptBR = {
13
+ chatTitle: "Assistente Appilots",
14
+ clearButton: "Limpar",
15
+ inputPlaceholder: "Digite uma mensagem...",
16
+ emptyTitle: "Como posso ajudar?",
17
+ emptySubtitle: "Navego, preencho formul\xE1rios e executo a\xE7\xF5es neste app.",
18
+ thinking: "Pensando...",
19
+ statusAnalyzing: "Analisando a tela...",
20
+ statusWaitingApp: "Aguardando o app responder...",
21
+ statusAdjusting: "Ajustando o plano...",
22
+ poweredBy: "Powered by Appilots",
23
+ approve: "Aprovar",
24
+ reject: "Rejeitar",
25
+ cancelledByUser: "Cancelado pelo usu\xE1rio",
26
+ confirmDestructiveTitle: "A\xE7\xE3o destrutiva",
27
+ confirmTitle: "Confirma\xE7\xE3o necess\xE1ria",
28
+ confirmMessage: 'Tem certeza que deseja "{action}"?',
29
+ confirmDestructiveApprove: "Sim, executar",
30
+ confirmApprove: "Confirmar",
31
+ confirmCancel: "Cancelar",
32
+ confirmThisAction: "esta a\xE7\xE3o",
33
+ unknownAction: "A\xE7\xE3o desconhecida",
34
+ somethingWentWrong: "Algo deu errado",
35
+ openChatA11y: "Abrir chat do assistente",
36
+ closeChatA11y: "Fechar chat do assistente",
37
+ stopGeneratingA11y: "Parar gera\xE7\xE3o",
38
+ talkToHumanA11y: "Falar com um atendente humano",
39
+ humanAgentLabel: "Atendente",
40
+ escalationRequested: "Certo! Chamei um atendente humano. Voc\xEA pode continuar usando o app normalmente enquanto isso.",
41
+ escalationConnected: "Um atendente entrou na conversa.",
42
+ escalationResolved: "O atendimento humano foi encerrado. Voltei a te ajudar por aqui.",
43
+ escalationOffer: "Se preferir, posso chamar uma pessoa para ajudar.",
44
+ escalationOfferChip: "Falar com um atendente",
45
+ escalationPendingBanner: "Aguardando um atendente...",
46
+ escalationActiveBanner: "Voc\xEA est\xE1 falando com um atendente",
47
+ pillWorking: "Trabalhando...",
48
+ pillExpandA11y: "Abrir a conversa do assistente",
49
+ pillStopA11y: "Parar o assistente",
50
+ missionPause: "Pausar",
51
+ missionResume: "Retomar tarefa",
52
+ missionCancel: "Cancelar tarefa",
53
+ missionPaused: "Pausada",
54
+ pillDone: "Pronto \u2014 toque para ver",
55
+ micStartA11y: "Segurar para falar",
56
+ micStopA11y: "Soltar para enviar",
57
+ micListening: "Ouvindo..."
58
+ };
59
+
60
+ // src/i18n/bundles/en.ts
61
+ var en = {
62
+ chatTitle: "Appilots Assistant",
63
+ clearButton: "Clear",
64
+ inputPlaceholder: "Type a message...",
65
+ emptyTitle: "How can I help?",
66
+ emptySubtitle: "I navigate, fill forms, and run actions in this app.",
67
+ thinking: "Thinking...",
68
+ statusAnalyzing: "Reading the screen...",
69
+ statusWaitingApp: "Waiting for the app...",
70
+ statusAdjusting: "Adjusting the plan...",
71
+ poweredBy: "Powered by Appilots",
72
+ approve: "Approve",
73
+ reject: "Reject",
74
+ cancelledByUser: "Cancelled by user",
75
+ confirmDestructiveTitle: "Destructive action",
76
+ confirmTitle: "Confirmation needed",
77
+ confirmMessage: 'Are you sure you want to "{action}"?',
78
+ confirmDestructiveApprove: "Yes, do it",
79
+ confirmApprove: "Confirm",
80
+ confirmCancel: "Cancel",
81
+ confirmThisAction: "this action",
82
+ unknownAction: "Unknown action",
83
+ somethingWentWrong: "Something went wrong",
84
+ openChatA11y: "Open assistant chat",
85
+ closeChatA11y: "Close assistant chat",
86
+ stopGeneratingA11y: "Stop generating",
87
+ talkToHumanA11y: "Talk to a human agent",
88
+ humanAgentLabel: "Agent",
89
+ escalationRequested: "Done! I've called a human agent. You can keep using the app normally in the meantime.",
90
+ escalationConnected: "A human agent joined the conversation.",
91
+ escalationResolved: "The human handoff has ended. I'm back to help you here.",
92
+ escalationOffer: "If you prefer, I can bring in a person to help.",
93
+ escalationOfferChip: "Talk to a human",
94
+ escalationPendingBanner: "Waiting for a human agent...",
95
+ escalationActiveBanner: "You're talking to a human agent",
96
+ pillWorking: "Working...",
97
+ pillExpandA11y: "Open the assistant conversation",
98
+ pillStopA11y: "Stop the assistant",
99
+ missionPause: "Pause",
100
+ missionResume: "Resume task",
101
+ missionCancel: "Cancel task",
102
+ missionPaused: "Paused",
103
+ pillDone: "Done \u2014 tap to read",
104
+ micStartA11y: "Hold to talk",
105
+ micStopA11y: "Release to send",
106
+ micListening: "Listening..."
107
+ };
108
+
109
+ // src/i18n/bundles/es.ts
110
+ var es = {
111
+ chatTitle: "Asistente Appilots",
112
+ clearButton: "Limpiar",
113
+ inputPlaceholder: "Escribe un mensaje...",
114
+ emptyTitle: "\xBFEn qu\xE9 puedo ayudarte?",
115
+ emptySubtitle: "Navego, relleno formularios y ejecuto acciones en esta app.",
116
+ thinking: "Pensando...",
117
+ statusAnalyzing: "Analizando la pantalla...",
118
+ statusWaitingApp: "Esperando a la app...",
119
+ statusAdjusting: "Ajustando el plan...",
120
+ poweredBy: "Hecho con Appilots",
121
+ approve: "Aprobar",
122
+ reject: "Rechazar",
123
+ cancelledByUser: "Cancelado por el usuario",
124
+ confirmDestructiveTitle: "Acci\xF3n destructiva",
125
+ confirmTitle: "Confirmaci\xF3n necesaria",
126
+ confirmMessage: '\xBFSeguro que quieres "{action}"?',
127
+ confirmDestructiveApprove: "S\xED, ejecutar",
128
+ confirmApprove: "Confirmar",
129
+ confirmCancel: "Cancelar",
130
+ confirmThisAction: "esta acci\xF3n",
131
+ unknownAction: "Acci\xF3n desconocida",
132
+ somethingWentWrong: "Algo sali\xF3 mal",
133
+ openChatA11y: "Abrir chat del asistente",
134
+ closeChatA11y: "Cerrar chat del asistente",
135
+ stopGeneratingA11y: "Detener generaci\xF3n",
136
+ talkToHumanA11y: "Hablar con un agente humano",
137
+ humanAgentLabel: "Agente",
138
+ escalationRequested: "\xA1Listo! Llam\xE9 a un agente humano. Mientras tanto puedes seguir usando la app con normalidad.",
139
+ escalationConnected: "Un agente humano se uni\xF3 a la conversaci\xF3n.",
140
+ escalationResolved: "La atenci\xF3n humana termin\xF3. Vuelvo a ayudarte por aqu\xED.",
141
+ escalationOffer: "Si prefieres, puedo llamar a una persona para ayudarte.",
142
+ escalationOfferChip: "Hablar con un agente",
143
+ escalationPendingBanner: "Esperando a un agente...",
144
+ escalationActiveBanner: "Est\xE1s hablando con un agente humano",
145
+ pillWorking: "Trabajando...",
146
+ pillExpandA11y: "Abrir la conversaci\xF3n del asistente",
147
+ pillStopA11y: "Detener el asistente",
148
+ missionPause: "Pausar",
149
+ missionResume: "Reanudar tarea",
150
+ missionCancel: "Cancelar tarea",
151
+ missionPaused: "Pausada",
152
+ pillDone: "Listo \u2014 toca para ver",
153
+ micStartA11y: "Mantener para hablar",
154
+ micStopA11y: "Soltar para enviar",
155
+ micListening: "Escuchando..."
156
+ };
157
+
158
+ // src/i18n/bundles/fr.ts
159
+ var fr = {
160
+ chatTitle: "Assistant Appilots",
161
+ clearButton: "Effacer",
162
+ inputPlaceholder: "\xC9crivez un message\u2026",
163
+ emptyTitle: "Comment puis-je vous aider ?",
164
+ emptySubtitle: "Je navigue, remplis les formulaires et effectue des actions dans cette application.",
165
+ thinking: "R\xE9flexion en cours\u2026",
166
+ statusAnalyzing: "Lecture de l\u2019\xE9cran\u2026",
167
+ statusWaitingApp: "En attente de l\u2019application\u2026",
168
+ statusAdjusting: "Ajustement du plan\u2026",
169
+ poweredBy: "Propuls\xE9 par Appilots",
170
+ approve: "Approuver",
171
+ reject: "Refuser",
172
+ cancelledByUser: "Annul\xE9 par l\u2019utilisateur",
173
+ confirmDestructiveTitle: "Action destructive",
174
+ confirmTitle: "Confirmation requise",
175
+ confirmMessage: "Voulez-vous vraiment \xAB {action} \xBB ?",
176
+ confirmDestructiveApprove: "Oui, ex\xE9cuter",
177
+ confirmApprove: "Confirmer",
178
+ confirmCancel: "Annuler",
179
+ confirmThisAction: "cette action",
180
+ unknownAction: "Action inconnue",
181
+ somethingWentWrong: "Une erreur est survenue",
182
+ openChatA11y: "Ouvrir la conversation avec l\u2019assistant",
183
+ closeChatA11y: "Fermer la conversation",
184
+ stopGeneratingA11y: "Arr\xEAter la g\xE9n\xE9ration",
185
+ talkToHumanA11y: "Parler \xE0 un conseiller",
186
+ humanAgentLabel: "Conseiller",
187
+ escalationRequested: "J\u2019ai contact\xE9 un conseiller. Vous pouvez continuer \xE0 utiliser l\u2019application en attendant.",
188
+ escalationConnected: "Un conseiller a rejoint la conversation.",
189
+ escalationResolved: "L\u2019intervention du conseiller est termin\xE9e. Je peux \xE0 nouveau vous aider.",
190
+ escalationOffer: "Si vous pr\xE9f\xE9rez, je peux faire appel \xE0 un conseiller.",
191
+ escalationOfferChip: "Parler \xE0 un conseiller",
192
+ escalationPendingBanner: "En attente d\u2019un conseiller\u2026",
193
+ escalationActiveBanner: "Vous discutez avec un conseiller",
194
+ pillWorking: "En cours\u2026",
195
+ pillExpandA11y: "Ouvrir la conversation avec l\u2019assistant",
196
+ pillStopA11y: "Arr\xEAter l\u2019assistant",
197
+ missionPause: "Mettre en pause",
198
+ missionResume: "Reprendre la t\xE2che",
199
+ missionCancel: "Annuler la t\xE2che",
200
+ missionPaused: "En pause",
201
+ pillDone: "Termin\xE9 \u2014 touchez pour lire",
202
+ micStartA11y: "Maintenez pour parler",
203
+ micStopA11y: "Rel\xE2chez pour envoyer",
204
+ micListening: "\xC9coute en cours\u2026"
205
+ };
206
+
207
+ // src/i18n/I18nProvider.tsx
208
+ var BUNDLES = {
209
+ "pt-BR": ptBR,
210
+ en,
211
+ es,
212
+ fr
213
+ };
214
+ var DEFAULT_LOCALE = "en";
215
+ function detectDeviceLocale() {
216
+ try {
217
+ let raw;
218
+ if (reactNative.Platform.OS === "ios") {
219
+ const manager = reactNative.NativeModules?.SettingsManager;
220
+ const settings = manager?.getConstants?.()?.settings ?? manager?.settings;
221
+ const languages = Array.isArray(settings?.AppleLanguages) ? settings.AppleLanguages : [];
222
+ for (const language of languages) {
223
+ const locale = chunkUZ2D4BVX_js.normalizeAppilotsLocale(language);
224
+ if (locale) return locale;
225
+ }
226
+ raw = settings?.AppleLocale;
227
+ } else if (reactNative.Platform.OS === "android") {
228
+ const manager = reactNative.NativeModules?.I18nManager;
229
+ raw = manager?.getConstants?.()?.localeIdentifier ?? manager?.localeIdentifier;
230
+ }
231
+ if (!raw || typeof raw !== "string") return null;
232
+ return chunkUZ2D4BVX_js.normalizeAppilotsLocale(raw) ?? null;
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+ function resolveLocale(prop) {
238
+ if (prop && BUNDLES[prop]) return prop;
239
+ const detected = detectDeviceLocale();
240
+ if (detected && BUNDLES[detected]) return detected;
241
+ return DEFAULT_LOCALE;
242
+ }
243
+ var I18nContext = React.createContext(null);
244
+ function AppilotsI18nProvider({ locale, overrides, children }) {
245
+ const value = React.useMemo(() => {
246
+ const resolved = resolveLocale(locale ?? null);
247
+ const merged = overrides ? { ...BUNDLES[resolved], ...overrides } : BUNDLES[resolved];
248
+ return {
249
+ locale: resolved,
250
+ strings: merged,
251
+ t: (key) => merged[key]
252
+ };
253
+ }, [locale, overrides]);
254
+ return /* @__PURE__ */ React__default.default.createElement(I18nContext.Provider, { value }, children);
255
+ }
256
+ function useAppilotsI18n() {
257
+ const ctx = React.useContext(I18nContext);
258
+ if (ctx) return ctx;
259
+ return {
260
+ locale: DEFAULT_LOCALE,
261
+ strings: BUNDLES[DEFAULT_LOCALE],
262
+ t: (key) => BUNDLES[DEFAULT_LOCALE][key]
263
+ };
264
+ }
265
+
11
266
  // src/introspection/captureSnapshot.ts
12
267
  function normalizeSliderKey(value) {
13
268
  return (value ?? "").toLowerCase().replace(/^slider[-_]/, "");
@@ -23,9 +278,9 @@ function fiberSliderMatches(fiber, registryId, registryLabel) {
23
278
  function reconcileSliders(snapshot) {
24
279
  const fiberSliders = snapshot.sliders;
25
280
  const out = [];
26
- for (const { id, kind } of chunkKZ2BLMS4_js.componentRegistry.snapshot()) {
281
+ for (const { id, kind } of chunkUZ2D4BVX_js.componentRegistry.snapshot()) {
27
282
  if (kind !== "slider") continue;
28
- const entry = chunkKZ2BLMS4_js.componentRegistry.getSlider(id);
283
+ const entry = chunkUZ2D4BVX_js.componentRegistry.getSlider(id);
29
284
  if (!entry) continue;
30
285
  const fiber = fiberSliders.find((s) => fiberSliderMatches(s, id, entry.label));
31
286
  const visible = !!fiber || typeof entry.screen === "string" && entry.screen === snapshot.route;
@@ -53,7 +308,7 @@ function normalizeFieldKey(value) {
53
308
  }
54
309
  function canonicalizeRegisteredFields(snapshot) {
55
310
  const claimed = /* @__PURE__ */ new Set();
56
- for (const { id, kind, label } of chunkKZ2BLMS4_js.componentRegistry.snapshot()) {
311
+ for (const { id, kind, label } of chunkUZ2D4BVX_js.componentRegistry.snapshot()) {
57
312
  if (kind !== "field") continue;
58
313
  if (snapshot.inputs.some((input) => input.id === id)) continue;
59
314
  const registryLabel = (label ?? "").toLowerCase();
@@ -72,7 +327,7 @@ function canonicalizeRegisteredFields(snapshot) {
72
327
  }
73
328
  }
74
329
  function mergeRegisteredLists(snapshot) {
75
- const registered = chunkKZ2BLMS4_js.listRegistry.snapshot();
330
+ const registered = chunkUZ2D4BVX_js.listRegistry.snapshot();
76
331
  if (registered.length === 0) return;
77
332
  const byId = /* @__PURE__ */ new Map();
78
333
  for (const list of snapshot.lists) {
@@ -80,20 +335,35 @@ function mergeRegisteredLists(snapshot) {
80
335
  }
81
336
  for (const entry of registered) {
82
337
  if (entry.kind === "scroll") continue;
338
+ const existing = byId.get(entry.id);
339
+ if (!existing && !(entry.screen && entry.screen === snapshot.route)) continue;
83
340
  const metrics = entry.getScrollMetrics?.();
84
341
  const scrollFacts = metrics ? {
85
342
  scrollOffsetY: Math.max(0, Math.round(metrics.offset)),
86
343
  canScrollUp: metrics.offset > 8,
87
344
  canScrollDown: metrics.offset + metrics.visibleLength < metrics.contentLength - 8
88
345
  } : void 0;
89
- const existing = byId.get(entry.id);
90
346
  if (existing) {
91
347
  existing.itemCount = existing.itemCount ?? entry.itemCount;
348
+ existing.totalItemCount = existing.totalItemCount ?? entry.totalItemCount;
92
349
  existing.refreshing = existing.refreshing ?? entry.refreshing;
93
350
  existing.empty = existing.empty ?? entry.empty;
94
351
  existing.label = existing.label ?? entry.label;
95
352
  existing.source = existing.source ?? "auto-tracked";
96
353
  existing.visibleItemCount = existing.items.length;
354
+ const viewport = entry.measureViewport?.();
355
+ if (viewport && viewport.width > 0 && viewport.height > 0) {
356
+ let measured = 0;
357
+ let inViewport = 0;
358
+ for (const item of existing.items) {
359
+ const rect = chunkUZ2D4BVX_js.rectForNode(item);
360
+ if (!rect) continue;
361
+ measured++;
362
+ item.onScreen = rect.width > 0 && rect.height > 0 && rect.x < viewport.x + viewport.width && rect.x + rect.width > viewport.x && rect.y < viewport.y + viewport.height && rect.y + rect.height > viewport.y;
363
+ if (item.onScreen) inViewport++;
364
+ }
365
+ if (measured === existing.items.length) existing.viewportItemCount = inViewport;
366
+ }
97
367
  if (entry.dataPreview && entry.dataPreview.length > 0) {
98
368
  existing.dataPreview = entry.dataPreview;
99
369
  }
@@ -110,6 +380,7 @@ function mergeRegisteredLists(snapshot) {
110
380
  containerType: entry.component,
111
381
  source: "registry",
112
382
  itemCount: entry.itemCount,
383
+ totalItemCount: entry.totalItemCount,
113
384
  visibleItemCount: 0,
114
385
  refreshing: entry.refreshing,
115
386
  empty: entry.empty,
@@ -121,7 +392,7 @@ function mergeRegisteredLists(snapshot) {
121
392
  }
122
393
  }
123
394
  function mergeRegisteredScrollables(snapshot) {
124
- const registered = chunkKZ2BLMS4_js.listRegistry.snapshot().filter((e) => e.kind === "scroll");
395
+ const registered = chunkUZ2D4BVX_js.listRegistry.snapshot().filter((e) => e.kind === "scroll");
125
396
  if (registered.length === 0) return;
126
397
  const listsById = /* @__PURE__ */ new Map();
127
398
  for (const list of snapshot.lists) {
@@ -154,7 +425,7 @@ function mergeRegisteredScrollables(snapshot) {
154
425
  if (out.length > 0) snapshot.scrollables = out;
155
426
  }
156
427
  function mergeNativeDialog(snapshot) {
157
- const dialog = chunkKZ2BLMS4_js.getOpenNativeDialog();
428
+ const dialog = chunkUZ2D4BVX_js.getOpenNativeDialog();
158
429
  if (!dialog) return;
159
430
  snapshot.nativeDialog = {
160
431
  id: dialog.id,
@@ -169,21 +440,58 @@ function mergeNativeDialog(snapshot) {
169
440
  }
170
441
  var MIN_FIBERS_FOR_BLINDNESS = 200;
171
442
  function recordBlindWalk() {
172
- const walk = chunkKZ2BLMS4_js.getLastWalkDiagnostics();
443
+ const walk = chunkUZ2D4BVX_js.getLastWalkDiagnostics();
173
444
  if (!walk) return;
174
445
  if (walk.visitedFibers < MIN_FIBERS_FOR_BLINDNESS) return;
175
- if (chunkKZ2BLMS4_js.totalDetectorHits(walk.detectors) > 0) return;
176
- chunkKZ2BLMS4_js.recordIntrospectionFailure("walk-recognized-nothing", null);
446
+ if (chunkUZ2D4BVX_js.totalDetectorHits(walk.detectors) > 0) return;
447
+ chunkUZ2D4BVX_js.recordIntrospectionFailure("walk-recognized-nothing", null);
448
+ }
449
+ function mergeRegisteredFieldValues(snapshot) {
450
+ const present = new Set(snapshot.inputs.map((i) => normalizeFieldKey(i.id)));
451
+ const onScreenIds = [
452
+ ...snapshot.buttons.map((b) => (b.id ?? "").toLowerCase()),
453
+ ...snapshot.toggles.map((t) => (t.id ?? "").toLowerCase())
454
+ ].filter(Boolean);
455
+ const onScreenLabels = new Set(
456
+ [...snapshot.buttons.map((b) => b.label), ...snapshot.texts].filter((l) => !!l).map((l) => l.toLowerCase())
457
+ );
458
+ for (const { id, kind, screen, label } of chunkUZ2D4BVX_js.componentRegistry.snapshot()) {
459
+ if (kind !== "field") continue;
460
+ const key = normalizeFieldKey(id);
461
+ if (!key || present.has(key)) continue;
462
+ const entry = chunkUZ2D4BVX_js.componentRegistry.getField(id);
463
+ if (!entry) continue;
464
+ const fieldType = entry.fieldType ?? "";
465
+ if (fieldType === "text" || fieldType === "") continue;
466
+ const declaresRoute = typeof screen === "string" && screen === snapshot.route;
467
+ if (screen && !declaresRoute && !chunkUZ2D4BVX_js.getActiveRouteNames().includes(screen)) continue;
468
+ const idEvidence = onScreenIds.some((candidate) => candidate.split(/[^a-z0-9]+/).includes(key));
469
+ const labelEvidence = !!entry.label && onScreenLabels.has(entry.label.toLowerCase());
470
+ if (!declaresRoute && !idEvidence && !labelEvidence) continue;
471
+ let value;
472
+ try {
473
+ value = String(entry.getValue() ?? "");
474
+ } catch {
475
+ continue;
476
+ }
477
+ snapshot.inputs.push({
478
+ id,
479
+ label: entry.label ?? label,
480
+ value,
481
+ type: fieldType
482
+ });
483
+ present.add(key);
484
+ }
177
485
  }
178
486
  function captureSnapshot() {
179
- const root = chunkKZ2BLMS4_js.getFiberRoot();
487
+ const root = chunkUZ2D4BVX_js.getFiberRoot();
180
488
  if (!root) {
181
- chunkKZ2BLMS4_js.appilotsDebugWarn(
489
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
182
490
  "captureSnapshot: fiber root not captured yet. Ensure <AppilotsProvider> has rendered before calling this."
183
491
  );
184
- chunkKZ2BLMS4_js.recordIntrospectionFailure("never-mounted", null);
492
+ chunkUZ2D4BVX_js.recordIntrospectionFailure("never-mounted", null);
185
493
  return {
186
- route: chunkKZ2BLMS4_js.getCurrentScreen(),
494
+ route: chunkUZ2D4BVX_js.getCurrentScreen(),
187
495
  texts: [],
188
496
  inputs: [],
189
497
  buttons: [],
@@ -197,9 +505,9 @@ function captureSnapshot() {
197
505
  };
198
506
  }
199
507
  const start = Date.now();
200
- const snapshot = chunkKZ2BLMS4_js.walkFiber(root);
508
+ const snapshot = chunkUZ2D4BVX_js.walkFiber(root);
201
509
  recordBlindWalk();
202
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
510
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
203
511
  const deepestRoute = activePath[activePath.length - 1];
204
512
  if (deepestRoute) snapshot.route = deepestRoute;
205
513
  mergeRegisteredLists(snapshot);
@@ -207,15 +515,16 @@ function captureSnapshot() {
207
515
  mergeNativeDialog(snapshot);
208
516
  reconcileSliders(snapshot);
209
517
  canonicalizeRegisteredFields(snapshot);
210
- chunkKZ2BLMS4_js.clampSnapshotToWireLimits(snapshot);
211
- const entries = chunkKZ2BLMS4_js.deriveInteractionElements(snapshot, { rectOf: chunkKZ2BLMS4_js.rectForNode });
518
+ mergeRegisteredFieldValues(snapshot);
519
+ chunkUZ2D4BVX_js.clampSnapshotToWireLimits(snapshot);
520
+ const entries = chunkUZ2D4BVX_js.deriveInteractionElements(snapshot, { rectOf: chunkUZ2D4BVX_js.rectForNode });
212
521
  const rectById = new Map(
213
522
  entries.flatMap((entry) => entry.rect ? [[entry.id, entry.rect]] : [])
214
523
  );
215
- snapshot.elements = entries.map(chunkKZ2BLMS4_js.toWireElement);
216
- snapshot.choiceGroups = chunkKZ2BLMS4_js.attachElementIdsToChoiceGroups(snapshot.choiceGroups, snapshot.elements);
217
- chunkKZ2BLMS4_js.clampSnapshotToWireLimits(snapshot);
218
- chunkKZ2BLMS4_js.elementRegistry.replaceAll(
524
+ snapshot.elements = entries.map(chunkUZ2D4BVX_js.toWireElement);
525
+ snapshot.choiceGroups = chunkUZ2D4BVX_js.attachElementIdsToChoiceGroups(snapshot.choiceGroups, snapshot.elements);
526
+ chunkUZ2D4BVX_js.clampSnapshotToWireLimits(snapshot);
527
+ chunkUZ2D4BVX_js.elementRegistry.replaceAll(
219
528
  snapshot.elements.map((element) => {
220
529
  const rect = rectById.get(element.id);
221
530
  return rect ? { ...element, rect } : element;
@@ -227,10 +536,10 @@ function captureSnapshot() {
227
536
  (acc, l) => acc + (typeof l.itemCount === "number" ? l.itemCount : 0),
228
537
  0
229
538
  );
230
- chunkKZ2BLMS4_js.appilotsDebugLog(
539
+ chunkUZ2D4BVX_js.appilotsDebugLog(
231
540
  `captureSnapshot: route="${snapshot.route}" texts=${snapshot.texts.length} inputs=${snapshot.inputs.length} buttons=${snapshot.buttons.length} toggles=${snapshot.toggles.length} sliders=${snapshot.sliders.length} loading=${snapshot.loading} modal=${snapshot.modalOpen} lists=${snapshot.lists.length}(visibleItems=${totalListItems}, dataItems=${totalListDataItems}) choices=${snapshot.choiceGroups.length} elements=${snapshot.elements.length} ` + (snapshot.truncated ? "truncated=true " : "") + `(walked ${snapshot.stats?.visitedFibers ?? 0} fibers, skipped ${snapshot.stats?.skippedHidden ?? 0} hidden, ${elapsed}ms)`
232
541
  );
233
- chunkKZ2BLMS4_js.appilotsDebugLog(
542
+ chunkUZ2D4BVX_js.appilotsDebugLog(
234
543
  `captureSnapshot detail: lists=[${snapshot.lists.map((l) => `${l.containerType}:${l.id ?? l.label ?? "?"}(rows=${l.items.length})`).join(", ")}] inputs=[${snapshot.inputs.map((i) => i.id ?? i.label ?? "?").slice(0, 12).join(", ")}] buttons=[${snapshot.buttons.map((b) => b.id ?? b.label ?? "?").slice(0, 15).join(", ")}]`
235
544
  );
236
545
  return snapshot;
@@ -241,10 +550,10 @@ function registryCounts() {
241
550
  toggles: 0,
242
551
  sliders: 0,
243
552
  targets: 0,
244
- lists: chunkKZ2BLMS4_js.listRegistry.snapshot().length,
245
- elements: chunkKZ2BLMS4_js.elementRegistry.size
553
+ lists: chunkUZ2D4BVX_js.listRegistry.snapshot().length,
554
+ elements: chunkUZ2D4BVX_js.elementRegistry.size
246
555
  };
247
- for (const { kind } of chunkKZ2BLMS4_js.componentRegistry.snapshot()) {
556
+ for (const { kind } of chunkUZ2D4BVX_js.componentRegistry.snapshot()) {
248
557
  if (kind === "field") counts.fields++;
249
558
  else if (kind === "toggle") counts.toggles++;
250
559
  else if (kind === "slider") counts.sliders++;
@@ -253,26 +562,26 @@ function registryCounts() {
253
562
  return counts;
254
563
  }
255
564
  function appilotsSelfCheck() {
256
- const root = chunkKZ2BLMS4_js.getFiberRoot();
565
+ const root = chunkUZ2D4BVX_js.getFiberRoot();
257
566
  const start = Date.now();
258
- if (root) chunkKZ2BLMS4_js.walkFiber(root);
567
+ if (root) chunkUZ2D4BVX_js.walkFiber(root);
259
568
  const elapsedMs = Date.now() - start;
260
- const walk = chunkKZ2BLMS4_js.getLastWalkDiagnostics();
569
+ const walk = chunkUZ2D4BVX_js.getLastWalkDiagnostics();
261
570
  return {
262
- sdkVersion: chunkKZ2BLMS4_js.SDK_VERSION,
571
+ sdkVersion: chunkUZ2D4BVX_js.SDK_VERSION,
263
572
  reactVersion: React__default.default.version ?? null,
264
- route: chunkKZ2BLMS4_js.getCurrentScreen() || null,
265
- introspection: chunkKZ2BLMS4_js.getIntrospectionDiagnostics(),
266
- autoTracking: chunkKZ2BLMS4_js.getAutoTrackingState(),
573
+ route: chunkUZ2D4BVX_js.getCurrentScreen() || null,
574
+ introspection: chunkUZ2D4BVX_js.getIntrospectionDiagnostics(),
575
+ autoTracking: chunkUZ2D4BVX_js.getAutoTrackingState(),
267
576
  walk: {
268
577
  visitedFibers: root ? walk?.visitedFibers ?? 0 : 0,
269
578
  skippedHidden: root ? walk?.skippedHidden ?? 0 : 0,
270
579
  elapsedMs
271
580
  },
272
- detectors: root ? walk?.detectors ?? chunkKZ2BLMS4_js.emptyDetectorCounts() : chunkKZ2BLMS4_js.emptyDetectorCounts(),
581
+ detectors: root ? walk?.detectors ?? chunkUZ2D4BVX_js.emptyDetectorCounts() : chunkUZ2D4BVX_js.emptyDetectorCounts(),
273
582
  registry: registryCounts(),
274
- nameMangling: chunkKZ2BLMS4_js.getNameManglingProbe(),
275
- geometry: { availability: chunkKZ2BLMS4_js.geometryAvailability(), ...chunkKZ2BLMS4_js.geometryStats() }
583
+ nameMangling: chunkUZ2D4BVX_js.getNameManglingProbe(),
584
+ geometry: { availability: chunkUZ2D4BVX_js.geometryAvailability(), ...chunkUZ2D4BVX_js.geometryStats() }
276
585
  };
277
586
  }
278
587
 
@@ -285,7 +594,7 @@ function declaredState(props) {
285
594
  return typeof checked === "boolean" ? checked : void 0;
286
595
  }
287
596
  function isReactNativeSwitch(fiber) {
288
- return chunkKZ2BLMS4_js.getTypeName(fiber?.type) === "Switch" || chunkKZ2BLMS4_js.getTypeName(fiber?.elementType) === "Switch";
597
+ return chunkUZ2D4BVX_js.getTypeName(fiber?.type) === "Switch" || chunkUZ2D4BVX_js.getTypeName(fiber?.elementType) === "Switch";
289
598
  }
290
599
  var SWITCH_FLIP_HANDLERS = ["onValueChange", "onChange", "onPress"];
291
600
  function flipSwitchFiber(fiber, next) {
@@ -309,6 +618,11 @@ function flipSwitchFiber(fiber, next) {
309
618
  return { ok: true, via: handler.via };
310
619
  }
311
620
 
621
+ // src/introspection/positionalIdentity.ts
622
+ function isPositionalControlId(value) {
623
+ return /^(?:list-\d+-item-\d+-)?(?:btn|input|slider|toggle)-\d+$/i.test(value.trim());
624
+ }
625
+
312
626
  // src/introspection/findInteractive.ts
313
627
  function getTypeName2(type) {
314
628
  if (!type) return null;
@@ -383,7 +697,7 @@ function shouldSkipSubtree2(fiber) {
383
697
  const typeName = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
384
698
  if (typeName && SELF_REFERENTIAL_COMPONENTS.has(typeName)) return true;
385
699
  if (isAppilotsSkipMarked(props)) return true;
386
- if (chunkKZ2BLMS4_js.isModalFiber(fiber) && props.visible === false) return true;
700
+ if (chunkUZ2D4BVX_js.isModalFiber(fiber) && props.visible === false) return true;
387
701
  if (matchesName(fiber, "Screen") && props.activityState === 0) return true;
388
702
  const style = props.style;
389
703
  if (style) {
@@ -393,9 +707,14 @@ function shouldSkipSubtree2(fiber) {
393
707
  }
394
708
  return false;
395
709
  }
396
- var normalize = chunkKZ2BLMS4_js.foldForCompare;
710
+ var normalize = chunkUZ2D4BVX_js.foldForCompare;
397
711
  function scoreMatchDetail(fiber, queryNorm, queryRaw) {
398
712
  const props = fiber.memoizedProps ?? {};
713
+ if (chunkUZ2D4BVX_js.isSourceControlId(queryRaw)) {
714
+ const matches = chunkUZ2D4BVX_js.sourceControlId(fiber) === queryRaw;
715
+ const disabled = props.disabled === true || props.accessibilityState?.disabled === true;
716
+ return { score: matches ? disabled ? 60 : 120 : 0, named: matches, disabled };
717
+ }
399
718
  const candidates = [];
400
719
  if (typeof props.testID === "string") candidates.push({ val: props.testID, weight: 100 });
401
720
  if (typeof props.accessibilityLabel === "string")
@@ -414,11 +733,11 @@ function scoreMatchDetail(fiber, queryNorm, queryRaw) {
414
733
  if (!vNorm) continue;
415
734
  if (val === queryRaw) best = Math.max(best, weight + 20);
416
735
  else if (vNorm === queryNorm) best = Math.max(best, weight + 10);
417
- else if (queryNorm && (vNorm.includes(queryNorm) || queryNorm.includes(vNorm)))
736
+ else if (!isPositionalControlId(queryRaw) && queryNorm && (vNorm.includes(queryNorm) || queryNorm.includes(vNorm)))
418
737
  best = Math.max(best, weight - 20);
419
738
  }
420
739
  const named = best >= IDENTITY_MATCH_SCORE;
421
- const disabledProp = props.disabled === true;
740
+ const disabledProp = props.disabled === true || props.editable === false || props.readOnly === true;
422
741
  const disabledA11y = typeof props.accessibilityState === "object" && props.accessibilityState !== null && props.accessibilityState.disabled === true;
423
742
  if (best > 0 && disabledProp) {
424
743
  best = Math.max(1, best - 60);
@@ -428,12 +747,18 @@ function scoreMatchDetail(fiber, queryNorm, queryRaw) {
428
747
  }
429
748
  return { score: best, named, disabled: disabledProp || disabledA11y };
430
749
  }
750
+ var inputIdentities = /* @__PURE__ */ new WeakMap();
431
751
  function wrapTextInput(fiber) {
752
+ const identity = inputIdentities.get(fiber) ?? (fiber.alternate && inputIdentities.get(fiber.alternate)) ?? {};
753
+ inputIdentities.set(fiber, identity);
754
+ if (fiber.alternate) inputIdentities.set(fiber.alternate, identity);
432
755
  const getProps = () => fiber.memoizedProps ?? {};
433
756
  return {
434
757
  kind: "field",
758
+ controlIdentity: identity,
435
759
  label: getProps().accessibilityLabel ?? getProps().label ?? getProps().placeholder ?? getProps().testID,
436
760
  getValue: () => getProps().value,
761
+ finishEditing: () => chunkUZ2D4BVX_js.endTextInputEditing(getProps()),
437
762
  setValue: (value) => {
438
763
  const { onChangeText, onChange } = getProps();
439
764
  if (typeof onChangeText === "function") {
@@ -468,7 +793,7 @@ function wrapSwitch(fiber) {
468
793
  // `accessibilityRole="switch"`) has only the former, and reporting
469
794
  // `false` for a control that is on is worse than reporting nothing:
470
795
  // the caller inverts this to decide where to move.
471
- getValue: () => chunkKZ2BLMS4_js.switchValue(getProps()),
796
+ getValue: () => chunkUZ2D4BVX_js.switchValue(getProps()),
472
797
  setValue: (value) => {
473
798
  const boolVal = typeof value === "boolean" ? value : value === "true" || value === true;
474
799
  const flipped = flipSwitchFiber(fiber, boolVal);
@@ -514,18 +839,18 @@ function describeCandidate(fiber, kind) {
514
839
  const props = fiber?.memoizedProps ?? {};
515
840
  return props.testID ?? props.accessibilityLabel ?? props.placeholder ?? (kind === "target" ? findChildText(fiber) : void 0);
516
841
  }
517
- function isDescendantOf(fiber, ancestor) {
842
+ function isDescendantOf(fiber, ancestor, parents) {
518
843
  if (!fiber || !ancestor) return false;
519
- let node = fiber.return;
520
- for (let depth = 0; node && depth < 200; depth++, node = node.return) {
844
+ let node = parents.get(fiber);
845
+ for (let depth = 0; node && depth < 200; depth++, node = parents.get(node)) {
521
846
  if (node === ancestor) return true;
522
847
  }
523
848
  return false;
524
849
  }
525
850
  function findInteractiveByIdentifier(query, options = {}) {
526
- const root = chunkKZ2BLMS4_js.getFiberRoot();
851
+ const root = chunkUZ2D4BVX_js.getFiberRoot();
527
852
  if (!root) {
528
- chunkKZ2BLMS4_js.appilotsDebugWarn("findInteractiveByIdentifier: fiber root not captured yet");
853
+ chunkUZ2D4BVX_js.appilotsDebugWarn("findInteractiveByIdentifier: fiber root not captured yet");
529
854
  return null;
530
855
  }
531
856
  const queryNorm = normalize(query);
@@ -538,18 +863,20 @@ function findInteractiveByIdentifier(query, options = {}) {
538
863
  let bestDisabled = false;
539
864
  let tied = [];
540
865
  const visited = /* @__PURE__ */ new WeakSet();
866
+ const parents = /* @__PURE__ */ new WeakMap();
541
867
  const stack = [];
542
- if (root.child) stack.push(root.child);
868
+ if (root.child) stack.push({ fiber: root.child, parent: root });
543
869
  while (stack.length > 0) {
544
- const fiber = stack.pop();
870
+ const { fiber, parent } = stack.pop();
545
871
  if (!fiber || visited.has(fiber)) continue;
546
872
  visited.add(fiber);
547
- if (fiber.sibling) stack.push(fiber.sibling);
873
+ parents.set(fiber, parent);
874
+ if (fiber.sibling) stack.push({ fiber: fiber.sibling, parent });
548
875
  if (shouldSkipSubtree2(fiber)) continue;
549
876
  const props = fiber.memoizedProps ?? {};
550
877
  let kind = null;
551
878
  if (matchesName(fiber, "TextInput")) kind = "field";
552
- else if (chunkKZ2BLMS4_js.isSwitchLike(fiber, props)) kind = "toggle";
879
+ else if (chunkUZ2D4BVX_js.isSwitchLike(fiber, props)) kind = "toggle";
553
880
  else if (isPressableTarget(fiber)) kind = "target";
554
881
  if (kind && (!options.kind || options.kind === kind)) {
555
882
  const detail = scoreMatchDetail(fiber, queryNorm, queryRaw);
@@ -558,7 +885,7 @@ function findInteractiveByIdentifier(query, options = {}) {
558
885
  if (score > bestScore) tied = [{ fiber, kind }];
559
886
  else if (score === bestScore) tied.push({ fiber, kind });
560
887
  }
561
- const beatsBest = score > bestScore || score > 0 && score === bestScore && isDescendantOf(fiber, bestFiber);
888
+ const beatsBest = score > bestScore || score > 0 && score === bestScore && isDescendantOf(fiber, bestFiber, parents);
562
889
  if (beatsBest) {
563
890
  bestScore = score;
564
891
  bestFiber = fiber;
@@ -567,14 +894,18 @@ function findInteractiveByIdentifier(query, options = {}) {
567
894
  bestId = describeCandidate(fiber, kind);
568
895
  if (kind === "field") bestHandle = wrapTextInput(fiber);
569
896
  else if (kind === "toggle") bestHandle = wrapSwitch(fiber);
570
- else bestHandle = wrapTouchable(fiber, findChildText(fiber));
897
+ else if (chunkUZ2D4BVX_js.isSourceControlId(queryRaw)) {
898
+ const bound = chunkUZ2D4BVX_js.sourceControlTarget(fiber);
899
+ if (!bound || bound.disabled) bestHandle = null;
900
+ else bestHandle = wrapTouchable(bound.fiber, findChildText(fiber));
901
+ } else bestHandle = wrapTouchable(fiber, findChildText(fiber));
571
902
  }
572
903
  }
573
904
  if (kind === "toggle") continue;
574
- if (fiber.child) stack.push(fiber.child);
905
+ if (fiber.child) stack.push({ fiber: fiber.child, parent: fiber });
575
906
  }
576
907
  if (bestHandle && bestScore < (options.minScore ?? 0)) {
577
- chunkKZ2BLMS4_js.appilotsDebugLog(
908
+ chunkUZ2D4BVX_js.appilotsDebugLog(
578
909
  `findInteractiveByIdentifier: query="${query}" \u2192 best match scored ${bestScore}, below the requested floor of ${options.minScore} \u2014 refusing it`
579
910
  );
580
911
  return null;
@@ -582,23 +913,138 @@ function findInteractiveByIdentifier(query, options = {}) {
582
913
  if (bestHandle) {
583
914
  const independent = tied.filter(
584
915
  (candidate) => !tied.some(
585
- (other) => other.fiber !== candidate.fiber && isDescendantOf(other.fiber, candidate.fiber)
916
+ (other) => other.fiber !== candidate.fiber && isDescendantOf(other.fiber, candidate.fiber, parents)
586
917
  )
587
918
  );
588
919
  bestHandle.matchCount = independent.length;
589
920
  bestHandle.named = bestNamed;
590
921
  bestHandle.disabled = bestDisabled;
922
+ const measured = bestFiber;
923
+ bestHandle.measure = () => chunkUZ2D4BVX_js.rectOfFiber(measured);
591
924
  const candidates = independent.map(({ fiber, kind }) => describeCandidate(fiber, kind)).filter((id) => typeof id === "string" && id.length > 0).slice(0, 8);
592
925
  if (candidates.length > 0) bestHandle.matchCandidates = candidates;
593
- chunkKZ2BLMS4_js.appilotsDebugLog(
926
+ chunkUZ2D4BVX_js.appilotsDebugLog(
594
927
  `findInteractiveByIdentifier: query="${query}" \u2192 kind=${bestHandle.kind} label="${bestHandle.label ?? bestId}" score=${bestScore} named=${bestNamed} disabled=${bestDisabled} tied=${independent.length}`
595
928
  );
596
929
  } else {
597
- chunkKZ2BLMS4_js.appilotsDebugLog(`findInteractiveByIdentifier: query="${query}" \u2192 no match`);
930
+ chunkUZ2D4BVX_js.appilotsDebugLog(`findInteractiveByIdentifier: query="${query}" \u2192 no match`);
598
931
  }
599
932
  return bestHandle;
600
933
  }
601
934
 
935
+ // src/introspection/spotlightTarget.ts
936
+ var listeners = /* @__PURE__ */ new Set();
937
+ var current = null;
938
+ var seq = 0;
939
+ var expiry = null;
940
+ var SPOTLIGHT_TTL_MS = 1200;
941
+ var mode = "off";
942
+ function setSpotlightMode(next) {
943
+ mode = next;
944
+ }
945
+ function getSpotlightMode() {
946
+ return mode;
947
+ }
948
+ function emit() {
949
+ for (const listener of listeners) listener(current);
950
+ }
951
+ function setSpotlightTarget(rect, label) {
952
+ if (expiry) {
953
+ clearTimeout(expiry);
954
+ expiry = null;
955
+ }
956
+ if (!rect) {
957
+ chunkUZ2D4BVX_js.appilotsDebugLog(
958
+ `setSpotlightTarget: cleared${label ? ` (was about to mark "${label}")` : ""}`
959
+ );
960
+ if (current === null) return;
961
+ current = null;
962
+ emit();
963
+ return;
964
+ }
965
+ current = { rect, label, seq: ++seq };
966
+ chunkUZ2D4BVX_js.appilotsDebugLog(
967
+ `setSpotlightTarget: seq=${current.seq} label="${label ?? ""}" rect=${Math.round(rect.x)},${Math.round(rect.y)} ${Math.round(rect.width)}x${Math.round(rect.height)}`
968
+ );
969
+ emit();
970
+ expiry = setTimeout(() => {
971
+ expiry = null;
972
+ current = null;
973
+ emit();
974
+ }, SPOTLIGHT_TTL_MS);
975
+ }
976
+ function getSpotlightTarget() {
977
+ return current;
978
+ }
979
+ function subscribeSpotlight(listener) {
980
+ listeners.add(listener);
981
+ return () => {
982
+ listeners.delete(listener);
983
+ };
984
+ }
985
+
986
+ // src/introspection/scrollIntoView.ts
987
+ var REVEAL_MARGIN = 24;
988
+ function offsetToReveal({ rect, viewport, metrics }) {
989
+ const maxOffset = Math.max(0, metrics.contentLength - metrics.visibleLength);
990
+ const viewTop = viewport.top + REVEAL_MARGIN;
991
+ const viewBottom = viewport.top + viewport.height - REVEAL_MARGIN;
992
+ const tallerThanViewport = rect.height > viewport.height - REVEAL_MARGIN * 2;
993
+ let delta;
994
+ if (tallerThanViewport) {
995
+ if (rect.y >= viewTop && rect.y <= viewBottom) return null;
996
+ delta = rect.y - viewTop;
997
+ } else if (rect.y < viewTop) {
998
+ delta = rect.y - viewTop;
999
+ } else if (rect.y + rect.height > viewBottom) {
1000
+ delta = rect.y + rect.height - viewBottom;
1001
+ } else {
1002
+ return null;
1003
+ }
1004
+ const target = clamp(metrics.offset + delta, 0, maxOffset);
1005
+ if (Math.abs(target - metrics.offset) < 1) return null;
1006
+ return target;
1007
+ }
1008
+ function clamp(value, min, max) {
1009
+ return Math.min(max, Math.max(min, value));
1010
+ }
1011
+
1012
+ // src/introspection/revealTarget.ts
1013
+ function revealTarget(rect) {
1014
+ const host = chunkUZ2D4BVX_js.getGeometryHost();
1015
+ if (!host) return { rect, scrolled: false };
1016
+ if (chunkUZ2D4BVX_js.isRectOnScreen(rect, host.window)) return { rect, scrolled: false };
1017
+ const scrollable = resolveScrollable();
1018
+ if (!scrollable) return { rect, scrolled: false };
1019
+ const metrics = scrollable.getScrollMetrics?.();
1020
+ if (!metrics) return { rect, scrolled: false };
1021
+ const box = scrollable.measureViewport?.();
1022
+ const viewport = box ? { top: box.y, height: box.height } : { top: 0, height: host.window.height };
1023
+ const target = offsetToReveal({ rect, viewport, metrics });
1024
+ if (target === null) return { rect, scrolled: false };
1025
+ const moved = scrollable.scrollToOffset?.(target) ?? false;
1026
+ if (!moved) {
1027
+ chunkUZ2D4BVX_js.appilotsDebugLog(`revealTarget: "${scrollable.id}" refused scrollToOffset(${target})`);
1028
+ return { rect, scrolled: false };
1029
+ }
1030
+ const delta = target - metrics.offset;
1031
+ chunkUZ2D4BVX_js.appilotsDebugLog(
1032
+ `revealTarget: scrolled "${scrollable.id}" by ${Math.round(delta)}px (${Math.round(metrics.offset)} \u2192 ${Math.round(target)})`
1033
+ );
1034
+ return { rect: { ...rect, y: rect.y - delta }, scrolled: true };
1035
+ }
1036
+ function resolveScrollable() {
1037
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen();
1038
+ const candidates = chunkUZ2D4BVX_js.listRegistry.snapshot().filter((entry) => entry.scrollToOffset && entry.getScrollMetrics).filter((entry) => !screen || !entry.screen || entry.screen === screen);
1039
+ if (candidates.length === 1) return candidates[0];
1040
+ if (candidates.length > 1) {
1041
+ chunkUZ2D4BVX_js.appilotsDebugLog(
1042
+ `revealTarget: ${candidates.length} scrollables on "${screen ?? "?"}" \u2014 cannot tell which holds the target, so not scrolling.`
1043
+ );
1044
+ }
1045
+ return null;
1046
+ }
1047
+
602
1048
  // src/executor/handlers/navigateHandler.ts
603
1049
  var _reportedMissingNavRef = false;
604
1050
  function reportMissingNavigationRef() {
@@ -641,43 +1087,52 @@ function screenExistsAnywhere(state, screenName) {
641
1087
  return false;
642
1088
  }
643
1089
  function safeNavigate(nav, screenName, params, path) {
1090
+ const state = nav.getState?.();
1091
+ const rootState = nav.getRootState?.() ?? state;
644
1092
  if (path && path.length > 0) {
1093
+ if (Array.isArray(rootState?.routeNames) && !screenExistsAnywhere(rootState, path[0]) && !state?.routeNames?.includes(path[0])) {
1094
+ if (state?.routeNames?.includes(screenName)) {
1095
+ nav.navigate(screenName, params);
1096
+ return;
1097
+ }
1098
+ throw new Error(
1099
+ `Route "${path[0]}" is not registered in any navigator. Cannot resolve the supplied path to "${screenName}".`
1100
+ );
1101
+ }
645
1102
  const { root, nestedParams } = buildNestedParams(path, screenName, params);
646
- chunkKZ2BLMS4_js.appilotsDebugLog(
1103
+ chunkUZ2D4BVX_js.appilotsDebugLog(
647
1104
  `safeNavigate: using path-based navigation \u2014 root="${root}", nested=${JSON.stringify(nestedParams)}`
648
1105
  );
649
1106
  nav.navigate(root, nestedParams);
650
1107
  return;
651
1108
  }
652
- const state = nav.getState?.();
653
1109
  if (state?.routeNames?.includes(screenName)) {
654
1110
  nav.navigate(screenName, params);
655
1111
  return;
656
1112
  }
657
- const rootState = nav.getRootState?.() ?? state;
658
1113
  if (!screenExistsAnywhere(rootState, screenName)) {
659
1114
  throw new Error(
660
1115
  `Screen "${screenName}" is not registered in any navigator. The screen may have been removed, may live inside a flow that is not currently mounted (e.g. Auth stack after login), or the MCP doc is stale.`
661
1116
  );
662
1117
  }
663
- chunkKZ2BLMS4_js.appilotsDebugLog(
1118
+ chunkUZ2D4BVX_js.appilotsDebugLog(
664
1119
  `safeNavigate: screen exists in nested navigator, attempting direct navigation to "${screenName}"`
665
1120
  );
666
1121
  nav.navigate(screenName, params);
667
1122
  }
668
1123
  function navigateHandler(payload, context) {
669
- chunkKZ2BLMS4_js.appilotsDebugLog(
1124
+ chunkUZ2D4BVX_js.appilotsDebugLog(
670
1125
  // Params carry record ids and, on some screens, the user's own
671
1126
  // data — the count answers "were params passed?" and nothing else.
672
- `navigateHandler called: screenName="${payload.screenName}" action="${payload.navigationAction}" params=${chunkKZ2BLMS4_js.describeValue(payload.params)}`
1127
+ `navigateHandler called: screenName="${payload.screenName}" action="${payload.navigationAction}" params=${chunkUZ2D4BVX_js.describeValue(payload.params)}`
673
1128
  );
674
1129
  const { navigationRef, permissions } = context;
675
1130
  const nav = navigationRef.current;
676
1131
  if (!permissions.canNavigate) {
677
1132
  return {
678
1133
  success: false,
679
- error: "Navigation is not permitted",
680
- diagnose: { category: "unknown", screen: payload.screenName }
1134
+ error: chunkUZ2D4BVX_js.permissionDeniedMessage("Navigation", "canNavigate"),
1135
+ diagnose: { category: "unknown", screen: payload.screenName, recoverable: false }
681
1136
  };
682
1137
  }
683
1138
  const isGoBack = payload.navigationAction === "goBack";
@@ -762,12 +1217,12 @@ function navigateHandler(payload, context) {
762
1217
  diagnose: { category: "unknown", screen: payload.screenName }
763
1218
  };
764
1219
  }
765
- chunkKZ2BLMS4_js.appilotsDebugLog(
1220
+ chunkUZ2D4BVX_js.appilotsDebugLog(
766
1221
  `navigateHandler: navigation succeeded for "${payload.screenName}" (action="${payload.navigationAction}")`
767
1222
  );
768
1223
  return { success: true };
769
1224
  } catch (err) {
770
- chunkKZ2BLMS4_js.appilotsDebugWarn(
1225
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
771
1226
  `navigateHandler: navigation failed for "${payload.screenName}": ${err?.message}`
772
1227
  );
773
1228
  const msg = String(err?.message ?? "Navigation failed");
@@ -815,18 +1270,18 @@ function markedItemIndexFromFiber(fiber) {
815
1270
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
816
1271
  }
817
1272
  function normalize2(value) {
818
- return chunkKZ2BLMS4_js.foldForCompare(String(value ?? ""));
1273
+ return chunkUZ2D4BVX_js.foldForCompare(String(value ?? ""));
819
1274
  }
820
1275
  function hasPressHandler2(fiber) {
821
1276
  const props = fiber?.memoizedProps ?? {};
822
1277
  return typeof props.onPress === "function" || typeof props.onPressIn === "function";
823
1278
  }
824
1279
  function isPressableFiber(fiber) {
825
- const typeName = chunkKZ2BLMS4_js.getTypeName(fiber.type) ?? chunkKZ2BLMS4_js.getTypeName(fiber.elementType) ?? "";
826
- return chunkKZ2BLMS4_js.TOUCHABLE_NAMES.has(typeName) || hasPressHandler2(fiber);
1280
+ const typeName = chunkUZ2D4BVX_js.getTypeName(fiber.type) ?? chunkUZ2D4BVX_js.getTypeName(fiber.elementType) ?? "";
1281
+ return chunkUZ2D4BVX_js.TOUCHABLE_NAMES.has(typeName) || hasPressHandler2(fiber);
827
1282
  }
828
1283
  function findListContainers(options = {}) {
829
- const root = chunkKZ2BLMS4_js.getFiberRoot();
1284
+ const root = chunkUZ2D4BVX_js.getFiberRoot();
830
1285
  if (!root) return [];
831
1286
  const containers = [];
832
1287
  const visited = /* @__PURE__ */ new WeakSet();
@@ -837,17 +1292,17 @@ function findListContainers(options = {}) {
837
1292
  if (!fiber || visited.has(fiber)) continue;
838
1293
  visited.add(fiber);
839
1294
  if (fiber.sibling) stack.push(fiber.sibling);
840
- if (chunkKZ2BLMS4_js.shouldSkipSubtree(fiber)) continue;
841
- if (chunkKZ2BLMS4_js.isAlwaysListContainer(fiber) || chunkKZ2BLMS4_js.isHeuristicListContainer(fiber)) {
842
- const minItems = chunkKZ2BLMS4_js.isAlwaysListContainer(fiber) || options.allowSingleHeuristic ? 1 : 2;
843
- const items = chunkKZ2BLMS4_js.findListItems(fiber, minItems);
1295
+ if (chunkUZ2D4BVX_js.shouldSkipSubtree(fiber)) continue;
1296
+ if (chunkUZ2D4BVX_js.isAlwaysListContainer(fiber) || chunkUZ2D4BVX_js.isHeuristicListContainer(fiber)) {
1297
+ const minItems = chunkUZ2D4BVX_js.isAlwaysListContainer(fiber) || options.allowSingleHeuristic ? 1 : 2;
1298
+ const items = chunkUZ2D4BVX_js.findListItems(fiber, minItems);
844
1299
  if (items && items.length >= minItems) {
845
1300
  containers.push({
846
1301
  container: fiber,
847
1302
  items,
848
1303
  listId: listIdFromContainer(fiber),
849
1304
  listLabel: listLabelFromContainer(fiber),
850
- containerType: chunkKZ2BLMS4_js.getTypeName(fiber.type) ?? chunkKZ2BLMS4_js.getTypeName(fiber.elementType) ?? "List"
1305
+ containerType: chunkUZ2D4BVX_js.getTypeName(fiber.type) ?? chunkUZ2D4BVX_js.getTypeName(fiber.elementType) ?? "List"
851
1306
  });
852
1307
  continue;
853
1308
  }
@@ -866,7 +1321,7 @@ function findFirstTouchable(item) {
866
1321
  const { fiber, includeSibling } = stack.pop();
867
1322
  if (!fiber || visited.has(fiber)) continue;
868
1323
  visited.add(fiber);
869
- if (chunkKZ2BLMS4_js.shouldSkipSubtree(fiber)) continue;
1324
+ if (chunkUZ2D4BVX_js.shouldSkipSubtree(fiber)) continue;
870
1325
  if (isPressableFiber(fiber)) {
871
1326
  const props = fiber.memoizedProps ?? {};
872
1327
  if (typeof props.onPress === "function" || typeof props.onPressIn === "function") {
@@ -893,7 +1348,7 @@ function findTouchableFibers(item) {
893
1348
  const { fiber, includeSibling } = stack.pop();
894
1349
  if (!fiber || visited.has(fiber)) continue;
895
1350
  visited.add(fiber);
896
- if (chunkKZ2BLMS4_js.shouldSkipSubtree(fiber)) continue;
1351
+ if (chunkUZ2D4BVX_js.shouldSkipSubtree(fiber)) continue;
897
1352
  const props = fiber.memoizedProps ?? {};
898
1353
  if (isPressableFiber(fiber) && (typeof props.onPress === "function" || typeof props.onPressIn === "function")) {
899
1354
  found.push(fiber);
@@ -937,7 +1392,7 @@ function findFirstText(fiber) {
937
1392
  const node = stack.pop();
938
1393
  if (!node || visited.has(node)) continue;
939
1394
  visited.add(node);
940
- const typeName = chunkKZ2BLMS4_js.getTypeName(node.type) ?? chunkKZ2BLMS4_js.getTypeName(node.elementType) ?? "";
1395
+ const typeName = chunkUZ2D4BVX_js.getTypeName(node.type) ?? chunkUZ2D4BVX_js.getTypeName(node.elementType) ?? "";
941
1396
  if (typeName === "Text") {
942
1397
  const text = textFromChildren(node.memoizedProps?.children).trim();
943
1398
  if (text) return text;
@@ -956,7 +1411,7 @@ function collectTexts(fiber) {
956
1411
  const node = stack.pop();
957
1412
  if (!node || visited.has(node)) continue;
958
1413
  visited.add(node);
959
- const typeName = chunkKZ2BLMS4_js.getTypeName(node.type) ?? chunkKZ2BLMS4_js.getTypeName(node.elementType) ?? "";
1414
+ const typeName = chunkUZ2D4BVX_js.getTypeName(node.type) ?? chunkUZ2D4BVX_js.getTypeName(node.elementType) ?? "";
960
1415
  if (typeName === "Text") {
961
1416
  const text = textFromChildren(node.memoizedProps?.children).trim();
962
1417
  if (text) out.push(text);
@@ -968,10 +1423,10 @@ function collectTexts(fiber) {
968
1423
  }
969
1424
  function hasUsefulTouchableIdentity(fiber) {
970
1425
  const props = fiber?.memoizedProps ?? {};
971
- if (chunkKZ2BLMS4_js.isReferenceable(props.testID)) return true;
972
- if (chunkKZ2BLMS4_js.isReferenceable(props.accessibilityLabel)) return true;
973
- if (chunkKZ2BLMS4_js.isReferenceable(props.label)) return true;
974
- return chunkKZ2BLMS4_js.isReferenceable(findFirstText(fiber));
1426
+ if (chunkUZ2D4BVX_js.isReferenceable(props.testID)) return true;
1427
+ if (chunkUZ2D4BVX_js.isReferenceable(props.accessibilityLabel)) return true;
1428
+ if (chunkUZ2D4BVX_js.isReferenceable(props.label)) return true;
1429
+ return chunkUZ2D4BVX_js.isReferenceable(findFirstText(fiber));
975
1430
  }
976
1431
  function describeTouchable(fiber) {
977
1432
  const props = fiber?.memoizedProps ?? {};
@@ -982,10 +1437,10 @@ function describeTouchable(fiber) {
982
1437
  }
983
1438
  function touchableLooksDestructive(fiber) {
984
1439
  const descriptor = describeTouchable(fiber);
985
- if (descriptor && chunkKZ2BLMS4_js.looksDestructiveActionLabel(descriptor)) return true;
1440
+ if (descriptor && chunkUZ2D4BVX_js.looksDestructiveActionLabel(descriptor)) return true;
986
1441
  const props = fiber?.memoizedProps ?? {};
987
1442
  const haystack = [props.testID, props.accessibilityLabel, props.accessibilityHint, props.label].filter((value) => typeof value === "string").join(" ");
988
- return chunkKZ2BLMS4_js.looksDestructiveActionLabel(haystack);
1443
+ return chunkUZ2D4BVX_js.looksDestructiveActionLabel(haystack);
989
1444
  }
990
1445
  function describeItemCandidate(listIndex, zeroBasedIndex, item) {
991
1446
  const syntheticId = `list-${listIndex}-item-${zeroBasedIndex + 1}`;
@@ -1034,8 +1489,10 @@ function pressResolvedListItem(list, itemFiber, listIndex, itemIndex) {
1034
1489
  (candidate) => candidate !== touchable && containsFiber(touchable, candidate)
1035
1490
  );
1036
1491
  if (primaryWrapsNestedTargets) {
1037
- chunkKZ2BLMS4_js.appilotsDebugLog(
1038
- `listItemDispatch: item "list-${listIndex}-item-${itemIndex}" has nested pressables; pressing outer row target`
1492
+ const outer = describeTouchable(touchable) ?? "?";
1493
+ const nested = safeIdentifiableTouchables.filter((candidate) => candidate !== touchable && containsFiber(touchable, candidate)).map((candidate) => describeTouchable(candidate) ?? "?").slice(0, 6);
1494
+ chunkUZ2D4BVX_js.appilotsDebugLog(
1495
+ `listItemDispatch: item "list-${listIndex}-item-${itemIndex}" has nested pressables; pressing outer row target [outer=${outer}] [nested=${nested.join(", ")}] [outerHasOnPress=${typeof touchable?.memoizedProps?.onPress === "function"}]`
1039
1496
  );
1040
1497
  } else {
1041
1498
  const candidates = safeIdentifiableTouchables.map(describeTouchable).filter((d) => !!d).slice(0, 8);
@@ -1069,14 +1526,14 @@ function pressResolvedListItem(list, itemFiber, listIndex, itemIndex) {
1069
1526
  }
1070
1527
  return {
1071
1528
  ok: true,
1072
- containerType: chunkKZ2BLMS4_js.getTypeName(container.type) ?? chunkKZ2BLMS4_js.getTypeName(container.elementType) ?? list.containerType
1529
+ containerType: chunkUZ2D4BVX_js.getTypeName(container.type) ?? chunkUZ2D4BVX_js.getTypeName(container.elementType) ?? list.containerType
1073
1530
  };
1074
1531
  } catch (err) {
1075
1532
  return {
1076
1533
  ok: false,
1077
1534
  reason: "press-threw",
1078
1535
  error: err?.message ?? "press handler threw",
1079
- containerType: chunkKZ2BLMS4_js.getTypeName(container.type) ?? chunkKZ2BLMS4_js.getTypeName(container.elementType) ?? list.containerType
1536
+ containerType: chunkUZ2D4BVX_js.getTypeName(container.type) ?? chunkUZ2D4BVX_js.getTypeName(container.elementType) ?? list.containerType
1080
1537
  };
1081
1538
  }
1082
1539
  }
@@ -1102,10 +1559,12 @@ function scoreItemCandidate(item, zeroBasedIndex, locator) {
1102
1559
  if (locator.itemKey) {
1103
1560
  if (itemKey === locator.itemKey) score += 1e3;
1104
1561
  else if (itemKey && normalize2(itemKey) === normalize2(locator.itemKey)) score += 950;
1562
+ else return 0;
1105
1563
  }
1106
1564
  if (locator.reactKey) {
1107
1565
  if (reactKey === locator.reactKey) score += 900;
1108
1566
  else if (reactKey && normalize2(reactKey) === normalize2(locator.reactKey)) score += 850;
1567
+ else if (!locator.itemKey) return 0;
1109
1568
  }
1110
1569
  if (locator.itemIndex && zeroBasedIndex === locator.itemIndex - 1) score += 750;
1111
1570
  if (locator.itemIndex && markedIndex !== void 0 && (markedIndex === locator.itemIndex || markedIndex === locator.itemIndex - 1)) {
@@ -1132,7 +1591,7 @@ function candidateLists(containers, locator) {
1132
1591
  return containers.map((list, listIndex) => ({ list, listIndex }));
1133
1592
  }
1134
1593
  function pressListItemAtOrdinal(listIndex, itemIndex) {
1135
- if (!chunkKZ2BLMS4_js.getFiberRoot()) return { ok: false, reason: "no-fiber-root" };
1594
+ if (!chunkUZ2D4BVX_js.getFiberRoot()) return { ok: false, reason: "no-fiber-root" };
1136
1595
  const containers = findListContainers({ allowSingleHeuristic: true });
1137
1596
  if (listIndex < 0 || listIndex >= containers.length) {
1138
1597
  return { ok: false, reason: "list-not-found" };
@@ -1146,7 +1605,7 @@ function pressListItemAtOrdinal(listIndex, itemIndex) {
1146
1605
  return pressResolvedListItem(list, items[zeroBased], listIndex, itemIndex);
1147
1606
  }
1148
1607
  function resolveListItemByIdentity(locator) {
1149
- if (!chunkKZ2BLMS4_js.getFiberRoot()) return { ok: false, result: { ok: false, reason: "no-fiber-root" } };
1608
+ if (!chunkUZ2D4BVX_js.getFiberRoot()) return { ok: false, result: { ok: false, reason: "no-fiber-root" } };
1150
1609
  const parsed = locator.syntheticId ? parseSyntheticListItemId(locator.syntheticId) : null;
1151
1610
  const effective = parsed ? { ...locator, listIndex: parsed.listIndex, itemIndex: parsed.itemIndex } : locator;
1152
1611
  const containers = findListContainers({ allowSingleHeuristic: true });
@@ -1175,7 +1634,7 @@ function resolveListItemByIdentity(locator) {
1175
1634
  scoredCandidates.sort((a, b) => b.score - a.score);
1176
1635
  const best = scoredCandidates[0];
1177
1636
  const tiedCandidates = scoredCandidates.filter((c) => c.score === best.score);
1178
- if (tiedCandidates.length > 1 && best.score < 750) {
1637
+ if (tiedCandidates.length > 1 && (best.score < 750 || Boolean(effective.itemKey || effective.reactKey))) {
1179
1638
  const candidates = tiedCandidates.map((c) => describeItemCandidate(c.listIndex, c.itemIndex - 1, c.item)).slice(0, 8);
1180
1639
  return {
1181
1640
  ok: false,
@@ -1207,9 +1666,9 @@ function findSwitchFibers(item) {
1207
1666
  const { fiber, includeSibling } = stack.pop();
1208
1667
  if (!fiber || visited.has(fiber)) continue;
1209
1668
  visited.add(fiber);
1210
- if (chunkKZ2BLMS4_js.shouldSkipSubtree(fiber)) continue;
1669
+ if (chunkUZ2D4BVX_js.shouldSkipSubtree(fiber)) continue;
1211
1670
  const props = fiber.memoizedProps ?? {};
1212
- if (chunkKZ2BLMS4_js.isSwitchLike(fiber, props)) {
1671
+ if (chunkUZ2D4BVX_js.isSwitchLike(fiber, props)) {
1213
1672
  if (props.__appilotsInternal !== true) found.push(fiber);
1214
1673
  if (includeSibling && fiber.sibling) {
1215
1674
  stack.push({ fiber: fiber.sibling, includeSibling: true });
@@ -1243,11 +1702,11 @@ function toggleListItemByIdentity(locator, options) {
1243
1702
  }
1244
1703
  const target = switches[0];
1245
1704
  const props = target?.memoizedProps ?? {};
1246
- const current = chunkKZ2BLMS4_js.switchValue(props);
1247
- if (options?.expected !== void 0 && current === options.expected) {
1248
- return { ok: true, containerType: list.containerType, value: current, noop: true };
1705
+ const current2 = chunkUZ2D4BVX_js.switchValue(props);
1706
+ if (options?.expected !== void 0 && current2 === options.expected) {
1707
+ return { ok: true, containerType: list.containerType, value: current2, noop: true };
1249
1708
  }
1250
- const flipped = flipSwitchFiber(target, !current);
1709
+ const flipped = flipSwitchFiber(target, !current2);
1251
1710
  if (!flipped.ok) {
1252
1711
  if (flipped.reason === "no-handler") {
1253
1712
  return {
@@ -1264,7 +1723,7 @@ function toggleListItemByIdentity(locator, options) {
1264
1723
  containerType: list.containerType
1265
1724
  };
1266
1725
  }
1267
- return { ok: true, containerType: list.containerType, value: !current };
1726
+ return { ok: true, containerType: list.containerType, value: !current2 };
1268
1727
  }
1269
1728
  function pressListItemByIdentity(locator) {
1270
1729
  const resolved = resolveListItemByIdentity(locator);
@@ -1282,7 +1741,7 @@ function screenDiagnose(targetId, category = "component-not-found") {
1282
1741
  return {
1283
1742
  category,
1284
1743
  targetId,
1285
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0
1744
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0
1286
1745
  };
1287
1746
  }
1288
1747
  function isPressLike(action) {
@@ -1302,10 +1761,10 @@ function elementLookupKey(value) {
1302
1761
  return canonicalElementId(value).toLowerCase().replace(/[\s_]+/g, "-");
1303
1762
  }
1304
1763
  function findRegisteredElement(elementId) {
1305
- const direct = chunkKZ2BLMS4_js.elementRegistry.get(elementId) ?? chunkKZ2BLMS4_js.elementRegistry.get(canonicalElementId(elementId));
1764
+ const direct = chunkUZ2D4BVX_js.elementRegistry.get(elementId) ?? chunkUZ2D4BVX_js.elementRegistry.get(canonicalElementId(elementId));
1306
1765
  if (direct) return direct;
1307
1766
  const wanted = elementLookupKey(elementId);
1308
- return chunkKZ2BLMS4_js.elementRegistry.snapshot().find((element) => elementLookupKey(element.id) === wanted);
1767
+ return chunkUZ2D4BVX_js.elementRegistry.snapshot().find((element) => elementLookupKey(element.id) === wanted);
1309
1768
  }
1310
1769
  function resolveInteractionElement(elementId) {
1311
1770
  return getLatestElement(elementId);
@@ -1425,7 +1884,7 @@ function dispatchIdentifiedToggle(targetId, action) {
1425
1884
  diagnose: { ...screenDiagnose(targetId, "unknown"), recoverable: true }
1426
1885
  };
1427
1886
  }
1428
- chunkKZ2BLMS4_js.appilotsDebugLog(
1887
+ chunkUZ2D4BVX_js.appilotsDebugLog(
1429
1888
  `elementDispatch: "${action}" on "${targetId}" names a switch \u2014 flipping it instead of pressing, so the verb cannot land on an ancestor`
1430
1889
  );
1431
1890
  try {
@@ -1459,6 +1918,12 @@ function dispatchFiberTarget(targetId, action) {
1459
1918
  diagnose: screenDiagnose(targetId)
1460
1919
  };
1461
1920
  }
1921
+ const measured = handle.measure?.() ?? null;
1922
+ if (measured && getSpotlightMode() === "guided") {
1923
+ setSpotlightTarget(revealTarget(measured).rect, handle.label);
1924
+ } else {
1925
+ setSpotlightTarget(measured, handle.label);
1926
+ }
1462
1927
  try {
1463
1928
  if (isPressLike(action) && handle.kind === "target" && handle.press) {
1464
1929
  handle.press();
@@ -1509,6 +1974,13 @@ function dispatchLegacyTarget(targetId, registry, action) {
1509
1974
  };
1510
1975
  }
1511
1976
  function dispatchElement(element, registry, action) {
1977
+ if (element.dispatchable === false) {
1978
+ return {
1979
+ success: false,
1980
+ error: `Element "${element.id}" is observation-only and has no exact live control binding.`,
1981
+ diagnose: { ...screenDiagnose(element.id, "ambiguous-target"), recoverable: false }
1982
+ };
1983
+ }
1512
1984
  if (element.disabled === true) {
1513
1985
  return {
1514
1986
  success: false,
@@ -1527,6 +1999,13 @@ function dispatchElement(element, registry, action) {
1527
1999
  texts: element.texts
1528
2000
  });
1529
2001
  if (pressed.ok) return { success: true };
2002
+ if (!pressed.refusal && (element.listContext.itemKey || element.listContext.reactKey)) {
2003
+ return {
2004
+ success: false,
2005
+ error: pressed.error ?? `The observed list entity "${element.label ?? element.id}" could not be resolved. Reobserve; do not substitute its old position.`,
2006
+ diagnose: { ...screenDiagnose(element.id, "ambiguous-target"), recoverable: true }
2007
+ };
2008
+ }
1530
2009
  if (pressed.refusal) {
1531
2010
  return {
1532
2011
  success: false,
@@ -1634,7 +2113,7 @@ function dispatchToggleElement(element, registry) {
1634
2113
  }
1635
2114
  function dispatchStructuredElementId(identifier, action) {
1636
2115
  if (!isElementIdentifier(identifier) || !isPressLike(action)) return null;
1637
- const parsed = chunkKZ2BLMS4_js.parseStableElementId(identifier);
2116
+ const parsed = chunkUZ2D4BVX_js.parseStableElementId(identifier);
1638
2117
  if (!parsed || parsed.role !== "option") return null;
1639
2118
  if (!parsed.itemKey && !parsed.listId) return null;
1640
2119
  const pressed = pressListItemByIdentity({
@@ -1644,7 +2123,7 @@ function dispatchStructuredElementId(identifier, action) {
1644
2123
  texts: parsed.itemKey ? [parsed.itemKey] : void 0
1645
2124
  });
1646
2125
  if (pressed.ok) {
1647
- chunkKZ2BLMS4_js.appilotsDebugLog(
2126
+ chunkUZ2D4BVX_js.appilotsDebugLog(
1648
2127
  `elementDispatch: resolved stale el id "${identifier}" via structural identity (listId="${parsed.listId}", itemKey="${parsed.itemKey}")`
1649
2128
  );
1650
2129
  return { success: true };
@@ -1675,20 +2154,20 @@ function normalize4(value) {
1675
2154
  return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
1676
2155
  }
1677
2156
  function resolveScreenForFormFields() {
1678
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
2157
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
1679
2158
  for (let i = activePath.length - 1; i >= 0; i--) {
1680
2159
  const name = activePath[i];
1681
- const meta = name ? chunkKZ2BLMS4_js.getScreenMetadata(name) : void 0;
2160
+ const meta = name ? chunkUZ2D4BVX_js.getScreenMetadata(name) : void 0;
1682
2161
  if (name && Array.isArray(meta?.fields) && meta.fields.length > 0) return name;
1683
2162
  }
1684
- return chunkKZ2BLMS4_js.getCurrentScreen();
2163
+ return chunkUZ2D4BVX_js.getCurrentScreen();
1685
2164
  }
1686
2165
  function collectKnownFormFields(registry) {
1687
2166
  const registryFields = registry.snapshot().filter((entry) => entry.kind === "field" || entry.kind === "slider").map((entry) => ({ id: entry.id, label: entry.label }));
1688
2167
  if (registryFields.length >= 2) return registryFields;
1689
2168
  const screen = resolveScreenForFormFields();
1690
2169
  if (screen) {
1691
- const metaFields = chunkKZ2BLMS4_js.getScreenMetadata(screen)?.fields;
2170
+ const metaFields = chunkUZ2D4BVX_js.getScreenMetadata(screen)?.fields;
1692
2171
  if (Array.isArray(metaFields) && metaFields.length > 0) {
1693
2172
  return metaFields.filter((field) => !!field && typeof field.id === "string").map((field) => ({
1694
2173
  id: field.id,
@@ -1698,7 +2177,7 @@ function collectKnownFormFields(registry) {
1698
2177
  }));
1699
2178
  }
1700
2179
  }
1701
- for (const meta of chunkKZ2BLMS4_js.getAllScreens()) {
2180
+ for (const meta of chunkUZ2D4BVX_js.getAllScreens()) {
1702
2181
  if (Array.isArray(meta.fields) && meta.fields.length > 0) {
1703
2182
  return meta.fields.filter((field) => !!field && typeof field.id === "string").map((field) => ({
1704
2183
  id: field.id,
@@ -1811,7 +2290,7 @@ function rewriteMisplacedFormFillFields(fields, registry) {
1811
2290
  const inferred = inferFieldIdForValue(String(field.value ?? ""), knownFields, used);
1812
2291
  if (!inferred) return field;
1813
2292
  used.add(inferred);
1814
- chunkKZ2BLMS4_js.appilotsDebugWarn(
2293
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
1815
2294
  `formFieldRewrite: ${field.fieldId} \u2192 ${inferred} (value=${JSON.stringify(field.value)})`
1816
2295
  );
1817
2296
  return { ...field, fieldId: inferred };
@@ -1819,8 +2298,8 @@ function rewriteMisplacedFormFillFields(fields, registry) {
1819
2298
  }
1820
2299
 
1821
2300
  // src/executor/formValidation.ts
1822
- var FORM_VALIDATION_TEXT_RE2 = chunkKZ2BLMS4_js.FORM_VALIDATION_TEXT_RE;
1823
- var FORM_SUCCESS_TEXT_RE = chunkKZ2BLMS4_js.FORM_WRITE_SUCCESS_TEXT_RE;
2301
+ var FORM_VALIDATION_TEXT_RE2 = chunkUZ2D4BVX_js.FORM_VALIDATION_TEXT_RE;
2302
+ var FORM_SUCCESS_TEXT_RE = chunkUZ2D4BVX_js.FORM_WRITE_SUCCESS_TEXT_RE;
1824
2303
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1825
2304
  function inferValidationFieldId(message, inputs) {
1826
2305
  const haystack = normalizeText(message);
@@ -1844,12 +2323,18 @@ function normalizeText(value) {
1844
2323
  function containsWord(haystack, needle) {
1845
2324
  return ` ${haystack} `.includes(` ${needle} `);
1846
2325
  }
1847
- function readVisibleFormValidation(snap) {
2326
+ function readVisibleFormValidation(snap, beforeTexts) {
1848
2327
  if (!snap) return void 0;
2328
+ const invalid = snap.inputs?.find((input) => input.invalid === true);
2329
+ const hasSubmitControl = snap.buttons?.some(
2330
+ (button) => chunkUZ2D4BVX_js.SUBMIT_CONTROL_RE.test(button.label ?? button.id ?? "")
2331
+ );
1849
2332
  for (const text of snap.texts ?? []) {
1850
- if (!text?.trim() || !FORM_VALIDATION_TEXT_RE2.test(text)) continue;
2333
+ if (!text?.trim() || !chunkUZ2D4BVX_js.isVisibleValidationText(text, { invalid: !!invalid, hasSubmitControl, beforeTexts }))
2334
+ continue;
1851
2335
  return { fieldId: inferValidationFieldId(text, snap.inputs), message: text };
1852
2336
  }
2337
+ if (invalid) return { fieldId: invalid.id, message: "The app marks this field as invalid." };
1853
2338
  return void 0;
1854
2339
  }
1855
2340
  function readVisibleFormSuccess(snap) {
@@ -1857,20 +2342,23 @@ function readVisibleFormSuccess(snap) {
1857
2342
  return (snap.texts ?? []).find((text) => text?.trim() && FORM_SUCCESS_TEXT_RE.test(text));
1858
2343
  }
1859
2344
  function submitAsyncStillPending(submitTargetId) {
1860
- const probe = chunkKZ2BLMS4_js.probeLoadingState(submitTargetId ?? null, chunkKZ2BLMS4_js.getCurrentScreen());
2345
+ const probe = chunkUZ2D4BVX_js.probeLoadingState(submitTargetId ?? null, chunkUZ2D4BVX_js.getCurrentScreen());
1861
2346
  if (probe.loading) return true;
1862
2347
  if (submitTargetId && probe.pressedFound && probe.pressedDisabled) return true;
1863
2348
  return false;
1864
2349
  }
1865
- function evaluatePostSubmitSnapshot(snap, activePath) {
1866
- const validation = readVisibleFormValidation(snap);
2350
+ function evaluatePostSubmitSnapshot(snap, activePath, beforeTexts) {
2351
+ const validation = readVisibleFormValidation(snap, beforeTexts);
1867
2352
  if (validation) {
1868
2353
  return { status: "failure", fieldId: validation.fieldId, message: validation.message };
1869
2354
  }
1870
2355
  const successText = readVisibleFormSuccess(snap);
1871
2356
  if (successText) return { status: "success", message: successText };
1872
- if (!chunkKZ2BLMS4_js.snapshotShowsOpenCreateForm(snap, activePath)) {
1873
- return { status: "success" };
2357
+ if (!chunkUZ2D4BVX_js.snapshotShowsOpenCreateForm(snap, activePath)) {
2358
+ return {
2359
+ status: "unverified",
2360
+ message: "The form closed without a visible success receipt. Inspect the resulting record before claiming the goal is complete."
2361
+ };
1874
2362
  }
1875
2363
  return void 0;
1876
2364
  }
@@ -1885,8 +2373,8 @@ async function waitForPostSubmitOutcome(options) {
1885
2373
  continue;
1886
2374
  }
1887
2375
  const snap2 = captureSnapshot();
1888
- const activePath2 = chunkKZ2BLMS4_js.getActiveRouteNames();
1889
- const outcome2 = evaluatePostSubmitSnapshot(snap2, activePath2);
2376
+ const activePath2 = chunkUZ2D4BVX_js.getActiveRouteNames();
2377
+ const outcome2 = evaluatePostSubmitSnapshot(snap2, activePath2, options?.beforeTexts);
1890
2378
  if (outcome2) return outcome2;
1891
2379
  await sleep(pollMs);
1892
2380
  }
@@ -1897,8 +2385,8 @@ async function waitForPostSubmitOutcome(options) {
1897
2385
  };
1898
2386
  }
1899
2387
  const snap = captureSnapshot();
1900
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
1901
- const outcome = evaluatePostSubmitSnapshot(snap, activePath);
2388
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
2389
+ const outcome = evaluatePostSubmitSnapshot(snap, activePath, options?.beforeTexts);
1902
2390
  if (outcome) return outcome;
1903
2391
  return {
1904
2392
  status: "failure",
@@ -1906,91 +2394,6 @@ async function waitForPostSubmitOutcome(options) {
1906
2394
  };
1907
2395
  }
1908
2396
 
1909
- // ../shared/dist/chunk-ERITSWKE.mjs
1910
- var DEFAULT_FUZZY_FLOOR = 4;
1911
- function normalizeId(value) {
1912
- return String(value ?? "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]/g, "");
1913
- }
1914
- var KIND_PREFIX_WORDS = "button|switch|toggle|input|field|link|btn|slider";
1915
- var KEBAB_SNAKE_PREFIX_RE = new RegExp(`^(?:${KIND_PREFIX_WORDS})[-_]`, "i");
1916
- var CAMEL_PREFIX_RE = new RegExp(`^(?:${KIND_PREFIX_WORDS})(?=[A-Z])`, "");
1917
- function stripKindPrefix(id) {
1918
- const raw = String(id ?? "");
1919
- if (KEBAB_SNAKE_PREFIX_RE.test(raw)) return raw.replace(KEBAB_SNAKE_PREFIX_RE, "");
1920
- if (CAMEL_PREFIX_RE.test(raw)) return raw.replace(CAMEL_PREFIX_RE, "");
1921
- return raw;
1922
- }
1923
- function actionAllowsKind(action, kind) {
1924
- if (!action || !kind) return true;
1925
- switch (action) {
1926
- case "press":
1927
- case "longPress":
1928
- return kind !== "field" && kind !== "input" && kind !== "slider";
1929
- case "set_value":
1930
- return kind === "slider" || kind === "field" || kind === "input";
1931
- case "focus":
1932
- return kind === "field" || kind === "input";
1933
- case "toggle":
1934
- return kind === "toggle";
1935
- default:
1936
- return true;
1937
- }
1938
- }
1939
- function distinctIds(candidates) {
1940
- return [...new Set(candidates.map((c) => c.id))];
1941
- }
1942
- function resolveTarget(requested, candidates, opts = {}) {
1943
- const floor = opts.fuzzyFloor ?? DEFAULT_FUZZY_FLOOR;
1944
- const nReq = normalizeId(requested);
1945
- if (!nReq || candidates.length === 0) {
1946
- return { status: "unverified", reason: "not-found" };
1947
- }
1948
- for (const c of candidates) {
1949
- if (normalizeId(c.id) === nReq) {
1950
- return { status: "matched", id: c.id, tier: "exact" };
1951
- }
1952
- }
1953
- const labelExact = distinctIds(
1954
- candidates.filter((c) => c.label && normalizeId(c.label) === nReq)
1955
- );
1956
- if (labelExact.length === 1) return { status: "matched", id: labelExact[0], tier: "exact" };
1957
- if (labelExact.length > 1) return { status: "unverified", reason: "ambiguous" };
1958
- const nReqStripped = normalizeId(stripKindPrefix(requested));
1959
- for (const c of candidates) {
1960
- const nId = normalizeId(c.id);
1961
- const nIdStripped = normalizeId(stripKindPrefix(c.id));
1962
- if (nReqStripped && (nReqStripped === nId || nReqStripped === nIdStripped) || nIdStripped && nIdStripped === nReq) {
1963
- return { status: "matched", id: c.id, tier: "prefix" };
1964
- }
1965
- }
1966
- if (opts.destructive) {
1967
- return { status: "unverified", reason: "destructive-inexact" };
1968
- }
1969
- if (nReq.length < floor) {
1970
- return { status: "unverified", reason: "not-found" };
1971
- }
1972
- const fuzzy = candidates.filter((c) => {
1973
- for (const s of [c.id, c.label]) {
1974
- if (!s) continue;
1975
- const ns = normalizeId(s);
1976
- if (ns.length >= floor && (ns.includes(nReq) || nReq.includes(ns))) return true;
1977
- }
1978
- return false;
1979
- });
1980
- if (fuzzy.length === 0) {
1981
- return { status: "unverified", reason: "not-found" };
1982
- }
1983
- const typed = fuzzy.filter((c) => actionAllowsKind(opts.action, c.kind));
1984
- if (typed.length === 0) {
1985
- return { status: "unverified", reason: "type-mismatch" };
1986
- }
1987
- const ids = distinctIds(typed);
1988
- if (ids.length > 1) {
1989
- return { status: "unverified", reason: "ambiguous" };
1990
- }
1991
- return { status: "matched", id: ids[0], tier: "fuzzy" };
1992
- }
1993
-
1994
2397
  // src/executor/handlers/uiInteractionHandler.ts
1995
2398
  function findSnapshotButton(targetId) {
1996
2399
  try {
@@ -2003,16 +2406,16 @@ function findSnapshotButton(targetId) {
2003
2406
  }
2004
2407
  }
2005
2408
  function buildUiDiagnose(targetId, cause) {
2006
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
2007
- const screen = activePath[activePath.length - 1] ?? chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0;
2409
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
2410
+ const screen = activePath[activePath.length - 1] ?? chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0;
2008
2411
  const screenPath = activePath.length > 1 ? activePath.join("/") : screen;
2009
2412
  const diagnose = {
2010
2413
  category: cause === "not-found" ? "component-not-found" : cause === "disabled" ? "disabled" : "unknown",
2011
2414
  targetId,
2012
2415
  screen: screenPath
2013
2416
  };
2014
- if (chunkKZ2BLMS4_js.isOptionalStepAutomationFailure(targetId, screenPath, activePath)) {
2015
- diagnose.visibleMessage = chunkKZ2BLMS4_js.OPTIONAL_STEP_AUTOMATION_HINT;
2417
+ if (chunkUZ2D4BVX_js.isOptionalStepAutomationFailure(targetId, screenPath, activePath)) {
2418
+ diagnose.visibleMessage = chunkUZ2D4BVX_js.OPTIONAL_STEP_AUTOMATION_HINT;
2016
2419
  diagnose.recoverable = true;
2017
2420
  }
2018
2421
  return diagnose;
@@ -2063,6 +2466,7 @@ function stripComponentPrefix(componentId) {
2063
2466
  return null;
2064
2467
  }
2065
2468
  function resolveComponentId(componentId, registry, action) {
2469
+ if (chunkUZ2D4BVX_js.isSourceControlId(componentId)) return componentId;
2066
2470
  if (registry.getTarget(componentId) || registry.getToggle(componentId) || registry.getField(componentId) || registry.getSlider(componentId)) {
2067
2471
  return componentId;
2068
2472
  }
@@ -2070,15 +2474,16 @@ function resolveComponentId(componentId, registry, action) {
2070
2474
  const snapshot = registry.snapshot();
2071
2475
  for (const comp of snapshot) {
2072
2476
  if (comp.id.toLowerCase() === lower) {
2073
- chunkKZ2BLMS4_js.appilotsDebugLog(
2477
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2074
2478
  `resolveComponentId: case-insensitive match "${componentId}" \u2192 "${comp.id}"`
2075
2479
  );
2076
2480
  return comp.id;
2077
2481
  }
2078
2482
  }
2079
- const currentScreen = chunkKZ2BLMS4_js.getCurrentScreen();
2483
+ if (isPositionalControlId(componentId)) return componentId;
2484
+ const currentScreen = chunkUZ2D4BVX_js.getCurrentScreen();
2080
2485
  if (currentScreen) {
2081
- const screenMeta = chunkKZ2BLMS4_js.getScreenMetadata(currentScreen);
2486
+ const screenMeta = chunkUZ2D4BVX_js.getScreenMetadata(currentScreen);
2082
2487
  const actions = screenMeta?.actions;
2083
2488
  if (actions && Array.isArray(actions)) {
2084
2489
  const metaAction = actions.find(
@@ -2087,14 +2492,14 @@ function resolveComponentId(componentId, registry, action) {
2087
2492
  if (metaAction && typeof metaAction === "object" && metaAction.label) {
2088
2493
  const metaLabel = metaAction.label;
2089
2494
  const normalizedMetaLabel = metaLabel.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
2090
- chunkKZ2BLMS4_js.appilotsDebugLog(
2495
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2091
2496
  `resolveComponentId: found screen metadata for "${componentId}" \u2192 label="${metaLabel}"`
2092
2497
  );
2093
2498
  for (const comp of snapshot) {
2094
2499
  if (!comp.label) continue;
2095
2500
  const compLabel = comp.label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
2096
2501
  if (compLabel === normalizedMetaLabel || compLabel.includes(normalizedMetaLabel) || normalizedMetaLabel.includes(compLabel)) {
2097
- chunkKZ2BLMS4_js.appilotsDebugLog(
2502
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2098
2503
  `resolveComponentId: FOUND via metadata: "${componentId}" \u2192 "${comp.id}" (label="${comp.label}")`
2099
2504
  );
2100
2505
  return comp.id;
@@ -2109,9 +2514,9 @@ function resolveComponentId(componentId, registry, action) {
2109
2514
  label: c.label,
2110
2515
  kind: c.kind
2111
2516
  }));
2112
- const match = resolveTarget(componentId, candidates, { action });
2517
+ const match = chunkUZ2D4BVX_js.resolveTarget(componentId, candidates, { action });
2113
2518
  if (match.status === "matched") {
2114
- chunkKZ2BLMS4_js.appilotsDebugLog(
2519
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2115
2520
  `resolveComponentId: matched "${componentId}" -> "${match.id}" (tier=${match.tier})`
2116
2521
  );
2117
2522
  return match.id;
@@ -2201,7 +2606,7 @@ function setValueHandler(rawComponentId, requested, registry) {
2201
2606
  try {
2202
2607
  slider.setValue(applied);
2203
2608
  if (applied !== requestedValue) {
2204
- chunkKZ2BLMS4_js.appilotsDebugLog(
2609
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2205
2610
  `uiInteractionHandler: set_value snapped ${requestedValue} \u2192 ${applied} for "${componentId}" (step=${step})`
2206
2611
  );
2207
2612
  }
@@ -2236,7 +2641,7 @@ function checkModalOcclusion(rawComponentId, action) {
2236
2641
  return null;
2237
2642
  }
2238
2643
  if (typeof rawComponentId !== "string" || rawComponentId.trim().length === 0) return null;
2239
- const dialog = chunkKZ2BLMS4_js.getOpenNativeDialog();
2644
+ const dialog = chunkUZ2D4BVX_js.getOpenNativeDialog();
2240
2645
  if (dialog) {
2241
2646
  const needle = normalizeIdent(rawComponentId);
2242
2647
  if (dialog.buttons.some((b) => normalizeIdent(b.label) === needle)) return null;
@@ -2246,16 +2651,16 @@ function checkModalOcclusion(rawComponentId, action) {
2246
2651
  diagnose: {
2247
2652
  category: "ambiguous-target",
2248
2653
  targetId: rawComponentId,
2249
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0,
2654
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0,
2250
2655
  candidates: dialog.buttons.map((b) => b.label).slice(0, 8)
2251
2656
  }
2252
2657
  };
2253
2658
  }
2254
- const fiberRoot = chunkKZ2BLMS4_js.getFiberRoot();
2659
+ const fiberRoot = chunkUZ2D4BVX_js.getFiberRoot();
2255
2660
  if (!fiberRoot) return null;
2256
2661
  let snap;
2257
2662
  try {
2258
- snap = chunkKZ2BLMS4_js.walkFiber(fiberRoot);
2663
+ snap = chunkUZ2D4BVX_js.walkFiber(fiberRoot);
2259
2664
  } catch {
2260
2665
  return null;
2261
2666
  }
@@ -2317,15 +2722,18 @@ function checkModalOcclusion(rawComponentId, action) {
2317
2722
  }
2318
2723
  function uiInteractionHandler(payload, context) {
2319
2724
  const { permissions } = context;
2320
- const registry = context.registry ?? chunkKZ2BLMS4_js.componentRegistry;
2725
+ const registry = context.registry ?? chunkUZ2D4BVX_js.componentRegistry;
2321
2726
  if (!permissions.canInteractUI) {
2322
2727
  return {
2323
2728
  success: false,
2324
- error: "UI interaction is not permitted",
2325
- diagnose: buildUiDiagnose(
2326
- payload.componentId ?? payload.targetId ?? "",
2327
- "unknown"
2328
- )
2729
+ error: chunkUZ2D4BVX_js.permissionDeniedMessage("UI interaction", "canInteractUI"),
2730
+ diagnose: {
2731
+ ...buildUiDiagnose(
2732
+ payload.componentId ?? payload.targetId ?? "",
2733
+ "unknown"
2734
+ ),
2735
+ recoverable: false
2736
+ }
2329
2737
  };
2330
2738
  }
2331
2739
  const { action } = payload;
@@ -2339,7 +2747,7 @@ function uiInteractionHandler(payload, context) {
2339
2747
  if (sliderId) {
2340
2748
  const smuggled = payload.value ?? payload.params?.value;
2341
2749
  if (smuggled !== void 0) {
2342
- chunkKZ2BLMS4_js.appilotsDebugLog(
2750
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2343
2751
  `uiInteractionHandler: "${actionStr}" on slider "${sliderId}" carried value=${JSON.stringify(smuggled)} \u2014 routing to set_value`
2344
2752
  );
2345
2753
  return setValueHandler(rawComponentId, smuggled, registry);
@@ -2350,23 +2758,23 @@ function uiInteractionHandler(payload, context) {
2350
2758
  diagnose: { ...buildUiDiagnose(sliderId, "unknown"), recoverable: true }
2351
2759
  };
2352
2760
  }
2353
- const openDialog = chunkKZ2BLMS4_js.getOpenNativeDialog();
2761
+ const openDialog = chunkUZ2D4BVX_js.getOpenNativeDialog();
2354
2762
  const isPressVerb = actionStr === "press" || actionStr === "tap" || actionStr === "click";
2355
2763
  if (openDialog && isPressVerb) {
2356
2764
  const needle = normalizeIdent(rawComponentId);
2357
2765
  const button = openDialog.buttons.find((b) => normalizeIdent(b.label) === needle);
2358
2766
  if (button) {
2359
2767
  button.press();
2360
- chunkKZ2BLMS4_js.appilotsDebugLog(`uiInteractionHandler: pressed native dialog button "${button.label}"`);
2768
+ chunkUZ2D4BVX_js.appilotsDebugLog(`uiInteractionHandler: pressed native dialog button "${button.label}"`);
2361
2769
  return { success: true, effect: "changed" };
2362
2770
  }
2363
2771
  }
2364
2772
  if (!openDialog && isPressVerb) {
2365
- const answered = chunkKZ2BLMS4_js.getLastAnsweredNativeDialog();
2773
+ const answered = chunkUZ2D4BVX_js.getLastAnsweredNativeDialog();
2366
2774
  const needle = normalizeIdent(rawComponentId);
2367
2775
  if (answered && needle) {
2368
2776
  if (answered.answeredWith && normalizeIdent(answered.answeredWith) === needle) {
2369
- chunkKZ2BLMS4_js.appilotsDebugLog(
2777
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2370
2778
  `uiInteractionHandler: native dialog button "${answered.answeredWith}" was already pressed and the dialog is closed \u2014 treating the repeat as done`
2371
2779
  );
2372
2780
  return { success: true, effect: "completed" };
@@ -2378,7 +2786,7 @@ function uiInteractionHandler(payload, context) {
2378
2786
  diagnose: {
2379
2787
  category: "component-not-found",
2380
2788
  targetId: rawComponentId,
2381
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0,
2789
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0,
2382
2790
  recoverable: false
2383
2791
  }
2384
2792
  };
@@ -2387,7 +2795,7 @@ function uiInteractionHandler(payload, context) {
2387
2795
  }
2388
2796
  const occluded = checkModalOcclusion(rawComponentId, actionStr);
2389
2797
  if (occluded) {
2390
- chunkKZ2BLMS4_js.appilotsDebugLog(
2798
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2391
2799
  `uiInteractionHandler: blocked "${actionStr}" on "${rawComponentId}" \u2014 target is behind an open modal`
2392
2800
  );
2393
2801
  return occluded;
@@ -2395,7 +2803,7 @@ function uiInteractionHandler(payload, context) {
2395
2803
  if (typeof rawComponentId === "string" && rawComponentId.trim().toLowerCase().startsWith("el:")) {
2396
2804
  const result = dispatchElementOrTarget(rawComponentId, registry, actionStr);
2397
2805
  if (result.success) {
2398
- chunkKZ2BLMS4_js.appilotsDebugLog(
2806
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2399
2807
  `uiInteractionHandler: interaction element dispatch succeeded for "${rawComponentId}"`
2400
2808
  );
2401
2809
  }
@@ -2403,17 +2811,17 @@ function uiInteractionHandler(payload, context) {
2403
2811
  }
2404
2812
  const ordinal = parseSyntheticListItemId(rawComponentId);
2405
2813
  if (ordinal && (action === "press" || actionStr === "tap" || actionStr === "click" || actionStr === "longPress")) {
2406
- chunkKZ2BLMS4_js.appilotsDebugLog(
2814
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2407
2815
  `uiInteractionHandler: dispatching synthetic list ordinal \u2014 componentId="${rawComponentId}" list=${ordinal.listIndex} item=${ordinal.itemIndex}`
2408
2816
  );
2409
2817
  const result = pressListItemAtOrdinal(ordinal.listIndex, ordinal.itemIndex);
2410
2818
  if (result.ok) {
2411
- chunkKZ2BLMS4_js.appilotsDebugLog(
2819
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2412
2820
  `uiInteractionHandler: synthetic ordinal press succeeded (container=${result.containerType ?? "List"})`
2413
2821
  );
2414
2822
  return { success: true };
2415
2823
  }
2416
- const screen = chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0;
2824
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0;
2417
2825
  if (result.reason === "list-not-found" || result.reason === "item-not-found") {
2418
2826
  return {
2419
2827
  success: false,
@@ -2454,7 +2862,7 @@ function uiInteractionHandler(payload, context) {
2454
2862
  };
2455
2863
  }
2456
2864
  const componentId = resolveComponentId(rawComponentId, registry, actionStr);
2457
- chunkKZ2BLMS4_js.appilotsDebugLog(
2865
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2458
2866
  `uiInteractionHandler: resolved componentId="${payload.componentId}" \u2192 "${componentId}"`
2459
2867
  );
2460
2868
  const target = registry.getTarget(componentId);
@@ -2507,7 +2915,7 @@ function uiInteractionHandler(payload, context) {
2507
2915
  return { success: false, error: err?.message ?? "Focus failed" };
2508
2916
  }
2509
2917
  }
2510
- chunkKZ2BLMS4_js.appilotsDebugLog(
2918
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2511
2919
  `uiInteractionHandler: registry miss for "${componentId}", falling back to fiber tree`
2512
2920
  );
2513
2921
  const namedToggle = dispatchIdentifiedToggle(componentId, actionStr);
@@ -2519,7 +2927,7 @@ function uiInteractionHandler(payload, context) {
2519
2927
  try {
2520
2928
  if (actionStr === "press" && handle.kind === "target" && handle.press) {
2521
2929
  handle.press();
2522
- chunkKZ2BLMS4_js.appilotsDebugLog(
2930
+ chunkUZ2D4BVX_js.appilotsDebugLog(
2523
2931
  `uiInteractionHandler: fiber fallback press succeeded for "${componentId}"`
2524
2932
  );
2525
2933
  return { success: true };
@@ -2555,6 +2963,28 @@ function uiInteractionHandler(payload, context) {
2555
2963
  };
2556
2964
  }
2557
2965
 
2966
+ // src/executor/completeTextEdit.ts
2967
+ var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2968
+ async function completeTextEdit(fieldId, expected, registered, origin, resolveRegistered) {
2969
+ await sleep2(80);
2970
+ const current2 = findInteractiveByIdentifier(fieldId, { kind: "field" });
2971
+ if (origin?.controlIdentity && current2?.controlIdentity !== origin.controlIdentity) return false;
2972
+ if (registered && resolveRegistered && resolveRegistered() !== registered) return false;
2973
+ if (registered?.finishEditing) registered.finishEditing();
2974
+ else if (current2 && (current2.matchCount ?? 1) === 1 && !current2.disabled)
2975
+ current2.finishEditing?.();
2976
+ const started = Date.now();
2977
+ do {
2978
+ await sleep2(80);
2979
+ const live = findInteractiveByIdentifier(fieldId, { kind: "field" });
2980
+ if (origin?.controlIdentity && live?.controlIdentity !== origin.controlIdentity) return false;
2981
+ if (registered && resolveRegistered && resolveRegistered() !== registered) return false;
2982
+ const value = registered ? registered.getValue() : live?.getValue?.();
2983
+ if (value !== void 0 && value !== null && String(value) === String(expected)) return true;
2984
+ } while (Date.now() - started < 480);
2985
+ return false;
2986
+ }
2987
+
2558
2988
  // src/executor/formSubmitResolve.ts
2559
2989
  var SUBMIT_LABEL_HINT_RE = /\b(submit|save|send|continue|next|sign\s*in|log\s*in|register|salvar|enviar|entrar|cadastrar|continuar|proximo|próximo|avancar|avançar|confirmar|guardar|registrarse|iniciar\s+sesi[óo]n|enregistrer|envoyer|valider|soumettre|confirmer|se\s+connecter|s['e]\s*inscrire)\b/i;
2560
2990
  var AGENT_CHROME_TARGET_RE = /\b(fechar|close|dismiss|cancelar|cancel|limpar|clear|chat\s+do\s+assistente|assistant\s+chat)\b/i;
@@ -2582,7 +3012,7 @@ function submitLabelHintsSubmit(label) {
2582
3012
  return SUBMIT_LABEL_HINT_RE.test(trimmed);
2583
3013
  }
2584
3014
  function screenDeclaresSubmitAction(screenName) {
2585
- const meta = chunkKZ2BLMS4_js.getScreenMetadata(screenName);
3015
+ const meta = chunkUZ2D4BVX_js.getScreenMetadata(screenName);
2586
3016
  return Array.isArray(meta?.actions) && meta.actions.some(
2587
3017
  (action) => typeof action === "object" && action !== null && action.type === "submit"
2588
3018
  );
@@ -2615,7 +3045,7 @@ function resolveFormScreensForSubmit(screen, activePath, registry) {
2615
3045
  } else {
2616
3046
  push(screen);
2617
3047
  }
2618
- push(chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0);
3048
+ push(chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0);
2619
3049
  const withSubmit = [];
2620
3050
  const rest = [];
2621
3051
  for (const name of ordered) {
@@ -2633,7 +3063,7 @@ function collectSubmitTargetCandidates(screens, registry) {
2633
3063
  candidates.push(id);
2634
3064
  };
2635
3065
  for (const screenName of screens) {
2636
- const meta = chunkKZ2BLMS4_js.getScreenMetadata(screenName);
3066
+ const meta = chunkUZ2D4BVX_js.getScreenMetadata(screenName);
2637
3067
  const submitMeta = Array.isArray(meta?.actions) ? meta.actions.find(
2638
3068
  (action) => typeof action === "object" && action !== null && action.type === "submit"
2639
3069
  ) : void 0;
@@ -2677,8 +3107,8 @@ function readVisibleValidation(fieldId, label) {
2677
3107
  return void 0;
2678
3108
  }
2679
3109
  function resolveScreenContext() {
2680
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
2681
- const screen = activePath[activePath.length - 1] ?? chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0;
3110
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
3111
+ const screen = activePath[activePath.length - 1] ?? chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0;
2682
3112
  return { screen, activePath };
2683
3113
  }
2684
3114
  function selectValueApplied(fieldId, requestedValue, currentValue, registry) {
@@ -2686,36 +3116,36 @@ function selectValueApplied(fieldId, requestedValue, currentValue, registry) {
2686
3116
  if (!requested) return false;
2687
3117
  const meta = getMetadataField(fieldId, registry);
2688
3118
  const options = Array.isArray(meta?.options) ? meta.options : [];
2689
- const requestedNorm = chunkKZ2BLMS4_js.normalize3(requested);
2690
- const currentNorm = chunkKZ2BLMS4_js.normalize3(currentValue);
3119
+ const requestedNorm = chunkUZ2D4BVX_js.normalize3(requested);
3120
+ const currentNorm = chunkUZ2D4BVX_js.normalize3(currentValue);
2691
3121
  if (currentNorm === requestedNorm) return true;
2692
3122
  for (const option of options) {
2693
3123
  const value = typeof option?.value === "string" ? option.value : "";
2694
3124
  const label = typeof option?.label === "string" ? option.label : "";
2695
- if (chunkKZ2BLMS4_js.normalize3(value) === requestedNorm && chunkKZ2BLMS4_js.normalize3(label) === currentNorm) return true;
2696
- if (chunkKZ2BLMS4_js.normalize3(label) === requestedNorm && chunkKZ2BLMS4_js.normalize3(label) === currentNorm) return true;
2697
- if (chunkKZ2BLMS4_js.normalize3(value) === requestedNorm && chunkKZ2BLMS4_js.normalize3(value) === currentNorm) return true;
3125
+ if (chunkUZ2D4BVX_js.normalize3(value) === requestedNorm && chunkUZ2D4BVX_js.normalize3(label) === currentNorm) return true;
3126
+ if (chunkUZ2D4BVX_js.normalize3(label) === requestedNorm && chunkUZ2D4BVX_js.normalize3(label) === currentNorm) return true;
3127
+ if (chunkUZ2D4BVX_js.normalize3(value) === requestedNorm && chunkUZ2D4BVX_js.normalize3(value) === currentNorm) return true;
2698
3128
  }
2699
3129
  return false;
2700
3130
  }
2701
3131
  function getMetadataField(fieldId, registry) {
2702
3132
  const screens = /* @__PURE__ */ new Set();
2703
- for (const name of chunkKZ2BLMS4_js.getActiveRouteNames()) screens.add(name);
2704
- const current = chunkKZ2BLMS4_js.getCurrentScreen();
2705
- if (current) screens.add(current);
3133
+ for (const name of chunkUZ2D4BVX_js.getActiveRouteNames()) screens.add(name);
3134
+ const current2 = chunkUZ2D4BVX_js.getCurrentScreen();
3135
+ if (current2) screens.add(current2);
2706
3136
  if (registry) {
2707
3137
  for (const comp of registry.snapshot()) {
2708
3138
  if (comp.kind === "field" && comp.screen) screens.add(comp.screen);
2709
3139
  }
2710
3140
  }
2711
- const needle = chunkKZ2BLMS4_js.normalize3(fieldId);
3141
+ const needle = chunkUZ2D4BVX_js.normalize3(fieldId);
2712
3142
  for (const screenName of screens) {
2713
- const screenMeta = chunkKZ2BLMS4_js.getScreenMetadata(screenName);
3143
+ const screenMeta = chunkUZ2D4BVX_js.getScreenMetadata(screenName);
2714
3144
  const fields = Array.isArray(screenMeta?.fields) ? screenMeta.fields : [];
2715
3145
  const match = fields.find((field) => {
2716
3146
  if (!field || typeof field !== "object") return false;
2717
- const id = typeof field.id === "string" ? chunkKZ2BLMS4_js.normalize3(field.id) : "";
2718
- const label = typeof field.label === "string" ? chunkKZ2BLMS4_js.normalize3(field.label) : "";
3147
+ const id = typeof field.id === "string" ? chunkUZ2D4BVX_js.normalize3(field.id) : "";
3148
+ const label = typeof field.label === "string" ? chunkUZ2D4BVX_js.normalize3(field.label) : "";
2719
3149
  return id === needle || label === needle;
2720
3150
  });
2721
3151
  if (match) return match;
@@ -2727,11 +3157,11 @@ function resolveSelectOption(fieldId, value, registry) {
2727
3157
  if (!requested) return void 0;
2728
3158
  const meta = getMetadataField(fieldId, registry);
2729
3159
  const options = Array.isArray(meta?.options) ? meta.options : [];
2730
- const requestedNorm = chunkKZ2BLMS4_js.normalize3(requested);
3160
+ const requestedNorm = chunkUZ2D4BVX_js.normalize3(requested);
2731
3161
  for (const option of options) {
2732
3162
  const optionValue = typeof option?.value === "string" ? option.value : "";
2733
3163
  const optionLabel = typeof option?.label === "string" ? option.label : "";
2734
- if (chunkKZ2BLMS4_js.normalize3(optionValue) === requestedNorm || chunkKZ2BLMS4_js.normalize3(optionLabel) === requestedNorm) {
3164
+ if (chunkUZ2D4BVX_js.normalize3(optionValue) === requestedNorm || chunkUZ2D4BVX_js.normalize3(optionLabel) === requestedNorm) {
2735
3165
  return { value: optionValue, label: optionLabel };
2736
3166
  }
2737
3167
  }
@@ -2743,7 +3173,7 @@ async function tryOpenSelectModal(fieldId, registry) {
2743
3173
  if (!target) continue;
2744
3174
  try {
2745
3175
  target.press();
2746
- await sleep2(180);
3176
+ await sleep3(180);
2747
3177
  return true;
2748
3178
  } catch {
2749
3179
  }
@@ -2781,12 +3211,12 @@ async function tryPressSelectOption(fieldId, value, registry, options = {}) {
2781
3211
  return {
2782
3212
  success: false,
2783
3213
  error: `No pressable option found for select field "${fieldId}"`,
2784
- diagnose: { category: "component-not-found", fieldId, screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0 }
3214
+ diagnose: { category: "component-not-found", fieldId, screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0 }
2785
3215
  };
2786
3216
  }
2787
3217
  function normalizeFieldType(type) {
2788
3218
  if (typeof type !== "string") return void 0;
2789
- const normalized = chunkKZ2BLMS4_js.normalize3(type);
3219
+ const normalized = chunkUZ2D4BVX_js.normalize3(type);
2790
3220
  if (normalized === "select" || normalized === "picker" || normalized === "dropdown")
2791
3221
  return "select";
2792
3222
  if (normalized === "toggle" || normalized === "switch" || normalized === "checkbox")
@@ -2816,7 +3246,7 @@ function fieldCueTerms(fieldId, label) {
2816
3246
  );
2817
3247
  const terms = /* @__PURE__ */ new Set();
2818
3248
  for (const value of raw) {
2819
- const normalized = chunkKZ2BLMS4_js.normalize3(value);
3249
+ const normalized = chunkUZ2D4BVX_js.normalize3(value);
2820
3250
  if (normalized.length >= 3 && normalized !== "id") terms.add(normalized);
2821
3251
  }
2822
3252
  return [...terms];
@@ -2824,17 +3254,17 @@ function fieldCueTerms(fieldId, label) {
2824
3254
  function snapshotHasFieldCue(snap, fieldId, label) {
2825
3255
  const terms = fieldCueTerms(fieldId, label);
2826
3256
  if (terms.length === 0) return false;
2827
- const visible = [...snap.texts ?? [], ...(snap.buttons ?? []).map((b) => b.label ?? b.id ?? "")].map((text) => chunkKZ2BLMS4_js.normalize3(text)).filter(Boolean);
3257
+ const visible = [...snap.texts ?? [], ...(snap.buttons ?? []).map((b) => b.label ?? b.id ?? "")].map((text) => chunkUZ2D4BVX_js.normalize3(text)).filter(Boolean);
2828
3258
  return visible.some((text) => terms.some((term) => text.includes(term) || term.includes(text)));
2829
3259
  }
2830
3260
  function scoreCandidateText(value, texts) {
2831
- const valueNorm = chunkKZ2BLMS4_js.normalize3(String(value ?? ""));
3261
+ const valueNorm = chunkUZ2D4BVX_js.normalize3(String(value ?? ""));
2832
3262
  if (!valueNorm) return 0;
2833
3263
  let best = 0;
2834
3264
  for (const text of texts) {
2835
- const textNorm = chunkKZ2BLMS4_js.normalize3(text);
3265
+ const textNorm = chunkUZ2D4BVX_js.normalize3(text);
2836
3266
  if (!textNorm) continue;
2837
- best = Math.max(best, chunkKZ2BLMS4_js.matchScore(valueNorm, textNorm));
3267
+ best = Math.max(best, chunkUZ2D4BVX_js.matchScore(valueNorm, textNorm));
2838
3268
  }
2839
3269
  return best;
2840
3270
  }
@@ -2848,7 +3278,7 @@ function optionTexts(option) {
2848
3278
  function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue, registry) {
2849
3279
  const fieldLabel = getFieldLabel(fieldId, registry);
2850
3280
  const fieldCueVisible = snapshotHasFieldCue(snap, fieldId, fieldLabel);
2851
- const genericValue = chunkKZ2BLMS4_js.isGenericSelectValue(value);
3281
+ const genericValue = chunkUZ2D4BVX_js.isGenericSelectValue(value);
2852
3282
  const candidates = [];
2853
3283
  let order = 0;
2854
3284
  const choiceGroups = snap.choiceGroups ?? [];
@@ -2860,7 +3290,7 @@ function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue,
2860
3290
  const groupCueScore = Math.max(
2861
3291
  globalCueScore,
2862
3292
  ...groupTexts.map((text) => {
2863
- const normalized = chunkKZ2BLMS4_js.normalize3(text);
3293
+ const normalized = chunkUZ2D4BVX_js.normalize3(text);
2864
3294
  return fieldCueTerms(fieldId, fieldLabel).some(
2865
3295
  (term) => normalized.includes(term) || term.includes(normalized)
2866
3296
  ) ? 80 : 0;
@@ -2897,7 +3327,7 @@ function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue,
2897
3327
  const elementCueScore = Math.max(
2898
3328
  fieldCueVisible && elements.length <= 12 ? 50 : 0,
2899
3329
  ...cueTexts.map((text) => {
2900
- const normalized = chunkKZ2BLMS4_js.normalize3(text);
3330
+ const normalized = chunkUZ2D4BVX_js.normalize3(text);
2901
3331
  return fieldTerms.some((term) => normalized.includes(term) || term.includes(normalized)) ? 80 : 0;
2902
3332
  })
2903
3333
  );
@@ -2955,7 +3385,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
2955
3385
  return {
2956
3386
  success: false,
2957
3387
  error: err?.message ?? "Could not capture visible choices",
2958
- diagnose: { category: "unknown", fieldId, screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0 }
3388
+ diagnose: { category: "unknown", fieldId, screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0 }
2959
3389
  };
2960
3390
  }
2961
3391
  const candidates = buildChoiceCandidates(
@@ -2973,7 +3403,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
2973
3403
  diagnose: {
2974
3404
  category: "component-not-found",
2975
3405
  fieldId,
2976
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0
3406
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0
2977
3407
  }
2978
3408
  };
2979
3409
  }
@@ -2987,7 +3417,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
2987
3417
  }
2988
3418
  function isContinuationButton(label) {
2989
3419
  if (!label) return false;
2990
- return /^(next|continue|proximo|prox|continuar|avancar|avançar)$/i.test(chunkKZ2BLMS4_js.normalize3(label));
3420
+ return /^(next|continue|proximo|prox|continuar|avancar|avançar)$/i.test(chunkUZ2D4BVX_js.normalize3(label));
2991
3421
  }
2992
3422
  function pressContinuationButton(registry) {
2993
3423
  const snap = captureSnapshot();
@@ -3000,40 +3430,40 @@ function pressContinuationButton(registry) {
3000
3430
  return {
3001
3431
  success: false,
3002
3432
  error: "No enabled continuation button is visible",
3003
- diagnose: { category: "component-not-found", screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0 }
3433
+ diagnose: { category: "component-not-found", screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0 }
3004
3434
  };
3005
3435
  }
3006
3436
  return pressTargetByIdOrLabel(id, registry);
3007
3437
  }
3008
3438
  function findField(fieldId, registry) {
3009
- chunkKZ2BLMS4_js.appilotsDebugLog(`findField: searching for fieldId="${fieldId}"`);
3439
+ chunkUZ2D4BVX_js.appilotsDebugLog(`findField: searching for fieldId="${fieldId}"`);
3010
3440
  const exact = registry.getField(fieldId);
3011
3441
  if (exact) {
3012
- chunkKZ2BLMS4_js.appilotsDebugLog(`findField: FOUND exact match for fieldId="${fieldId}"`);
3442
+ chunkUZ2D4BVX_js.appilotsDebugLog(`findField: FOUND exact match for fieldId="${fieldId}"`);
3013
3443
  return exact;
3014
3444
  }
3015
3445
  const lower = fieldId.toLowerCase();
3016
3446
  const snapshot = registry.snapshot();
3017
3447
  for (const comp of snapshot) {
3018
3448
  if (comp.kind === "field" && comp.id.toLowerCase() === lower) {
3019
- chunkKZ2BLMS4_js.appilotsDebugLog(
3449
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3020
3450
  `findField: FOUND case-insensitive match: registered="${comp.id}" for searched="${fieldId}"`
3021
3451
  );
3022
3452
  return registry.getField(comp.id);
3023
3453
  }
3024
3454
  }
3025
- const currentScreenForTieBreak = chunkKZ2BLMS4_js.getCurrentScreen();
3026
- const needle = chunkKZ2BLMS4_js.normalize3(fieldId);
3455
+ const currentScreenForTieBreak = chunkUZ2D4BVX_js.getCurrentScreen();
3456
+ const needle = chunkUZ2D4BVX_js.normalize3(fieldId);
3027
3457
  let bestComp;
3028
3458
  let bestScore = 0;
3029
3459
  let bestOnCurrentScreen = false;
3030
3460
  for (const comp of snapshot) {
3031
3461
  if (comp.kind !== "field") continue;
3032
- const candidates = [chunkKZ2BLMS4_js.normalize3(comp.id)];
3033
- if (comp.label) candidates.push(chunkKZ2BLMS4_js.normalize3(comp.label));
3462
+ const candidates = [chunkUZ2D4BVX_js.normalize3(comp.id)];
3463
+ if (comp.label) candidates.push(chunkUZ2D4BVX_js.normalize3(comp.label));
3034
3464
  let candidateScore = 0;
3035
3465
  for (const cand of candidates) {
3036
- const s = chunkKZ2BLMS4_js.matchScore(needle, cand);
3466
+ const s = chunkUZ2D4BVX_js.matchScore(needle, cand);
3037
3467
  if (s > candidateScore) candidateScore = s;
3038
3468
  }
3039
3469
  if (candidateScore === 0) continue;
@@ -3046,14 +3476,14 @@ function findField(fieldId, registry) {
3046
3476
  }
3047
3477
  }
3048
3478
  if (bestComp && bestScore >= 75) {
3049
- chunkKZ2BLMS4_js.appilotsDebugLog(
3479
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3050
3480
  `findField: scored match "${fieldId}" -> "${bestComp.id}" (label="${bestComp.label}", score=${bestScore})`
3051
3481
  );
3052
3482
  return registry.getField(bestComp.id);
3053
3483
  }
3054
- const currentScreen = chunkKZ2BLMS4_js.getCurrentScreen();
3484
+ const currentScreen = chunkUZ2D4BVX_js.getCurrentScreen();
3055
3485
  if (currentScreen) {
3056
- const screenMeta = chunkKZ2BLMS4_js.getScreenMetadata(currentScreen);
3486
+ const screenMeta = chunkUZ2D4BVX_js.getScreenMetadata(currentScreen);
3057
3487
  const fields = screenMeta?.fields;
3058
3488
  if (fields && Array.isArray(fields)) {
3059
3489
  const metaField = fields.find(
@@ -3061,19 +3491,19 @@ function findField(fieldId, registry) {
3061
3491
  );
3062
3492
  if (metaField && typeof metaField === "object" && metaField.label) {
3063
3493
  const metaLabel = metaField.label;
3064
- const metaNeedle = chunkKZ2BLMS4_js.normalize3(metaLabel);
3494
+ const metaNeedle = chunkUZ2D4BVX_js.normalize3(metaLabel);
3065
3495
  let mBest;
3066
3496
  let mBestScore = 0;
3067
3497
  for (const comp of snapshot) {
3068
3498
  if (comp.kind !== "field" || !comp.label) continue;
3069
- const score = chunkKZ2BLMS4_js.matchScore(metaNeedle, chunkKZ2BLMS4_js.normalize3(comp.label));
3499
+ const score = chunkUZ2D4BVX_js.matchScore(metaNeedle, chunkUZ2D4BVX_js.normalize3(comp.label));
3070
3500
  if (score > mBestScore) {
3071
3501
  mBestScore = score;
3072
3502
  mBest = comp;
3073
3503
  }
3074
3504
  }
3075
3505
  if (mBest && mBestScore >= 60) {
3076
- chunkKZ2BLMS4_js.appilotsDebugLog(
3506
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3077
3507
  `findField: FOUND via screen metadata translation: fieldId="${fieldId}" -> metaLabel="${metaLabel}" -> registered="${mBest.id}" (score=${mBestScore})`
3078
3508
  );
3079
3509
  return registry.getField(mBest.id);
@@ -3082,34 +3512,34 @@ function findField(fieldId, registry) {
3082
3512
  }
3083
3513
  }
3084
3514
  if (bestComp && bestScore >= 40) {
3085
- chunkKZ2BLMS4_js.appilotsDebugLog(
3515
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3086
3516
  `findField: weak substring match "${fieldId}" -> "${bestComp.id}" (label="${bestComp.label}", score=${bestScore})`
3087
3517
  );
3088
3518
  return registry.getField(bestComp.id);
3089
3519
  }
3090
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3520
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3091
3521
  `findField: NOT FOUND \u2014 fieldId="${fieldId}" not in registry. Available fields: ${snapshot.filter((c) => c.kind === "field").map((c) => `"${c.id}" (label: ${c.label})`).join(", ") || "none"}`
3092
3522
  );
3093
3523
  return void 0;
3094
3524
  }
3095
3525
  function findToggle(fieldId, registry) {
3096
- chunkKZ2BLMS4_js.appilotsDebugLog(`findToggle: searching for fieldId="${fieldId}"`);
3526
+ chunkUZ2D4BVX_js.appilotsDebugLog(`findToggle: searching for fieldId="${fieldId}"`);
3097
3527
  const exact = registry.getToggle(fieldId);
3098
3528
  if (exact) {
3099
- chunkKZ2BLMS4_js.appilotsDebugLog(`findToggle: FOUND exact match for "${fieldId}"`);
3529
+ chunkUZ2D4BVX_js.appilotsDebugLog(`findToggle: FOUND exact match for "${fieldId}"`);
3100
3530
  return exact;
3101
3531
  }
3102
3532
  const lower = fieldId.toLowerCase();
3103
3533
  const snapshot = registry.snapshot();
3104
3534
  for (const comp of snapshot) {
3105
3535
  if (comp.kind === "toggle" && comp.id.toLowerCase() === lower) {
3106
- chunkKZ2BLMS4_js.appilotsDebugLog(
3536
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3107
3537
  `findToggle: FOUND case-insensitive match: registered="${comp.id}" for searched="${fieldId}"`
3108
3538
  );
3109
3539
  return registry.getToggle(comp.id);
3110
3540
  }
3111
3541
  }
3112
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3542
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3113
3543
  `findToggle: NOT FOUND \u2014 fieldId="${fieldId}" not in registry. Available toggles: ${snapshot.filter((c) => c.kind === "toggle").map((c) => `"${c.id}"`).join(", ") || "none"}`
3114
3544
  );
3115
3545
  return void 0;
@@ -3120,7 +3550,7 @@ function findSlider(fieldId, registry) {
3120
3550
  const lower = fieldId.toLowerCase();
3121
3551
  for (const comp of registry.snapshot()) {
3122
3552
  if (comp.kind === "slider" && comp.id.toLowerCase() === lower) {
3123
- chunkKZ2BLMS4_js.appilotsDebugLog(
3553
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3124
3554
  `findSlider: FOUND case-insensitive match: registered="${comp.id}" for searched="${fieldId}"`
3125
3555
  );
3126
3556
  return registry.getSlider(comp.id);
@@ -3128,17 +3558,17 @@ function findSlider(fieldId, registry) {
3128
3558
  }
3129
3559
  return void 0;
3130
3560
  }
3131
- var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3561
+ var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3132
3562
  async function formFillHandler(payload, context) {
3133
3563
  const { permissions } = context;
3134
- const registry = context.registry ?? chunkKZ2BLMS4_js.componentRegistry;
3564
+ const registry = context.registry ?? chunkUZ2D4BVX_js.componentRegistry;
3135
3565
  const { screen, activePath } = resolveScreenContext();
3136
3566
  const screenPath = activePath.length > 1 ? activePath.join("/") : screen;
3137
3567
  if (!permissions.canFillForms) {
3138
3568
  return {
3139
3569
  success: false,
3140
- error: "Form filling is not permitted",
3141
- diagnose: { category: "unknown", screen: screenPath }
3570
+ error: chunkUZ2D4BVX_js.permissionDeniedMessage("Form filling", "canFillForms"),
3571
+ diagnose: { category: "unknown", screen: screenPath, recoverable: false }
3142
3572
  };
3143
3573
  }
3144
3574
  if (!payload.fields || payload.fields.length === 0) {
@@ -3150,11 +3580,11 @@ async function formFillHandler(payload, context) {
3150
3580
  }
3151
3581
  const registrySnapshot = registry.snapshot();
3152
3582
  if (registrySnapshot.length === 0) {
3153
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3583
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3154
3584
  "formFillHandler: registry is EMPTY \u2014 auto-tracking may not be active. Ensure enableAppilotsAutoTracking() runs before components mount."
3155
3585
  );
3156
3586
  } else {
3157
- chunkKZ2BLMS4_js.appilotsDebugLog(
3587
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3158
3588
  `[Appilots formFillHandler] Registry has ${registrySnapshot.length} components:`,
3159
3589
  registrySnapshot.map((c) => `${c.kind}:${c.id}`).join(", ")
3160
3590
  );
@@ -3162,7 +3592,7 @@ async function formFillHandler(payload, context) {
3162
3592
  const fieldResults = [];
3163
3593
  let allSuccess = true;
3164
3594
  let selectedChoiceThisBatch = false;
3165
- chunkKZ2BLMS4_js.appilotsDebugLog(`formFillHandler: processing ${payload.fields.length} fields`);
3595
+ chunkUZ2D4BVX_js.appilotsDebugLog(`formFillHandler: processing ${payload.fields.length} fields`);
3166
3596
  const availableFieldIds = () => registry.snapshot().filter((comp) => comp.kind === "field").map((comp) => `${comp.id}${comp.label ? ` (${comp.label})` : ""}`).slice(0, 8);
3167
3597
  const isElementIdField = (field) => typeof field.fieldId === "string" && field.fieldId.trim().toLowerCase().startsWith("el:");
3168
3598
  for (const field of payload.fields.filter(isElementIdField)) {
@@ -3189,20 +3619,57 @@ async function formFillHandler(payload, context) {
3189
3619
  const { fieldId, fieldType, value } = field;
3190
3620
  const effectiveFieldType = getEffectiveFieldType(fieldId, fieldType, registry);
3191
3621
  let selectFailure;
3192
- chunkKZ2BLMS4_js.appilotsDebugLog(
3193
- `formFillHandler: looking up fieldId="${fieldId}" fieldType="${fieldType}" effectiveFieldType="${effectiveFieldType}" value=${chunkKZ2BLMS4_js.describeValue(value)}`
3622
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3623
+ `formFillHandler: looking up fieldId="${fieldId}" fieldType="${fieldType}" effectiveFieldType="${effectiveFieldType}" value=${chunkUZ2D4BVX_js.describeValue(value)}`
3194
3624
  );
3195
3625
  const fieldEntry = findField(fieldId, registry);
3196
3626
  if (fieldEntry) {
3197
3627
  try {
3198
- chunkKZ2BLMS4_js.appilotsDebugLog(
3628
+ const nativeField = findInteractiveByIdentifier(fieldId, { kind: "field" });
3629
+ if (nativeField?.disabled || (nativeField?.matchCount ?? 1) > 1) {
3630
+ fieldResults.push({
3631
+ fieldId,
3632
+ success: false,
3633
+ error: nativeField?.disabled ? "The field is disabled" : "The field identifier matches multiple controls",
3634
+ diagnose: {
3635
+ category: nativeField?.disabled ? "disabled" : "ambiguous-target",
3636
+ fieldId,
3637
+ screen: screenPath
3638
+ }
3639
+ });
3640
+ allSuccess = false;
3641
+ continue;
3642
+ }
3643
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3199
3644
  `formFillHandler: found field "${fieldId}" via registry, calling setValue(<${String(value).length} chars>)`
3200
3645
  );
3201
3646
  const beforeValue = effectiveFieldType === "date" ? fieldEntry.getValue?.() ?? "" : void 0;
3202
3647
  fieldEntry.focus?.();
3203
3648
  fieldEntry.setValue(String(value));
3649
+ if (effectiveFieldType === "text" || effectiveFieldType === "number") {
3650
+ if (!await completeTextEdit(
3651
+ fieldId,
3652
+ value,
3653
+ fieldEntry,
3654
+ nativeField,
3655
+ () => findField(fieldId, registry)
3656
+ )) {
3657
+ fieldResults.push({
3658
+ fieldId,
3659
+ success: false,
3660
+ error: "The field did not retain the requested value after editing ended. Inspect its current value before retrying.",
3661
+ diagnose: { category: "validation", fieldId, screen: screenPath }
3662
+ });
3663
+ allSuccess = false;
3664
+ continue;
3665
+ }
3666
+ }
3204
3667
  if (effectiveFieldType === "date") {
3205
- const afterValue = fieldEntry.getValue?.() ?? "";
3668
+ let afterValue = fieldEntry.getValue?.() ?? "";
3669
+ if (afterValue === beforeValue) {
3670
+ await sleep3(80);
3671
+ afterValue = fieldEntry.getValue?.() ?? "";
3672
+ }
3206
3673
  if (afterValue === beforeValue) {
3207
3674
  fieldResults.push({
3208
3675
  fieldId,
@@ -3222,11 +3689,11 @@ async function formFillHandler(payload, context) {
3222
3689
  if (effectiveFieldType === "select") {
3223
3690
  let after = fieldEntry.getValue?.() ?? "";
3224
3691
  if (!selectValueApplied(fieldId, value, after, registry)) {
3225
- await sleep2(80);
3692
+ await sleep3(80);
3226
3693
  after = fieldEntry.getValue?.() ?? "";
3227
3694
  }
3228
3695
  if (!selectValueApplied(fieldId, value, after, registry)) {
3229
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3696
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3230
3697
  `formFillHandler: select field "${fieldId}" setValue did not stick (wrote <${String(value).length} chars>, current is <${String(after ?? "").length} chars>), trying visible choice fallback`
3231
3698
  );
3232
3699
  const selectResult = await selectVisibleChoice(fieldId, value, registry, {
@@ -3343,7 +3810,7 @@ async function formFillHandler(payload, context) {
3343
3810
  if (!selectResult.success && selectedChoiceThisBatch) {
3344
3811
  const nextResult = pressContinuationButton(registry);
3345
3812
  if (nextResult.success) {
3346
- await sleep2(180);
3813
+ await sleep3(180);
3347
3814
  selectResult = await selectVisibleChoice(fieldId, value, registry, {
3348
3815
  allowFirstVisibleWhenNoCue: true
3349
3816
  });
@@ -3351,24 +3818,50 @@ async function formFillHandler(payload, context) {
3351
3818
  }
3352
3819
  if (selectResult.success) {
3353
3820
  selectedChoiceThisBatch = true;
3354
- chunkKZ2BLMS4_js.appilotsDebugLog(
3821
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3355
3822
  `formFillHandler: selected visible option for "${fieldId}"` + (selectResult.selectedLabel ? ` \u2192 "${selectResult.selectedLabel}"` : "")
3356
3823
  );
3357
- await sleep2(120);
3824
+ await sleep3(120);
3358
3825
  fieldResults.push({ fieldId, success: true });
3359
3826
  continue;
3360
3827
  }
3361
3828
  selectFailure = selectResult;
3362
3829
  }
3363
- chunkKZ2BLMS4_js.appilotsDebugLog(`formFillHandler: registry miss for "${fieldId}", falling back to fiber tree`);
3830
+ chunkUZ2D4BVX_js.appilotsDebugLog(`formFillHandler: registry miss for "${fieldId}", falling back to fiber tree`);
3364
3831
  const handle = findInteractiveByIdentifier(fieldId, {
3365
3832
  kind: effectiveFieldType === "toggle" ? "toggle" : "field"
3366
3833
  });
3367
3834
  if (handle?.setValue) {
3368
3835
  try {
3836
+ if (handle.disabled || (handle.matchCount ?? 1) > 1) {
3837
+ fieldResults.push({
3838
+ fieldId,
3839
+ success: false,
3840
+ error: handle.disabled ? "The field is disabled" : "The field identifier matches multiple controls",
3841
+ diagnose: {
3842
+ category: handle.disabled ? "disabled" : "ambiguous-target",
3843
+ fieldId,
3844
+ screen: screenPath,
3845
+ candidates: handle.matchCandidates
3846
+ }
3847
+ });
3848
+ allSuccess = false;
3849
+ continue;
3850
+ }
3851
+ setSpotlightTarget(handle.measure?.() ?? null, handle.label);
3369
3852
  handle.focus?.();
3370
3853
  handle.setValue(value);
3371
- chunkKZ2BLMS4_js.appilotsDebugLog(
3854
+ if (handle.kind === "field" && !await completeTextEdit(fieldId, value, void 0, handle)) {
3855
+ fieldResults.push({
3856
+ fieldId,
3857
+ success: false,
3858
+ error: "The field did not retain the requested value after editing ended. Inspect its current value before retrying.",
3859
+ diagnose: { category: "validation", fieldId, screen: screenPath }
3860
+ });
3861
+ allSuccess = false;
3862
+ continue;
3863
+ }
3864
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3372
3865
  `formFillHandler: fiber fallback succeeded for "${fieldId}" \u2192 label="${handle.label}"`
3373
3866
  );
3374
3867
  fieldResults.push({ fieldId, success: true });
@@ -3405,13 +3898,25 @@ async function formFillHandler(payload, context) {
3405
3898
  allSuccess = false;
3406
3899
  }
3407
3900
  if (payload.submitAfterFill === true) {
3901
+ if (!permissions.canSubmitForms) {
3902
+ return {
3903
+ success: false,
3904
+ error: "Form submission is not permitted",
3905
+ fieldResults,
3906
+ diagnose: { category: "unknown", screen: screenPath }
3907
+ };
3908
+ }
3408
3909
  if (!allSuccess) {
3409
3910
  const failedIds = fieldResults.filter((result) => !result.success).map((result) => result.fieldId);
3410
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3911
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3411
3912
  "formFillHandler: submitAfterFill=true requested but some fields failed; NOT submitting" + (failedIds.length > 0 ? ` (failed: ${failedIds.join(", ")})` : "")
3412
3913
  );
3413
3914
  } else {
3414
- await sleep2(120);
3915
+ await sleep3(120);
3916
+ if (!permissions.canSubmitForms) {
3917
+ return { success: false, error: "Form submission is not permitted", fieldResults };
3918
+ }
3919
+ const beforeSubmitTexts = captureSnapshot().texts;
3415
3920
  const submitResult = pressSubmitButton(
3416
3921
  {
3417
3922
  screen,
@@ -3422,7 +3927,7 @@ async function formFillHandler(payload, context) {
3422
3927
  registry
3423
3928
  );
3424
3929
  if (!submitResult.success) {
3425
- chunkKZ2BLMS4_js.appilotsDebugWarn(`formFillHandler: submitAfterFill failed \u2014 ${submitResult.error}`);
3930
+ chunkUZ2D4BVX_js.appilotsDebugWarn(`formFillHandler: submitAfterFill failed \u2014 ${submitResult.error}`);
3426
3931
  return {
3427
3932
  success: false,
3428
3933
  error: submitResult.error,
@@ -3431,10 +3936,11 @@ async function formFillHandler(payload, context) {
3431
3936
  };
3432
3937
  }
3433
3938
  const postSubmit = await waitForPostSubmitOutcome({
3434
- submitTargetId: submitResult.pressedTargetId
3939
+ submitTargetId: submitResult.pressedTargetId,
3940
+ beforeTexts: beforeSubmitTexts
3435
3941
  });
3436
3942
  if (postSubmit.status === "pending") {
3437
- chunkKZ2BLMS4_js.appilotsDebugWarn(`formFillHandler: submit still pending \u2014 ${postSubmit.message}`);
3943
+ chunkUZ2D4BVX_js.appilotsDebugWarn(`formFillHandler: submit still pending \u2014 ${postSubmit.message}`);
3438
3944
  return {
3439
3945
  success: false,
3440
3946
  error: postSubmit.message,
@@ -3443,7 +3949,7 @@ async function formFillHandler(payload, context) {
3443
3949
  };
3444
3950
  }
3445
3951
  if (postSubmit.status === "failure") {
3446
- chunkKZ2BLMS4_js.appilotsDebugWarn(
3952
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3447
3953
  `formFillHandler: submit rejected by visible validation \u2014 ${postSubmit.message}`
3448
3954
  );
3449
3955
  return {
@@ -3458,7 +3964,10 @@ async function formFillHandler(payload, context) {
3458
3964
  }
3459
3965
  };
3460
3966
  }
3461
- chunkKZ2BLMS4_js.appilotsDebugLog("formFillHandler: submitAfterFill \u2014 submit completed");
3967
+ if (postSubmit.status === "unverified") {
3968
+ return { success: true, fieldResults, effect: "unknown" };
3969
+ }
3970
+ chunkUZ2D4BVX_js.appilotsDebugLog("formFillHandler: submitAfterFill \u2014 submit completed");
3462
3971
  }
3463
3972
  }
3464
3973
  const aggregatedDiagnose = aggregateFieldDiagnoses(fieldResults, screenPath);
@@ -3470,17 +3979,17 @@ async function formFillHandler(payload, context) {
3470
3979
  };
3471
3980
  }
3472
3981
  function pressSubmitButton(context, registry) {
3473
- const activePath = context.activePath ?? chunkKZ2BLMS4_js.getActiveRouteNames();
3982
+ const activePath = context.activePath ?? chunkUZ2D4BVX_js.getActiveRouteNames();
3474
3983
  const hintScreen = context.screen ?? context.screenPath;
3475
3984
  const formScreens = resolveFormScreensForSubmit(hintScreen, activePath, registry);
3476
- const resolvedScreen = formScreens.find((name) => screenDeclaresSubmit(name)) ?? formScreens[0] ?? hintScreen ?? chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0;
3985
+ const resolvedScreen = formScreens.find((name) => screenDeclaresSubmit(name)) ?? formScreens[0] ?? hintScreen ?? chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0;
3477
3986
  const candidates = collectSubmitTargetCandidates(formScreens, registry);
3478
3987
  for (const candidate of candidates) {
3479
3988
  const target = registry.getTarget(candidate);
3480
3989
  if (target) {
3481
3990
  try {
3482
3991
  target.press();
3483
- chunkKZ2BLMS4_js.appilotsDebugLog(
3992
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3484
3993
  `formFillHandler: submitAfterFill \u2014 pressed registry target "${candidate}"` + (resolvedScreen ? ` on screen "${resolvedScreen}"` : "")
3485
3994
  );
3486
3995
  return { success: true, pressedTargetId: candidate };
@@ -3494,7 +4003,7 @@ function pressSubmitButton(context, registry) {
3494
4003
  }
3495
4004
  }
3496
4005
  for (const screenName of formScreens) {
3497
- const screenMeta = chunkKZ2BLMS4_js.getScreenMetadata(screenName);
4006
+ const screenMeta = chunkUZ2D4BVX_js.getScreenMetadata(screenName);
3498
4007
  const submitMeta = Array.isArray(screenMeta?.actions) ? screenMeta.actions.find(
3499
4008
  (action) => typeof action === "object" && action !== null && action.type === "submit"
3500
4009
  ) : void 0;
@@ -3505,7 +4014,7 @@ function pressSubmitButton(context, registry) {
3505
4014
  if (handle?.press) {
3506
4015
  try {
3507
4016
  handle.press();
3508
- chunkKZ2BLMS4_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 fiber fallback "${submitLabel}"`);
4017
+ chunkUZ2D4BVX_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 fiber fallback "${submitLabel}"`);
3509
4018
  return { success: true, pressedTargetId: submitId ?? submitLabel };
3510
4019
  } catch (err) {
3511
4020
  return {
@@ -3527,7 +4036,7 @@ function pressSubmitButton(context, registry) {
3527
4036
  if (id) {
3528
4037
  const pressed = pressTargetByIdOrLabel(id, registry);
3529
4038
  if (pressed.success) {
3530
- chunkKZ2BLMS4_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 pressed visible heuristic "${id}"`);
4039
+ chunkUZ2D4BVX_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 pressed visible heuristic "${id}"`);
3531
4040
  return { success: true, pressedTargetId: id };
3532
4041
  }
3533
4042
  }
@@ -3541,7 +4050,7 @@ function pressSubmitButton(context, registry) {
3541
4050
  if (target) {
3542
4051
  try {
3543
4052
  target.press();
3544
- chunkKZ2BLMS4_js.appilotsDebugLog(
4053
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3545
4054
  `formFillHandler: submitAfterFill \u2014 pressed by label heuristic "${comp.label}"`
3546
4055
  );
3547
4056
  return { success: true, pressedTargetId: comp.id };
@@ -3557,7 +4066,7 @@ function pressSubmitButton(context, registry) {
3557
4066
  for (const fieldId of [...context.filledFieldIds ?? []].reverse()) {
3558
4067
  const handle = findInteractiveByIdentifier(fieldId, { kind: "field" });
3559
4068
  if (handle?.submitFromKeyboard?.()) {
3560
- chunkKZ2BLMS4_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 return key on "${fieldId}"`);
4069
+ chunkUZ2D4BVX_js.appilotsDebugLog(`formFillHandler: submitAfterFill \u2014 return key on "${fieldId}"`);
3561
4070
  return { success: true, pressedTargetId: fieldId };
3562
4071
  }
3563
4072
  }
@@ -3595,7 +4104,21 @@ function aggregateFieldDiagnoses(results, screen) {
3595
4104
  var FALLBACK_PAGE_PX = 600;
3596
4105
  var PAGE_FRACTION = 0.85;
3597
4106
  var POST_SCROLL_MOUNT_MS = 400;
3598
- var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
4107
+ var PAGINATION_MAX_WAIT_MS = 4e3;
4108
+ var PAGINATION_POLL_MS = 150;
4109
+ var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
4110
+ async function settleAfterScroll() {
4111
+ await sleep4(POST_SCROLL_MOUNT_MS);
4112
+ if (!chunkUZ2D4BVX_js.probeLoadingState(void 0, chunkUZ2D4BVX_js.getCurrentScreen()).loading) return;
4113
+ const deadline = Date.now() + PAGINATION_MAX_WAIT_MS;
4114
+ while (Date.now() < deadline) {
4115
+ await sleep4(PAGINATION_POLL_MS);
4116
+ if (!chunkUZ2D4BVX_js.probeLoadingState(void 0, chunkUZ2D4BVX_js.getCurrentScreen()).loading) {
4117
+ await sleep4(POST_SCROLL_MOUNT_MS);
4118
+ return;
4119
+ }
4120
+ }
4121
+ }
3599
4122
  function normalizeId2(value) {
3600
4123
  return String(value ?? "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "");
3601
4124
  }
@@ -3603,12 +4126,12 @@ function buildDiagnose(category, targetId, candidates) {
3603
4126
  return {
3604
4127
  category,
3605
4128
  targetId,
3606
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0,
4129
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0,
3607
4130
  ...candidates && candidates.length > 0 ? { candidates: candidates.slice(0, 8) } : {}
3608
4131
  };
3609
4132
  }
3610
4133
  function resolveListEntry(listId) {
3611
- const entries = chunkKZ2BLMS4_js.listRegistry.snapshot();
4134
+ const entries = chunkUZ2D4BVX_js.listRegistry.snapshot();
3612
4135
  if (entries.length === 0) return {};
3613
4136
  if (listId) {
3614
4137
  const wanted = normalizeId2(listId);
@@ -3631,13 +4154,26 @@ function pageOffset(metrics, direction) {
3631
4154
  }
3632
4155
  return next;
3633
4156
  }
4157
+ async function finishScroll(entry, before) {
4158
+ await settleAfterScroll();
4159
+ const current2 = chunkUZ2D4BVX_js.listRegistry.get(entry.id) ?? entry;
4160
+ const after = current2.getScrollMetrics?.();
4161
+ const grew = typeof entry.itemCount === "number" && typeof current2.itemCount === "number" && current2.itemCount > entry.itemCount;
4162
+ const moved = grew || !!(before && after && (Math.abs(after.offset - before.offset) > 2 || after.contentLength > before.contentLength + 2));
4163
+ const outcome = moved ? "moved" : before && after ? "no-progress" : "unverified";
4164
+ current2.exploration?.recordScroll(outcome);
4165
+ return {
4166
+ success: true,
4167
+ effect: moved ? "changed" : outcome === "no-progress" ? "none" : "unknown"
4168
+ };
4169
+ }
3634
4170
  async function scrollListHandler(payload, context) {
3635
4171
  const { permissions } = context;
3636
4172
  if (!permissions.canInteractUI) {
3637
4173
  return {
3638
4174
  success: false,
3639
- error: "UI interaction is not permitted",
3640
- diagnose: buildDiagnose("unknown", payload.listId)
4175
+ error: chunkUZ2D4BVX_js.permissionDeniedMessage("UI interaction", "canInteractUI"),
4176
+ diagnose: { ...buildDiagnose("unknown", payload.listId), recoverable: false }
3641
4177
  };
3642
4178
  }
3643
4179
  const toIndex = typeof payload.toIndex === "number" ? payload.toIndex : void 0;
@@ -3665,6 +4201,15 @@ async function scrollListHandler(payload, context) {
3665
4201
  diagnose: buildDiagnose("component-not-found", payload.listId, candidates)
3666
4202
  };
3667
4203
  }
4204
+ const exploration = entry.exploration?.snapshot();
4205
+ if (exploration && (exploration.remainingScrollSteps === 0 || exploration.consecutiveNoProgress >= 2)) {
4206
+ return {
4207
+ success: false,
4208
+ effect: "none",
4209
+ error: "List exploration stopped: scroll budget exhausted or repeated no progress. Use the current loaded count/coverage and report any remaining uncertainty; do not repeat this scroll.",
4210
+ diagnose: { ...buildDiagnose("validation", entry.id), recoverable: false }
4211
+ };
4212
+ }
3668
4213
  if (toIndex !== void 0) {
3669
4214
  if (entry.kind === "scroll") {
3670
4215
  return {
@@ -3680,17 +4225,16 @@ async function scrollListHandler(payload, context) {
3680
4225
  diagnose: buildDiagnose("validation", entry.id)
3681
4226
  };
3682
4227
  }
4228
+ const before = entry.getScrollMetrics?.();
3683
4229
  const zeroBased = toIndex - 1;
3684
4230
  if (entry.scrollToIndex?.(zeroBased)) {
3685
- await sleep3(POST_SCROLL_MOUNT_MS);
3686
- return { success: true };
4231
+ return finishScroll(entry, before);
3687
4232
  }
3688
4233
  const metrics2 = entry.getScrollMetrics?.();
3689
4234
  if (metrics2 && metrics2.contentLength > 0 && typeof entry.itemCount === "number" && entry.itemCount > 0) {
3690
4235
  const estimated = zeroBased / entry.itemCount * metrics2.contentLength;
3691
4236
  if (entry.scrollToOffset?.(Math.max(0, estimated))) {
3692
- await sleep3(POST_SCROLL_MOUNT_MS);
3693
- return { success: true };
4237
+ return finishScroll(entry, before);
3694
4238
  }
3695
4239
  }
3696
4240
  return {
@@ -3700,10 +4244,21 @@ async function scrollListHandler(payload, context) {
3700
4244
  };
3701
4245
  }
3702
4246
  const metrics = entry.getScrollMetrics?.();
4247
+ if (!metrics && exploration?.lastScroll === "unverified") {
4248
+ return {
4249
+ success: false,
4250
+ effect: "unknown",
4251
+ error: "Scroll offset is still unavailable after one probe. Cannot safely page again; do not infer the end or repeat the same absolute offset.",
4252
+ diagnose: { ...buildDiagnose("validation", entry.id), recoverable: false }
4253
+ };
4254
+ }
3703
4255
  const target = pageOffset(metrics, direction);
4256
+ if (metrics && Math.abs(target - metrics.offset) <= 2) {
4257
+ entry.exploration?.recordScroll("boundary");
4258
+ return { success: true, effect: "none" };
4259
+ }
3704
4260
  if (entry.scrollToOffset?.(target)) {
3705
- await sleep3(POST_SCROLL_MOUNT_MS);
3706
- return { success: true };
4261
+ return finishScroll(entry, metrics);
3707
4262
  }
3708
4263
  return {
3709
4264
  success: false,
@@ -3717,6 +4272,59 @@ async function confirmHandler(_actionId, _payload) {
3717
4272
  return { success: true, effect: "unknown" };
3718
4273
  }
3719
4274
 
4275
+ // src/platform/asyncBudget.ts
4276
+ var POST_ACTION_LOADING_MAX_MS = 6e3;
4277
+ var INFERRED_ASYNC_LOADING_MAX_MS = 1e4;
4278
+ var POST_NAVIGATE_LOADING_MAX_MS = 3e3;
4279
+ var MAX_DECLARED_ASYNC_BUDGET_MS = 12e4;
4280
+ var ASYNC_BUSY_CEILING_MS = MAX_DECLARED_ASYNC_BUDGET_MS;
4281
+ function declaredBudgetMs(hints) {
4282
+ const declared = hints?.asyncBudgetMs;
4283
+ if (typeof declared !== "number" || !Number.isFinite(declared) || declared <= 0) return void 0;
4284
+ return Math.min(declared, MAX_DECLARED_ASYNC_BUDGET_MS);
4285
+ }
4286
+ function resolveSettleCapMs(hints) {
4287
+ const declared = declaredBudgetMs(hints);
4288
+ if (declared !== void 0) return declared;
4289
+ return hints?.isAsyncTrigger === true ? INFERRED_ASYNC_LOADING_MAX_MS : POST_ACTION_LOADING_MAX_MS;
4290
+ }
4291
+ function resolveBusyCeilingMs(hints) {
4292
+ return declaredBudgetMs(hints) === void 0 ? ASYNC_BUSY_CEILING_MS : void 0;
4293
+ }
4294
+ function resolvePostNavigateCapMs(hints) {
4295
+ return Math.max(POST_NAVIGATE_LOADING_MAX_MS, declaredBudgetMs(hints) ?? 0);
4296
+ }
4297
+ function resolveActionHints(screen, pressedComponentId, actionPayload) {
4298
+ if (actionPayload && typeof actionPayload === "object") {
4299
+ const payload = actionPayload;
4300
+ const inline = payload._appilotsInferred;
4301
+ const declared = payload._appilotsAsyncBudgetMs;
4302
+ if (inline && typeof inline === "object") {
4303
+ const hints = { ...inline };
4304
+ if (typeof declared === "number") hints.asyncBudgetMs = declared;
4305
+ return hints;
4306
+ }
4307
+ if (typeof declared === "number") return { asyncBudgetMs: declared };
4308
+ }
4309
+ if (!screen || !pressedComponentId) return void 0;
4310
+ const meta = chunkUZ2D4BVX_js.getScreenMetadata(screen);
4311
+ const actions = meta?.actions;
4312
+ if (!actions || actions.length === 0) return void 0;
4313
+ for (const a of actions) {
4314
+ if (typeof a === "string") continue;
4315
+ if (chunkUZ2D4BVX_js.idLooselyMatches(a.id, pressedComponentId)) {
4316
+ const inferred = a.appilotsInferred;
4317
+ const declared = a.asyncBudgetMs;
4318
+ const hints = {
4319
+ ...inferred && typeof inferred === "object" ? inferred : {},
4320
+ ...typeof declared === "number" ? { asyncBudgetMs: declared } : {}
4321
+ };
4322
+ return Object.keys(hints).length > 0 ? hints : void 0;
4323
+ }
4324
+ }
4325
+ return void 0;
4326
+ }
4327
+
3720
4328
  // src/executor/postInteractionSettle.ts
3721
4329
  var sleepMs = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3722
4330
  function targetIdFromPayload(payload) {
@@ -3734,12 +4342,12 @@ function hashFieldValue(value) {
3734
4342
  }
3735
4343
  return (hash >>> 0).toString(36);
3736
4344
  }
3737
- var DISCRETE_FIELD_TYPES = /* @__PURE__ */ new Set(["select", "date"]);
4345
+ var DISCRETE_FIELD_TYPES = /* @__PURE__ */ new Set(["select", "date", "number"]);
3738
4346
  function discreteFieldSignature() {
3739
4347
  const sigs = [];
3740
- for (const { id, kind } of chunkKZ2BLMS4_js.componentRegistry.snapshot()) {
4348
+ for (const { id, kind } of chunkUZ2D4BVX_js.componentRegistry.snapshot()) {
3741
4349
  if (kind !== "field") continue;
3742
- const entry = chunkKZ2BLMS4_js.componentRegistry.getField(id);
4350
+ const entry = chunkUZ2D4BVX_js.componentRegistry.getField(id);
3743
4351
  if (!entry || !DISCRETE_FIELD_TYPES.has(entry.fieldType ?? "")) continue;
3744
4352
  try {
3745
4353
  sigs.push(`${id}:${hashFieldValue(String(entry.getValue() ?? ""))}`);
@@ -3748,34 +4356,46 @@ function discreteFieldSignature() {
3748
4356
  }
3749
4357
  return sigs.sort().join(",");
3750
4358
  }
3751
- async function waitForPostInteractionSettle(payload) {
4359
+ function capturePreInteractionBaseline(payload) {
4360
+ const targetId = targetIdFromPayload(payload);
4361
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen();
4362
+ const probe = chunkUZ2D4BVX_js.probeLoadingState(targetId || null, screen);
4363
+ return {
4364
+ fingerprint: probe.fingerprint,
4365
+ fields: discreteFieldSignature(),
4366
+ screen,
4367
+ readable: probe.fingerprint !== ""
4368
+ };
4369
+ }
4370
+ async function waitForPostInteractionSettle(payload, preBaseline) {
3752
4371
  const targetId = targetIdFromPayload(payload);
3753
- const fromScreen = chunkKZ2BLMS4_js.getCurrentScreen();
3754
- const baseline = chunkKZ2BLMS4_js.probeLoadingState(targetId || null, fromScreen);
3755
- const baselineFields = discreteFieldSignature();
3756
- const readable = baseline.fingerprint !== "";
4372
+ const baseline = preBaseline ?? capturePreInteractionBaseline(payload);
4373
+ const fromScreen = baseline.screen;
4374
+ const baselineFields = baseline.fields;
4375
+ const readable = baseline.readable;
3757
4376
  await sleepMs(100);
3758
- const afterPress = chunkKZ2BLMS4_js.probeLoadingState(targetId || null, chunkKZ2BLMS4_js.getCurrentScreen());
4377
+ const afterPress = chunkUZ2D4BVX_js.probeLoadingState(targetId || null, chunkUZ2D4BVX_js.getCurrentScreen());
3759
4378
  const modalOrLoading = afterPress.modalOpen || afterPress.loading || afterPress.pressedDisabled;
3760
4379
  const fingerprintChanged = afterPress.fingerprint !== baseline.fingerprint;
3761
4380
  const verdict = () => {
3762
4381
  if (!readable) return { effect: "unknown" };
3763
- const settled = chunkKZ2BLMS4_js.probeLoadingState(targetId || null, chunkKZ2BLMS4_js.getCurrentScreen());
4382
+ const settled = chunkUZ2D4BVX_js.probeLoadingState(targetId || null, chunkUZ2D4BVX_js.getCurrentScreen());
3764
4383
  if (settled.fingerprint === "") return { effect: "unknown" };
3765
4384
  if (discreteFieldSignature() !== baselineFields) return { effect: "changed" };
3766
4385
  return { effect: settled.fingerprint !== baseline.fingerprint ? "changed" : "none" };
3767
4386
  };
3768
- const capMs = chunkKZ2BLMS4_js.isListItemPressTarget(targetId) ? 5e3 : 1e4;
4387
+ const declared = declaredBudgetMs(resolveActionHints(fromScreen, targetId || null, payload));
4388
+ const capMs = declared ?? (chunkUZ2D4BVX_js.isListItemPressTarget(targetId) ? 5e3 : 1e4);
3769
4389
  if (modalOrLoading || fingerprintChanged) {
3770
- await chunkKZ2BLMS4_js.waitForLoadingSettle({
4390
+ await chunkUZ2D4BVX_js.waitForLoadingSettle({
3771
4391
  pressedComponentId: targetId || null,
3772
4392
  fromScreen,
3773
4393
  maxMs: capMs
3774
4394
  });
3775
4395
  return verdict();
3776
4396
  }
3777
- if (chunkKZ2BLMS4_js.isListItemPressTarget(targetId)) {
3778
- await chunkKZ2BLMS4_js.waitForLoadingSettle({
4397
+ if (chunkUZ2D4BVX_js.isListItemPressTarget(targetId)) {
4398
+ await chunkUZ2D4BVX_js.waitForLoadingSettle({
3779
4399
  fromScreen,
3780
4400
  maxMs: 3e3
3781
4401
  });
@@ -3784,19 +4404,13 @@ async function waitForPostInteractionSettle(payload) {
3784
4404
  }
3785
4405
 
3786
4406
  // src/executor/ActionExecutor.ts
3787
- var DEFAULT_PERMISSIONS = {
3788
- canNavigate: true,
3789
- canFillForms: true,
3790
- canInteractUI: true,
3791
- canSubmitForms: true
3792
- };
3793
4407
  function validateActionPayload(action) {
3794
4408
  const invalid = (message, ids) => ({
3795
4409
  success: false,
3796
4410
  error: message,
3797
4411
  diagnose: {
3798
4412
  category: "validation",
3799
- screen: chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0,
4413
+ screen: chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0,
3800
4414
  ...ids ?? {}
3801
4415
  }
3802
4416
  });
@@ -3857,10 +4471,10 @@ function isDestructivePayload(payload) {
3857
4471
  return !!params && typeof params === "object" && params.destructive === true;
3858
4472
  }
3859
4473
  async function executeAction(action, context) {
3860
- chunkKZ2BLMS4_js.appilotsDebugLog(
4474
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3861
4475
  `executeAction called: type="${action.type}", id="${action.id}", payload=${JSON.stringify(action.payload)}`
3862
4476
  );
3863
- const permissions = context.permissions ?? DEFAULT_PERMISSIONS;
4477
+ const permissions = chunkUZ2D4BVX_js.resolveAgentPermissions(context.permissions);
3864
4478
  context.emit({
3865
4479
  type: "agent:action:start",
3866
4480
  timestamp: Date.now(),
@@ -3886,7 +4500,7 @@ async function executeAction(action, context) {
3886
4500
  let result;
3887
4501
  const validationFailure = validateActionPayload(action);
3888
4502
  if (validationFailure) {
3889
- chunkKZ2BLMS4_js.appilotsDebugWarn(
4503
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3890
4504
  `executeAction: payload validation failed for type="${action.type}" id="${action.id}": ${validationFailure.error}`
3891
4505
  );
3892
4506
  context.emit({
@@ -3913,17 +4527,18 @@ async function executeAction(action, context) {
3913
4527
  case "form_fill":
3914
4528
  result = await formFillHandler(action.payload, {
3915
4529
  permissions,
3916
- registry: context.registry ?? chunkKZ2BLMS4_js.getDefaultRegistry()
4530
+ registry: context.registry ?? chunkUZ2D4BVX_js.getDefaultRegistry()
3917
4531
  });
3918
4532
  break;
3919
4533
  case "ui_interaction": {
3920
4534
  const payload = action.payload;
4535
+ const baseline = capturePreInteractionBaseline(payload);
3921
4536
  result = uiInteractionHandler(payload, {
3922
4537
  permissions,
3923
- registry: context.registry ?? chunkKZ2BLMS4_js.getDefaultRegistry()
4538
+ registry: context.registry ?? chunkUZ2D4BVX_js.getDefaultRegistry()
3924
4539
  });
3925
4540
  if (result.success) {
3926
- const settle = await waitForPostInteractionSettle(payload);
4541
+ const settle = await waitForPostInteractionSettle(payload, baseline);
3927
4542
  result.effect = result.effect ?? effectFor(payload, settle.effect);
3928
4543
  }
3929
4544
  break;
@@ -3949,7 +4564,7 @@ async function executeAction(action, context) {
3949
4564
  };
3950
4565
  }
3951
4566
  } catch (err) {
3952
- chunkKZ2BLMS4_js.appilotsDebugWarn(
4567
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
3953
4568
  `executeAction: exception during action type="${action.type}" id="${action.id}": ${err?.message}`
3954
4569
  );
3955
4570
  result = {
@@ -3957,7 +4572,7 @@ async function executeAction(action, context) {
3957
4572
  error: err?.message ?? "Action execution failed unexpectedly"
3958
4573
  };
3959
4574
  }
3960
- chunkKZ2BLMS4_js.appilotsDebugLog(
4575
+ chunkUZ2D4BVX_js.appilotsDebugLog(
3961
4576
  `executeAction result: type="${action.type}", id="${action.id}", success=${result.success}, error=${result.error ?? "none"}`
3962
4577
  );
3963
4578
  context.emit({
@@ -4011,18 +4626,28 @@ var _patchedAlert = (title, message, buttons, options) => {
4011
4626
  return suppression.originalAlert(title, message, buttons, options);
4012
4627
  }
4013
4628
  suppression.consumed = true;
4014
- chunkKZ2BLMS4_js.appilotsDebugLog("Suppressing native Alert during already-confirmed destructive agent action");
4629
+ const report = (handlerCompleted) => suppression.onHandled?.({
4630
+ title: title.slice(0, 300),
4631
+ message: message?.slice(0, 1e3),
4632
+ buttonLabel: button.text?.slice(0, 160),
4633
+ handlerCompleted
4634
+ });
4635
+ chunkUZ2D4BVX_js.appilotsDebugLog("Suppressing native Alert during already-confirmed destructive agent action");
4015
4636
  try {
4016
- suppression.pending = Promise.resolve(button.onPress()).then(() => void 0).catch((err) => reportHandlerFailure(err));
4637
+ suppression.pending = Promise.resolve(button.onPress()).then(() => report(true)).catch((err) => {
4638
+ reportHandlerFailure(err);
4639
+ report(false);
4640
+ });
4017
4641
  } catch (err) {
4018
4642
  reportHandlerFailure(err);
4019
4643
  suppression.pending = null;
4644
+ report(false);
4020
4645
  }
4021
4646
  };
4022
4647
  function reportHandlerFailure(err) {
4023
4648
  console.error("[Appilots] The app\u2019s Alert handler threw while being auto-confirmed", err);
4024
4649
  }
4025
- async function runWithConfirmedDestructiveContext(enabled, host, fn) {
4650
+ async function runWithConfirmedDestructiveContext(enabled, host, fn, onHandled) {
4026
4651
  if (!enabled) return fn();
4027
4652
  if (_active) {
4028
4653
  _active.depth += 1;
@@ -4043,6 +4668,7 @@ async function runWithConfirmedDestructiveContext(enabled, host, fn) {
4043
4668
  depth: 1,
4044
4669
  consumed: false,
4045
4670
  pending: null,
4671
+ onHandled,
4046
4672
  timer: null
4047
4673
  };
4048
4674
  _active = suppression;
@@ -4062,138 +4688,153 @@ async function runWithConfirmedDestructiveContext(enabled, host, fn) {
4062
4688
  }
4063
4689
 
4064
4690
  // src/platform/reactNativeAdapter.ts
4065
- var POST_ACTION_LOADING_MAX_MS = 6e3;
4691
+ var deltaTracker = chunkUZ2D4BVX_js.createSnapshotDeltaTracker();
4066
4692
  function resolveScreenMetadata() {
4067
- const activePath = chunkKZ2BLMS4_js.getActiveRouteNames();
4693
+ const activePath = chunkUZ2D4BVX_js.getActiveRouteNames();
4068
4694
  for (let i = activePath.length - 1; i >= 0; i--) {
4069
4695
  const name = activePath[i];
4070
- const meta = name ? chunkKZ2BLMS4_js.getScreenMetadata(name) : void 0;
4696
+ const meta = name ? chunkUZ2D4BVX_js.getScreenMetadata(name) : void 0;
4071
4697
  if (meta && (Array.isArray(meta.fields) && meta.fields.length > 0 || Array.isArray(meta.actions) && meta.actions.length > 0)) {
4072
4698
  return meta;
4073
4699
  }
4074
4700
  }
4075
- const screen = chunkKZ2BLMS4_js.getCurrentScreen();
4076
- return screen ? chunkKZ2BLMS4_js.getScreenMetadata(screen) : void 0;
4701
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen();
4702
+ return screen ? chunkUZ2D4BVX_js.getScreenMetadata(screen) : void 0;
4077
4703
  }
4078
4704
  function buildAgentContext(extras = {}) {
4705
+ if (extras.newMission === true) {
4706
+ deltaTracker.reset();
4707
+ for (const entry of chunkUZ2D4BVX_js.listRegistry.snapshot()) entry.exploration?.reset();
4708
+ }
4079
4709
  const context = {};
4080
4710
  context.platform = "react-native";
4081
- const screen = chunkKZ2BLMS4_js.getCurrentScreen();
4711
+ context.missionProtocol = 1;
4712
+ context.deviceLocale = detectDeviceLocale() ?? "en";
4713
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen();
4082
4714
  if (screen) context.currentScreen = screen;
4083
- const navigationState = chunkKZ2BLMS4_js.getNavigationStateSnapshot();
4715
+ const navigationState = chunkUZ2D4BVX_js.getNavigationStateSnapshot();
4084
4716
  if (navigationState) context.navigationState = navigationState;
4085
4717
  const screenMeta = resolveScreenMetadata();
4086
4718
  if (screenMeta) context.screenMetadata = screenMeta;
4087
4719
  try {
4088
- context.snapshot = captureSnapshot();
4720
+ const snapshot = captureSnapshot();
4721
+ for (const list of snapshot.lists) {
4722
+ const memory = list.id ? chunkUZ2D4BVX_js.listRegistry.get(list.id)?.exploration : void 0;
4723
+ if (!memory) continue;
4724
+ memory.observe(list.items);
4725
+ list.exploration = memory.snapshot();
4726
+ }
4727
+ const delta = deltaTracker.observe(snapshot);
4728
+ if (delta) snapshot.delta = delta;
4729
+ context.snapshot = snapshot;
4089
4730
  } catch (err) {
4090
- chunkKZ2BLMS4_js.appilotsDebugWarn("buildAgentContext: captureSnapshot failed", err);
4731
+ chunkUZ2D4BVX_js.appilotsDebugWarn("buildAgentContext: captureSnapshot failed", err);
4091
4732
  }
4092
- const registry = chunkKZ2BLMS4_js.componentRegistry.snapshot();
4733
+ const registry = chunkUZ2D4BVX_js.componentRegistry.snapshot();
4093
4734
  const filtered = screen ? registry.filter((c) => !c.screen || c.screen === screen) : registry;
4094
4735
  if (filtered.length > 0) context.registeredComponents = filtered;
4095
- const registeredLists = chunkKZ2BLMS4_js.listRegistry.snapshot();
4736
+ const registeredLists = chunkUZ2D4BVX_js.listRegistry.snapshot();
4096
4737
  if (registeredLists.length > 0) {
4097
- context.registeredLists = registeredLists.map(
4098
- ({ scrollToIndex, scrollToOffset, getScrollMetrics, dataPreview, ...rest }) => rest
4738
+ const snapshot = context.snapshot;
4739
+ const activeIds = new Set(
4740
+ [...snapshot?.lists ?? [], ...snapshot?.scrollables ?? []].map((l) => l.id)
4099
4741
  );
4742
+ context.registeredLists = registeredLists.filter((entry) => activeIds.has(entry.id)).map(({ id, kind, component, itemCount, totalItemCount, label, refreshing, empty }) => ({
4743
+ id,
4744
+ kind,
4745
+ component,
4746
+ itemCount,
4747
+ totalItemCount,
4748
+ label,
4749
+ refreshing,
4750
+ empty
4751
+ }));
4100
4752
  }
4101
4753
  if (extras.loadingPending === true) context.loadingPending = true;
4102
4754
  return context;
4103
4755
  }
4104
- function resolveActionHints(screen, pressedComponentId, actionPayload) {
4105
- if (actionPayload && typeof actionPayload === "object") {
4106
- const inline = actionPayload._appilotsInferred;
4107
- if (inline && typeof inline === "object") {
4108
- return inline;
4109
- }
4110
- }
4111
- if (!screen || !pressedComponentId) return void 0;
4112
- const meta = chunkKZ2BLMS4_js.getScreenMetadata(screen);
4113
- const actions = meta?.actions;
4114
- if (!actions || actions.length === 0) return void 0;
4115
- for (const a of actions) {
4116
- if (typeof a === "string") continue;
4117
- if (chunkKZ2BLMS4_js.idLooselyMatches(a.id, pressedComponentId)) {
4118
- const inferred = a.appilotsInferred;
4119
- if (inferred && typeof inferred === "object") return inferred;
4120
- return void 0;
4121
- }
4122
- }
4123
- return void 0;
4124
- }
4125
4756
  async function settleTurn(input) {
4126
4757
  const toolWithoutEffectIds = /* @__PURE__ */ new Set();
4127
4758
  let loadingPending = false;
4128
4759
  const { turnActions, pressedComponentId: pressedId, navigateTargetScreen } = input;
4129
4760
  const successById = new Map(input.results.map((r) => [r.actionId, r.success]));
4761
+ const effectById = new Map(input.results.map((r) => [r.actionId, r.effect]));
4130
4762
  const typeById = new Map(input.results.map((r) => [r.actionId, r.type]));
4763
+ const pressedAction = turnActions.find(
4764
+ (a) => a.type === "ui_interaction" && a.payload?.componentId === pressedId
4765
+ );
4766
+ const hints = resolveActionHints(
4767
+ chunkUZ2D4BVX_js.getCurrentScreen(),
4768
+ pressedId,
4769
+ pressedAction?.payload
4770
+ );
4771
+ const capSource = typeof hints?.asyncBudgetMs === "number" && hints.asyncBudgetMs > 0 ? " (app-declared)" : hints?.isAsyncTrigger ? " (mcp-inferred)" : "";
4131
4772
  if (input.hadNavigate) {
4132
- const settle = await chunkKZ2BLMS4_js.waitForScreenSettle({
4773
+ const settle = await chunkUZ2D4BVX_js.waitForScreenSettle({
4133
4774
  expectingChange: true,
4134
4775
  fromScreen: input.preNavigateScreen ?? null,
4135
4776
  fromSignature: input.preNavigateSignature ?? null,
4136
4777
  targetScreen: navigateTargetScreen,
4137
4778
  maxMs: 3500
4138
4779
  });
4139
- chunkKZ2BLMS4_js.appilotsDebugLog(
4780
+ chunkUZ2D4BVX_js.appilotsDebugLog(
4140
4781
  `settleTurn: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
4141
4782
  );
4142
- const navigationMoved = settle.transitioned || chunkKZ2BLMS4_js.screenDepartedBaseline(input.preNavigateScreen ?? null, input.preNavigateSignature ?? null) || !!navigateTargetScreen && !!settle.screen && chunkKZ2BLMS4_js.routesBelongToSameFeature(settle.screen, navigateTargetScreen);
4783
+ const navigationMoved = settle.transitioned || chunkUZ2D4BVX_js.screenDepartedBaseline(input.preNavigateScreen ?? null, input.preNavigateSignature ?? null) || !!navigateTargetScreen && !!settle.screen && chunkUZ2D4BVX_js.routesBelongToSameFeature(settle.screen, navigateTargetScreen);
4143
4784
  if (!navigationMoved) {
4144
- chunkKZ2BLMS4_js.appilotsDebugWarn(
4785
+ chunkUZ2D4BVX_js.appilotsDebugWarn(
4145
4786
  `tool-without-effect: navigate emitted but screen did not change (stayed at "${input.preNavigateScreen ?? "unknown"}", waitedMs=${settle.waitedMs}). Likely causes: target screenName missing from MCP doc, nested-nav path absent, or React Navigation rejected the route.`
4146
4787
  );
4147
4788
  for (const [actionId, type] of typeById) {
4148
4789
  if (type === "navigate") toolWithoutEffectIds.add(actionId);
4149
4790
  }
4150
4791
  }
4151
- const loadSettle = await chunkKZ2BLMS4_js.waitForLoadingSettle({
4792
+ const postNavigateCapMs = resolvePostNavigateCapMs(hints);
4793
+ const loadSettle = await chunkUZ2D4BVX_js.waitForLoadingSettle({
4152
4794
  fromScreen: settle.screen ?? null,
4153
- maxMs: 3e3
4795
+ maxMs: postNavigateCapMs
4154
4796
  });
4155
- chunkKZ2BLMS4_js.appilotsDebugLog(
4156
- `settleTurn: post-navigate loading settle \u2014 settled=${loadSettle.settled} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs}`
4797
+ chunkUZ2D4BVX_js.appilotsDebugLog(
4798
+ `settleTurn: post-navigate loading settle \u2014 settled=${loadSettle.settled} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs} cap=${postNavigateCapMs}${capSource}`
4157
4799
  );
4158
4800
  if (loadSettle.loadingPending) loadingPending = true;
4159
4801
  } else {
4160
- const pressedAction = turnActions.find(
4161
- (a) => a.type === "ui_interaction" && a.payload?.componentId === pressedId
4162
- );
4163
- const hints = resolveActionHints(chunkKZ2BLMS4_js.getCurrentScreen(), pressedId, pressedAction?.payload);
4164
- const settleCapMs = hints?.isAsyncTrigger === true ? 1e4 : POST_ACTION_LOADING_MAX_MS;
4165
- const loadSettle = await chunkKZ2BLMS4_js.waitForLoadingSettle({
4802
+ const settleCapMs = resolveSettleCapMs(hints);
4803
+ const busyMaxMs = resolveBusyCeilingMs(hints);
4804
+ const loadSettle = await chunkUZ2D4BVX_js.waitForLoadingSettle({
4166
4805
  pressedComponentId: pressedId,
4167
- fromScreen: chunkKZ2BLMS4_js.getCurrentScreen(),
4168
- maxMs: settleCapMs
4806
+ fromScreen: chunkUZ2D4BVX_js.getCurrentScreen(),
4807
+ maxMs: settleCapMs,
4808
+ ...busyMaxMs !== void 0 ? { busyMaxMs } : {}
4169
4809
  });
4170
- chunkKZ2BLMS4_js.appilotsDebugLog(
4171
- `settleTurn: post-action loading settle \u2014 pressedId="${pressedId ?? ""}" settled=${loadSettle.settled} transitioned=${loadSettle.transitioned} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs} cap=${settleCapMs}${hints?.isAsyncTrigger ? " (mcp-inferred)" : ""}`
4810
+ chunkUZ2D4BVX_js.appilotsDebugLog(
4811
+ `settleTurn: post-action loading settle \u2014 pressedId="${pressedId ?? ""}" settled=${loadSettle.settled} transitioned=${loadSettle.transitioned} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs} cap=${settleCapMs}${capSource}${busyMaxMs !== void 0 ? ` busyCap=${busyMaxMs}` : ""}`
4172
4812
  );
4173
4813
  if (loadSettle.loadingPending) loadingPending = true;
4174
4814
  const listPressActions = turnActions.filter((action) => {
4175
- const target = chunkKZ2BLMS4_js.actionPressTargetId(action);
4176
- return target ? chunkKZ2BLMS4_js.isListItemPressTarget(target) : false;
4815
+ const target = chunkUZ2D4BVX_js.actionPressTargetId(action);
4816
+ return target ? chunkUZ2D4BVX_js.isListItemPressTarget(target) : false;
4177
4817
  });
4178
4818
  if (listPressActions.length > 0) {
4179
- const baselineFingerprint = chunkKZ2BLMS4_js.probeLoadingState(pressedId, chunkKZ2BLMS4_js.getCurrentScreen()).fingerprint;
4180
- const postPressSettle = await chunkKZ2BLMS4_js.waitForScreenSettle({
4819
+ const baselineFingerprint = chunkUZ2D4BVX_js.probeLoadingState(pressedId, chunkUZ2D4BVX_js.getCurrentScreen()).fingerprint;
4820
+ const postPressSettle = await chunkUZ2D4BVX_js.waitForScreenSettle({
4181
4821
  expectingChange: true,
4182
4822
  fromScreen: input.preNavigateScreen ?? null,
4183
4823
  fromSignature: input.preNavigateSignature ?? null,
4184
4824
  maxMs: 2500
4185
4825
  });
4186
- await chunkKZ2BLMS4_js.waitForLoadingSettle({
4826
+ await chunkUZ2D4BVX_js.waitForLoadingSettle({
4187
4827
  pressedComponentId: pressedId,
4188
- fromScreen: chunkKZ2BLMS4_js.getCurrentScreen(),
4828
+ fromScreen: chunkUZ2D4BVX_js.getCurrentScreen(),
4189
4829
  maxMs: 4e3
4190
4830
  });
4191
- const afterFingerprint = chunkKZ2BLMS4_js.probeLoadingState(pressedId, chunkKZ2BLMS4_js.getCurrentScreen()).fingerprint;
4192
- chunkKZ2BLMS4_js.appilotsDebugLog(
4831
+ const afterFingerprint = chunkUZ2D4BVX_js.probeLoadingState(pressedId, chunkUZ2D4BVX_js.getCurrentScreen()).fingerprint;
4832
+ chunkUZ2D4BVX_js.appilotsDebugLog(
4193
4833
  `settleTurn: post-list-press settle \u2014 screen="${postPressSettle.screen}" transitioned=${postPressSettle.transitioned} timedOut=${postPressSettle.timedOut} waitedMs=${postPressSettle.waitedMs}`
4194
4834
  );
4195
4835
  if (!postPressSettle.transitioned && baselineFingerprint === afterFingerprint) {
4196
4836
  for (const action of listPressActions) {
4837
+ if (effectById.get(action.id) === "changed") continue;
4197
4838
  if (successById.get(action.id)) {
4198
4839
  toolWithoutEffectIds.add(action.id);
4199
4840
  }
@@ -4205,46 +4846,68 @@ async function settleTurn(input) {
4205
4846
  }
4206
4847
  var reactNativeChatAdapter = {
4207
4848
  buildContext: buildAgentContext,
4208
- getCurrentScreen: chunkKZ2BLMS4_js.getCurrentScreen,
4209
- getCurrentScreenSignature: chunkKZ2BLMS4_js.getCurrentScreenSignature,
4849
+ getCurrentScreen: chunkUZ2D4BVX_js.getCurrentScreen,
4850
+ getCurrentScreenSignature: chunkUZ2D4BVX_js.getCurrentScreenSignature,
4210
4851
  settleTurn
4211
4852
  };
4212
4853
  function createReactNativeActionRunner(getOptions) {
4213
4854
  return {
4855
+ buildMissionContext: () => reactNativeChatAdapter.buildContext(),
4214
4856
  async execute(action, { confirmedDestructive }) {
4215
- const { permissions, emit, navigationRef, suppressNativeConfirm } = getOptions();
4216
- const navRef = navigationRef?.current ? navigationRef : { current: chunkKZ2BLMS4_js.getNavigationRef() };
4217
- return runWithConfirmedDestructiveContext(
4857
+ const { permissions, emit: emit2, navigationRef, suppressNativeConfirm } = getOptions();
4858
+ const navRef = navigationRef?.current ? navigationRef : { current: chunkUZ2D4BVX_js.getNavigationRef() };
4859
+ let receipt;
4860
+ let terminal;
4861
+ const result = await runWithConfirmedDestructiveContext(
4218
4862
  confirmedDestructive && suppressNativeConfirm !== false,
4219
4863
  reactNative.Alert,
4220
4864
  () => executeAction(action, {
4221
4865
  navigationRef: navRef,
4222
4866
  permissions,
4223
- emit
4224
- })
4867
+ emit: (event) => {
4868
+ if (confirmedDestructive && (event.type === "agent:action:complete" || event.type === "agent:action:error"))
4869
+ terminal = event;
4870
+ else emit2(event);
4871
+ }
4872
+ }),
4873
+ (handled) => {
4874
+ receipt = handled;
4875
+ }
4225
4876
  );
4877
+ const failed = receipt?.handlerCompleted === false;
4878
+ if (terminal)
4879
+ emit2({
4880
+ ...terminal,
4881
+ type: failed ? "agent:action:error" : terminal.type,
4882
+ data: {
4883
+ ...terminal.data,
4884
+ ...receipt ? { nativeConfirmation: receipt } : {},
4885
+ ...failed ? { success: false, error: "Native confirmation callback failed" } : {}
4886
+ }
4887
+ });
4888
+ return failed ? { ...result, success: false, error: "Native confirmation callback failed" } : result;
4226
4889
  },
4227
- getCurrentScreen: chunkKZ2BLMS4_js.getCurrentScreen,
4228
- getCurrentScreenSignature: chunkKZ2BLMS4_js.getCurrentScreenSignature,
4890
+ getCurrentScreen: chunkUZ2D4BVX_js.getCurrentScreen,
4891
+ getCurrentScreenSignature: chunkUZ2D4BVX_js.getCurrentScreenSignature,
4229
4892
  getScreenActionsMetadata(screen) {
4230
- return chunkKZ2BLMS4_js.getScreenMetadata(screen)?.actions;
4893
+ return chunkUZ2D4BVX_js.getScreenMetadata(screen)?.actions;
4231
4894
  },
4232
- waitForScreenSettle: chunkKZ2BLMS4_js.waitForScreenSettle
4895
+ waitForScreenSettle: chunkUZ2D4BVX_js.waitForScreenSettle
4233
4896
  };
4234
4897
  }
4235
4898
 
4236
4899
  // src/hooks/useAppilotsChat.ts
4237
4900
  function useAppilotsChat(options = {}) {
4238
- const { client, emit, subscribe } = chunkKZ2BLMS4_js.useAppilotsContext();
4901
+ const { client, emit: emit2, subscribe } = chunkUZ2D4BVX_js.useAppilotsContext();
4239
4902
  const machine = React.useMemo(
4240
- () => new chunkKZ2BLMS4_js.ChatSessionMachine(
4241
- { client, adapter: reactNativeChatAdapter, emit, warn: chunkKZ2BLMS4_js.appilotsDebugWarn },
4903
+ () => new chunkUZ2D4BVX_js.ChatSessionMachine(
4904
+ { client, adapter: reactNativeChatAdapter, emit: emit2, warn: chunkUZ2D4BVX_js.appilotsDebugWarn },
4242
4905
  options
4243
4906
  ),
4244
4907
  // A new machine per client/provider instance — options are pushed in
4245
4908
  // below so changing copy never resets the transcript.
4246
4909
  // eslint-disable-next-line react-hooks/exhaustive-deps
4247
- [client, emit]
4910
+ [client, emit2]
4248
4911
  );
4249
4912
  const optionsRef = React.useRef(options);
4250
4913
  optionsRef.current = options;
@@ -4252,6 +4915,14 @@ function useAppilotsChat(options = {}) {
4252
4915
  const state = React.useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
4253
4916
  React.useEffect(() => subscribe(machine.handleEvent), [subscribe, machine]);
4254
4917
  React.useEffect(() => () => machine.dispose(), [machine]);
4918
+ React.useEffect(() => {
4919
+ void machine.restoreMission();
4920
+ const subscription = reactNative.AppState?.addEventListener?.("change", (next) => {
4921
+ if (next !== "active" && machine.getState().mission?.status === "running")
4922
+ void machine.pauseMission();
4923
+ });
4924
+ return () => subscription?.remove();
4925
+ }, [machine]);
4255
4926
  const requestHuman = React.useCallback(
4256
4927
  (reason) => machine.requestHuman(reason),
4257
4928
  [machine]
@@ -4261,7 +4932,13 @@ function useAppilotsChat(options = {}) {
4261
4932
  const clearMessages = React.useCallback(() => machine.clearMessages(), [machine]);
4262
4933
  const clearError = React.useCallback(() => machine.clearError(), [machine]);
4263
4934
  return {
4935
+ mission: state.mission,
4936
+ pauseMission: () => machine.pauseMission(),
4937
+ resumeMission: () => machine.resumeMission(),
4938
+ cancelMission: () => machine.cancelMission(),
4939
+ hasStartedActing: state.hasStartedActing,
4264
4940
  messages: state.messages,
4941
+ replyLocale: state.replyLocale,
4265
4942
  isLoading: state.isLoading,
4266
4943
  loadingStatusKey: state.loadingStatusKey,
4267
4944
  error: state.error,
@@ -4274,39 +4951,33 @@ function useAppilotsChat(options = {}) {
4274
4951
  clearError
4275
4952
  };
4276
4953
  }
4277
- var DEFAULT_PERMISSIONS2 = {
4278
- canNavigate: true,
4279
- canFillForms: true,
4280
- canInteractUI: true,
4281
- canSubmitForms: true
4282
- };
4283
4954
  function useAppilotsActions(options = {}) {
4284
- const { client, subscribe, config, emit } = chunkKZ2BLMS4_js.useAppilotsContext();
4955
+ const { client, subscribe, config, emit: emit2 } = chunkUZ2D4BVX_js.useAppilotsContext();
4285
4956
  const { navigationRef, autoExecute = false } = options;
4286
4957
  const runnerOptionsRef = React.useRef({
4287
- permissions: config.permissions ?? DEFAULT_PERMISSIONS2,
4288
- emit,
4958
+ permissions: chunkUZ2D4BVX_js.resolveAgentPermissions(config.permissions),
4959
+ emit: emit2,
4289
4960
  navigationRef,
4290
4961
  suppressNativeConfirm: config.suppressNativeConfirm
4291
4962
  });
4292
4963
  runnerOptionsRef.current = {
4293
- permissions: config.permissions ?? DEFAULT_PERMISSIONS2,
4294
- emit,
4964
+ permissions: chunkUZ2D4BVX_js.resolveAgentPermissions(config.permissions),
4965
+ emit: emit2,
4295
4966
  navigationRef,
4296
4967
  suppressNativeConfirm: config.suppressNativeConfirm
4297
4968
  };
4298
4969
  const machine = React.useMemo(
4299
- () => new chunkKZ2BLMS4_js.ActionQueueMachine(
4970
+ () => new chunkUZ2D4BVX_js.ActionQueueMachine(
4300
4971
  {
4301
4972
  client,
4302
4973
  adapter: createReactNativeActionRunner(() => runnerOptionsRef.current),
4303
- emit,
4304
- warn: chunkKZ2BLMS4_js.appilotsDebugWarn
4974
+ emit: emit2,
4975
+ warn: chunkUZ2D4BVX_js.appilotsDebugWarn
4305
4976
  },
4306
4977
  { autoExecute }
4307
4978
  ),
4308
4979
  // eslint-disable-next-line react-hooks/exhaustive-deps
4309
- [client, emit]
4980
+ [client, emit2]
4310
4981
  );
4311
4982
  machine.setOptions({ autoExecute });
4312
4983
  const state = React.useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
@@ -4358,7 +5029,7 @@ function shouldShowSuggestedPrompts(args) {
4358
5029
 
4359
5030
  // src/hooks/useSuggestedPrompts.ts
4360
5031
  function useSuggestedPrompts(options) {
4361
- const { client } = chunkKZ2BLMS4_js.useAppilotsContext();
5032
+ const { client } = chunkUZ2D4BVX_js.useAppilotsContext();
4362
5033
  const { enabled, limit = 4 } = options;
4363
5034
  const [prompts, setPrompts] = React.useState([]);
4364
5035
  const [isLoading, setIsLoading] = React.useState(false);
@@ -4368,8 +5039,8 @@ function useSuggestedPrompts(options) {
4368
5039
  if (!enabled) return;
4369
5040
  const requestId = ++requestIdRef.current;
4370
5041
  let cancelled = false;
4371
- const screen = chunkKZ2BLMS4_js.getCurrentScreen() ?? void 0;
4372
- const localDev = screen ? (chunkKZ2BLMS4_js.getScreenMetadata(screen)?.suggestedPrompts ?? []).map((text) => ({
5042
+ const screen = chunkUZ2D4BVX_js.getCurrentScreen() ?? void 0;
5043
+ const localDev = screen ? (chunkUZ2D4BVX_js.getScreenMetadata(screen)?.suggestedPrompts ?? []).map((text) => ({
4373
5044
  text,
4374
5045
  source: "dev-defined"
4375
5046
  })) : [];
@@ -4383,7 +5054,7 @@ function useSuggestedPrompts(options) {
4383
5054
  if (cancelled || requestIdRef.current !== requestId) return;
4384
5055
  setPrompts(mergePromptSources(localDev, data, limit));
4385
5056
  }).catch((err) => {
4386
- chunkKZ2BLMS4_js.appilotsDebugWarn("useSuggestedPrompts: fetch failed", err);
5057
+ chunkUZ2D4BVX_js.appilotsDebugWarn("useSuggestedPrompts: fetch failed", err);
4387
5058
  }).finally(() => {
4388
5059
  if (cancelled || requestIdRef.current !== requestId) return;
4389
5060
  setIsLoading(false);
@@ -4394,11 +5065,11 @@ function useSuggestedPrompts(options) {
4394
5065
  }, [client, enabled, limit, refreshTick]);
4395
5066
  React.useEffect(() => {
4396
5067
  if (!enabled) return;
4397
- let lastScreen = chunkKZ2BLMS4_js.getCurrentScreen();
5068
+ let lastScreen = chunkUZ2D4BVX_js.getCurrentScreen();
4398
5069
  const interval = setInterval(() => {
4399
- const current = chunkKZ2BLMS4_js.getCurrentScreen();
4400
- if (current !== lastScreen) {
4401
- lastScreen = current;
5070
+ const current2 = chunkUZ2D4BVX_js.getCurrentScreen();
5071
+ if (current2 !== lastScreen) {
5072
+ lastScreen = current2;
4402
5073
  setRefreshTick((t) => t + 1);
4403
5074
  }
4404
5075
  }, 800);
@@ -4411,22 +5082,22 @@ function useSuggestedPrompts(options) {
4411
5082
  };
4412
5083
  }
4413
5084
  function useAppilotsNavigation() {
4414
- const { emit } = chunkKZ2BLMS4_js.useAppilotsContext();
5085
+ const { emit: emit2 } = chunkUZ2D4BVX_js.useAppilotsContext();
4415
5086
  const [currentScreen, setCurrentScreenState] = React.useState(null);
4416
5087
  const [navigationHistory, setNavigationHistory] = React.useState([]);
4417
5088
  const navigationRef = React.useRef(null);
4418
5089
  const setCurrentScreen2 = React.useCallback(
4419
5090
  (screenName) => {
4420
5091
  setCurrentScreenState(screenName);
4421
- chunkKZ2BLMS4_js.setCurrentScreen(screenName);
5092
+ chunkUZ2D4BVX_js.setCurrentScreen(screenName);
4422
5093
  setNavigationHistory((prev) => [...prev, screenName]);
4423
- emit({
5094
+ emit2({
4424
5095
  type: "navigation:change",
4425
5096
  timestamp: Date.now(),
4426
5097
  data: { screenName }
4427
5098
  });
4428
5099
  },
4429
- [emit]
5100
+ [emit2]
4430
5101
  );
4431
5102
  React.useEffect(() => {
4432
5103
  const nav = navigationRef.current;
@@ -4449,7 +5120,7 @@ function useAppilotsNavigation() {
4449
5120
 
4450
5121
  // src/hooks/useAppilots.ts
4451
5122
  function useAppilots() {
4452
- const { config, client } = chunkKZ2BLMS4_js.useAppilotsContext();
5123
+ const { config, client } = chunkUZ2D4BVX_js.useAppilotsContext();
4453
5124
  const chat = useAppilotsChat();
4454
5125
  const navigation = useAppilotsNavigation();
4455
5126
  const actions = useAppilotsActions();
@@ -4476,9 +5147,14 @@ function useAppilots() {
4476
5147
  rejectAction: actions.rejectAction
4477
5148
  };
4478
5149
  }
5150
+ function useAppilotsElementProps(id) {
5151
+ return React.useMemo(() => ({ testID: id }), [id]);
5152
+ }
5153
+
5154
+ // src/hooks/useAppilotsField.ts
4479
5155
  function useAppilotsField(id, options) {
4480
5156
  const { value, onChangeText, ref, fieldType, label, screen } = options;
4481
- const registry = chunkKZ2BLMS4_js.useResolvedRegistry();
5157
+ const registry = chunkUZ2D4BVX_js.useResolvedRegistry();
4482
5158
  const valueRef = React.useRef(value);
4483
5159
  const onChangeRef = React.useRef(onChangeText);
4484
5160
  valueRef.current = value;
@@ -4503,10 +5179,11 @@ function useAppilotsField(id, options) {
4503
5179
  registry.unregister(id);
4504
5180
  };
4505
5181
  }, [id, registry, getValue, setValue, focus, ref, fieldType, label, screen]);
5182
+ return useAppilotsElementProps(id);
4506
5183
  }
4507
5184
  function useAppilotsTarget(id, options) {
4508
5185
  const { onPress, onLongPress, onScrollTo, label, screen } = options;
4509
- const registry = chunkKZ2BLMS4_js.useResolvedRegistry();
5186
+ const registry = chunkUZ2D4BVX_js.useResolvedRegistry();
4510
5187
  const onPressRef = React.useRef(onPress);
4511
5188
  const onLongPressRef = React.useRef(onLongPress);
4512
5189
  const onScrollToRef = React.useRef(onScrollTo);
@@ -4530,10 +5207,11 @@ function useAppilotsTarget(id, options) {
4530
5207
  registry.unregister(id);
4531
5208
  };
4532
5209
  }, [id, registry, press, longPress, scrollTo, onLongPress, onScrollTo, label, screen]);
5210
+ return useAppilotsElementProps(id);
4533
5211
  }
4534
5212
  function useAppilotsToggle(id, options) {
4535
5213
  const { value, onValueChange, label, screen } = options;
4536
- const registry = chunkKZ2BLMS4_js.useResolvedRegistry();
5214
+ const registry = chunkUZ2D4BVX_js.useResolvedRegistry();
4537
5215
  const valueRef = React.useRef(value);
4538
5216
  const onChangeRef = React.useRef(onValueChange);
4539
5217
  valueRef.current = value;
@@ -4553,10 +5231,11 @@ function useAppilotsToggle(id, options) {
4553
5231
  registry.unregister(id);
4554
5232
  };
4555
5233
  }, [id, registry, getValue, setValue, label, screen]);
5234
+ return useAppilotsElementProps(id);
4556
5235
  }
4557
5236
  function useAppilotsSlider(id, options) {
4558
5237
  const { value, onValueChange, min, max, step, label, screen } = options;
4559
- const registry = chunkKZ2BLMS4_js.useResolvedRegistry();
5238
+ const registry = chunkUZ2D4BVX_js.useResolvedRegistry();
4560
5239
  const valueRef = React.useRef(value);
4561
5240
  const onChangeRef = React.useRef(onValueChange);
4562
5241
  valueRef.current = value;
@@ -4579,16 +5258,30 @@ function useAppilotsSlider(id, options) {
4579
5258
  registry.unregister(id);
4580
5259
  };
4581
5260
  }, [id, registry, getValue, setValue, min, max, step, label, screen]);
5261
+ return useAppilotsElementProps(id);
4582
5262
  }
4583
5263
 
5264
+ exports.AppilotsI18nProvider = AppilotsI18nProvider;
5265
+ exports.REVEAL_MARGIN = REVEAL_MARGIN;
5266
+ exports.SPOTLIGHT_TTL_MS = SPOTLIGHT_TTL_MS;
4584
5267
  exports.appilotsSelfCheck = appilotsSelfCheck;
4585
5268
  exports.captureSnapshot = captureSnapshot;
5269
+ exports.detectDeviceLocale = detectDeviceLocale;
4586
5270
  exports.executeAction = executeAction;
5271
+ exports.getSpotlightMode = getSpotlightMode;
5272
+ exports.getSpotlightTarget = getSpotlightTarget;
5273
+ exports.offsetToReveal = offsetToReveal;
5274
+ exports.resolveLocale = resolveLocale;
5275
+ exports.revealTarget = revealTarget;
5276
+ exports.setSpotlightMode = setSpotlightMode;
5277
+ exports.setSpotlightTarget = setSpotlightTarget;
4587
5278
  exports.shouldShowSuggestedPrompts = shouldShowSuggestedPrompts;
5279
+ exports.subscribeSpotlight = subscribeSpotlight;
4588
5280
  exports.useAppilots = useAppilots;
4589
5281
  exports.useAppilotsActions = useAppilotsActions;
4590
5282
  exports.useAppilotsChat = useAppilotsChat;
4591
5283
  exports.useAppilotsField = useAppilotsField;
5284
+ exports.useAppilotsI18n = useAppilotsI18n;
4592
5285
  exports.useAppilotsNavigation = useAppilotsNavigation;
4593
5286
  exports.useAppilotsSlider = useAppilotsSlider;
4594
5287
  exports.useAppilotsTarget = useAppilotsTarget;