@appilots/sdk 0.11.0 → 0.11.3

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.
@@ -161,7 +161,7 @@ new RegExp(
161
161
  );
162
162
  var CREATE_EDIT_ROUTE_RE = new RegExp(`(${build(CREATE_EDIT_ROUTE)})`, "i");
163
163
 
164
- // ../shared/dist/chunk-43YQ52Q7.mjs
164
+ // ../shared/dist/chunk-SWQOZWHP.mjs
165
165
  var SENSITIVE_FIELD_NAME_RE = /(\b|_|-)(password|senha|contrase[ñn]a|mot[\s_-]*de[\s_-]*passe|passwd|pwd|secret|token|api[\s_-]*key|access[\s_-]*key|private[\s_-]*key|cvv|cvc|cvn|cvc2|cid|pin(?:[\s_-]*code)?|otp|one[\s_-]*time[\s_-]*code|2fa|mfa|security[\s_-]*code|c[óo]digo[\s_-]*seguran[çc]a|c[óo]digo[\s_-]*verifica[çc][ãa]o|verification[\s_-]*code)(\b|_|-)/i;
166
166
  function isSensitiveFieldName(id, label, placeholder, type) {
167
167
  if (type === "password") return true;
@@ -1370,6 +1370,7 @@ function snapshotHasVisibleSubmitButton(snap) {
1370
1370
  );
1371
1371
  }
1372
1372
  function snapshotHasVisibleValidation(snap) {
1373
+ if ((snap.inputs ?? []).some((input) => input.invalid === true)) return true;
1373
1374
  return (snap.texts ?? []).some(
1374
1375
  (text) => typeof text === "string" && FORM_VALIDATION_TEXT_RE.test(text)
1375
1376
  );
@@ -1515,6 +1516,8 @@ var ChatSessionMachine = class {
1515
1516
  /** Tear down timers and the escalation stream. Safe to call twice. */
1516
1517
  dispose() {
1517
1518
  this.disposed = true;
1519
+ this.abortController?.abort();
1520
+ this.currentTurn = null;
1518
1521
  this.closeEscalationStream();
1519
1522
  }
1520
1523
  setState(partial) {
@@ -1561,7 +1564,7 @@ var ChatSessionMachine = class {
1561
1564
  }
1562
1565
  // ── Escalation: request, stream, and message routing ─────────
1563
1566
  async requestHuman(reason = "user_requested") {
1564
- if (this.state.escalation) return;
1567
+ if (this.disposed || this.state.escalation) return;
1565
1568
  try {
1566
1569
  const lastError = this.state.messages.slice().reverse().find((m) => m.role === "assistant" && m.id.startsWith("error_"));
1567
1570
  const result = await this.deps.client.requestEscalation(reason, lastError?.content);
@@ -1735,7 +1738,7 @@ var ChatSessionMachine = class {
1735
1738
  // ── Continuation: when all current-turn actions finish, ask the
1736
1739
  // model what to do next, given the outcomes + fresh snapshot.
1737
1740
  async runContinuation(turn) {
1738
- if (this.continuationInFlight) return;
1741
+ if (this.disposed || this.continuationInFlight) return;
1739
1742
  this.continuationInFlight = true;
1740
1743
  try {
1741
1744
  this.setState({ isLoading: true, loadingStatusKey: "statusWaitingApp" });
@@ -1758,6 +1761,7 @@ var ChatSessionMachine = class {
1758
1761
  pressedComponentId: pressedId,
1759
1762
  navigateTargetScreen
1760
1763
  });
1764
+ if (this.disposed || this.currentTurn !== turn) return;
1761
1765
  const loadingPending = settle.loadingPending;
1762
1766
  for (const id of settle.toolWithoutEffectIds) toolWithoutEffectIds.add(id);
1763
1767
  const context = this.deps.adapter.buildContext({ loadingPending });
@@ -1823,6 +1827,7 @@ var ChatSessionMachine = class {
1823
1827
  `ChatSessionMachine: running continuation hop=${turn.hop} (${results.filter((r) => r.success).length}/${results.length} succeeded` + (exhausted.length > 0 ? `, ${exhausted.length} recovery-exhausted` : "") + ")"
1824
1828
  );
1825
1829
  const response = await this.deps.client.continueAgent(results, context, turn.hop);
1830
+ if (this.disposed) return;
1826
1831
  this.addAssistantMessage(response.message);
1827
1832
  this.deps.emit({
1828
1833
  type: "agent:message",
@@ -1910,7 +1915,7 @@ var ChatSessionMachine = class {
1910
1915
  }
1911
1916
  // ── Send / cancel / clear ────────────────────────────────────
1912
1917
  async sendMessage(content) {
1913
- if (!content.trim() || this.state.isLoading) return;
1918
+ if (this.disposed || !content.trim() || this.state.isLoading) return;
1914
1919
  if (this.state.escalation) {
1915
1920
  await this.sendEscalationMessage(content.trim());
1916
1921
  return;
@@ -1937,7 +1942,7 @@ var ChatSessionMachine = class {
1937
1942
  const abortController = new AbortController();
1938
1943
  this.abortController = abortController;
1939
1944
  try {
1940
- const context = this.deps.adapter.buildContext();
1945
+ const context = this.deps.adapter.buildContext({ newMission: true });
1941
1946
  appilotsDebugLog(
1942
1947
  `ChatSessionMachine sendMessage: context \u2014 currentScreen="${context.currentScreen ?? "unknown"}", snapshot={texts:${context.snapshot?.texts?.length ?? 0}, inputs:${context.snapshot?.inputs?.length ?? 0}, buttons:${context.snapshot?.buttons?.length ?? 0}, elements:${context.snapshot?.elements?.length ?? 0}, lists:${context.snapshot?.lists?.length ?? 0}, listItems:${(context.snapshot?.lists ?? []).reduce((acc, l) => acc + (l.items?.length ?? 0), 0)}, choiceGroups:${context.snapshot?.choiceGroups?.length ?? 0}}`
1943
1948
  );
@@ -2138,6 +2143,7 @@ var ActionQueueMachine = class {
2138
2143
  processed = /* @__PURE__ */ new Set();
2139
2144
  /** Guards against overlapping sequential runs. */
2140
2145
  draining = false;
2146
+ disposed = false;
2141
2147
  /** Set when new work arrives while a drain is in flight. */
2142
2148
  drainRequested = false;
2143
2149
  /** Set while a drain is queued on the microtask queue. */
@@ -2152,6 +2158,12 @@ var ActionQueueMachine = class {
2152
2158
  this.rejectAction = this.rejectAction.bind(this);
2153
2159
  }
2154
2160
  // ── State plumbing ───────────────────────────────────────────
2161
+ dispose() {
2162
+ this.disposed = true;
2163
+ this.drainRequested = false;
2164
+ this.setActions([]);
2165
+ this.listeners.clear();
2166
+ }
2155
2167
  getState() {
2156
2168
  return this.state;
2157
2169
  }
@@ -2177,7 +2189,7 @@ var ActionQueueMachine = class {
2177
2189
  }
2178
2190
  // ── Event intake: new actions from the agent ─────────────────
2179
2191
  handleEvent(event) {
2180
- if (event.type !== "agent:action:start") return;
2192
+ if (this.disposed || event.type !== "agent:action:start") return;
2181
2193
  const action = event.data?.action;
2182
2194
  if (!action?.id) return;
2183
2195
  appilotsDebugLog(
@@ -2204,7 +2216,7 @@ var ActionQueueMachine = class {
2204
2216
  * batching before its effect ran.)
2205
2217
  */
2206
2218
  scheduleDrain() {
2207
- if (!this.options.autoExecute) return;
2219
+ if (this.disposed || !this.options.autoExecute) return;
2208
2220
  if (this.draining) {
2209
2221
  this.drainRequested = true;
2210
2222
  return;
@@ -2213,7 +2225,7 @@ var ActionQueueMachine = class {
2213
2225
  this.drainScheduled = true;
2214
2226
  queueMicrotask(() => {
2215
2227
  this.drainScheduled = false;
2216
- if (!this.options.autoExecute) return;
2228
+ if (this.disposed || !this.options.autoExecute) return;
2217
2229
  if (this.draining) {
2218
2230
  this.drainRequested = true;
2219
2231
  return;
@@ -2336,6 +2348,7 @@ var ActionQueueMachine = class {
2336
2348
  let preNavScreen = null;
2337
2349
  let preNavSignature = null;
2338
2350
  for (const [index, action] of pending.entries()) {
2351
+ if (this.disposed) return;
2339
2352
  if (action.type === "confirm") {
2340
2353
  continue;
2341
2354
  }
@@ -2455,6 +2468,7 @@ var ActionQueueMachine = class {
2455
2468
  }
2456
2469
  // ── Approve & execute an action ─────────────────────────────
2457
2470
  async approveAction(actionId) {
2471
+ if (this.disposed) return false;
2458
2472
  const action = this.state.actions.find((a) => a.id === actionId);
2459
2473
  if (!action) return false;
2460
2474
  appilotsDebugLog(
@@ -3270,6 +3284,91 @@ function clampSnapshotToWireLimits(snapshot) {
3270
3284
  if (cut.dropped) snapshot.truncated = true;
3271
3285
  return snapshot;
3272
3286
  }
3287
+ var MAX_DELTA_FIELD_IDS = 12;
3288
+ function isFilled(input) {
3289
+ if (typeof input.filled === "boolean") return input.filled;
3290
+ return typeof input.value === "string" && input.value.length > 0;
3291
+ }
3292
+ function fingerprintSnapshot(snap) {
3293
+ const texts = /* @__PURE__ */ new Set();
3294
+ for (const text of snap.texts ?? []) {
3295
+ if (typeof text !== "string") continue;
3296
+ const trimmed = text.trim();
3297
+ if (trimmed) texts.add(trimmed);
3298
+ }
3299
+ let visibleRows = 0;
3300
+ let totalRows = 0;
3301
+ for (const list of snap.lists ?? []) {
3302
+ visibleRows += list.items?.length ?? 0;
3303
+ if (typeof list.itemCount === "number") totalRows += list.itemCount;
3304
+ }
3305
+ const filledDeclaredFields = /* @__PURE__ */ new Set();
3306
+ let anyInvalid = false;
3307
+ for (const input of snap.inputs ?? []) {
3308
+ if (input.invalid === true) anyInvalid = true;
3309
+ if (input.provenance !== "declared") continue;
3310
+ if (!input.id) continue;
3311
+ if (isFilled(input)) filledDeclaredFields.add(input.id);
3312
+ }
3313
+ return {
3314
+ route: snap.route ?? null,
3315
+ modal: snap.modalOpen === true || !!snap.nativeDialog,
3316
+ loading: snap.loading === true,
3317
+ texts,
3318
+ visibleRows,
3319
+ totalRows,
3320
+ filledDeclaredFields,
3321
+ anyInvalid
3322
+ };
3323
+ }
3324
+ function countMissing(from, inSet) {
3325
+ let n = 0;
3326
+ for (const value of from) if (!inSet.has(value)) n += 1;
3327
+ return n;
3328
+ }
3329
+ function namesMissing(from, inSet) {
3330
+ const out = [];
3331
+ for (const value of from) if (!inSet.has(value)) out.push(value);
3332
+ out.sort();
3333
+ return out.slice(0, MAX_DELTA_FIELD_IDS);
3334
+ }
3335
+ function diffFingerprints(prev, next) {
3336
+ const delta = {};
3337
+ if (prev.route !== next.route) delta.routeChanged = true;
3338
+ if (!prev.modal && next.modal) delta.modalOpened = true;
3339
+ if (prev.modal && !next.modal) delta.modalClosed = true;
3340
+ if (!prev.loading && next.loading) delta.loadingStarted = true;
3341
+ if (prev.loading && !next.loading) delta.loadingFinished = true;
3342
+ const added = countMissing(next.texts, prev.texts);
3343
+ const removed = countMissing(prev.texts, next.texts);
3344
+ if (added > 0) delta.textsAdded = added;
3345
+ if (removed > 0) delta.textsRemoved = removed;
3346
+ const visibleRowsDelta = next.visibleRows - prev.visibleRows;
3347
+ if (visibleRowsDelta !== 0) delta.visibleRowsDelta = visibleRowsDelta;
3348
+ const totalRowsDelta = next.totalRows - prev.totalRows;
3349
+ if (totalRowsDelta !== 0) delta.totalRowsDelta = totalRowsDelta;
3350
+ const newlyFilled = namesMissing(next.filledDeclaredFields, prev.filledDeclaredFields);
3351
+ if (newlyFilled.length > 0) delta.fieldsNewlyFilled = newlyFilled;
3352
+ const cleared = namesMissing(prev.filledDeclaredFields, next.filledDeclaredFields);
3353
+ if (cleared.length > 0) delta.fieldsCleared = cleared;
3354
+ if (!prev.anyInvalid && next.anyInvalid) delta.invalidAppeared = true;
3355
+ if (Object.keys(delta).length === 0) delta.unchanged = true;
3356
+ return delta;
3357
+ }
3358
+ function createSnapshotDeltaTracker() {
3359
+ let previous;
3360
+ return {
3361
+ observe(snap) {
3362
+ const next = fingerprintSnapshot(snap);
3363
+ const delta = previous ? diffFingerprints(previous, next) : void 0;
3364
+ previous = next;
3365
+ return delta;
3366
+ },
3367
+ reset() {
3368
+ previous = void 0;
3369
+ }
3370
+ };
3371
+ }
3273
3372
 
3274
3373
  // src/registry/ComponentRegistry.ts
3275
3374
  var ComponentRegistryImpl = class {
@@ -4471,6 +4570,37 @@ function rectForNode(node) {
4471
4570
  return _rectByNode.get(node);
4472
4571
  }
4473
4572
 
4573
+ // src/introspection/focusState.ts
4574
+ var _host2 = null;
4575
+ function setFocusHost(host) {
4576
+ _host2 = host;
4577
+ }
4578
+ var HOST_SEARCH_DEPTH2 = 6;
4579
+ function isFiberFocused(fiber) {
4580
+ const host = _host2;
4581
+ if (!host || !fiber) return void 0;
4582
+ let focused = null;
4583
+ try {
4584
+ focused = host.focusedInstance();
4585
+ } catch {
4586
+ return void 0;
4587
+ }
4588
+ if (!focused) return false;
4589
+ const stack = [{ node: fiber, depth: 0 }];
4590
+ while (stack.length > 0) {
4591
+ const { node, depth } = stack.pop();
4592
+ const current = node;
4593
+ if (!current) continue;
4594
+ if (current.stateNode && current.stateNode === focused) return true;
4595
+ if (depth < HOST_SEARCH_DEPTH2 && current.child) {
4596
+ stack.push({ node: current.child, depth: depth + 1 });
4597
+ const sibling = current.child.sibling;
4598
+ if (sibling) stack.push({ node: sibling, depth: depth + 1 });
4599
+ }
4600
+ }
4601
+ return false;
4602
+ }
4603
+
4474
4604
  // src/introspection/walkFiber.ts
4475
4605
  function getTypeName(type) {
4476
4606
  if (!type) return null;
@@ -4539,6 +4669,71 @@ function isAppilotsSensitiveMarked(props) {
4539
4669
  if (!props) return false;
4540
4670
  return props.__appilotsSensitive === true || props["data-appilots-sensitive"] === true || props.appilotsSensitive === true;
4541
4671
  }
4672
+ function booleanish(value) {
4673
+ if (typeof value === "boolean") return value;
4674
+ if (typeof value === "string") {
4675
+ const normalized = value.trim().toLowerCase();
4676
+ if (normalized === "true") return true;
4677
+ if (normalized === "false") return false;
4678
+ }
4679
+ return void 0;
4680
+ }
4681
+ function firstBooleanish(props, keys) {
4682
+ for (const key of keys) {
4683
+ const resolved = booleanish(props?.[key]);
4684
+ if (resolved !== void 0) return resolved;
4685
+ }
4686
+ return void 0;
4687
+ }
4688
+ function readRequiredProp(props) {
4689
+ const direct = firstBooleanish(props, [
4690
+ "required",
4691
+ "aria-required",
4692
+ "isRequired",
4693
+ "accessibilityRequired"
4694
+ ]);
4695
+ if (direct !== void 0) return direct;
4696
+ return booleanish(props?.accessibilityState?.required);
4697
+ }
4698
+ function readInvalidProp(props) {
4699
+ const direct = firstBooleanish(props, ["invalid", "aria-invalid", "isInvalid", "hasError"]);
4700
+ if (direct !== void 0) return direct;
4701
+ const state = booleanish(props?.accessibilityState?.invalid);
4702
+ if (state !== void 0) return state;
4703
+ for (const key of ["error", "errorMessage", "errorText"]) {
4704
+ const value = props?.[key];
4705
+ const asBoolean = booleanish(value);
4706
+ if (asBoolean !== void 0) return asBoolean;
4707
+ if (typeof value === "string") return value.trim().length > 0;
4708
+ }
4709
+ return void 0;
4710
+ }
4711
+ var WRAPPER_PROP_SEARCH_DEPTH = 3;
4712
+ var WRAPPER_SUBTREE_BUDGET = 64;
4713
+ function wrapsExactlyOneInput(fiber) {
4714
+ let seen = 0;
4715
+ let visited = 0;
4716
+ const stack = [fiber?.child];
4717
+ while (stack.length > 0) {
4718
+ if ((visited += 1) > WRAPPER_SUBTREE_BUDGET) return false;
4719
+ const node = stack.pop();
4720
+ if (!node) continue;
4721
+ if (matchesName(node, "TextInput") && (seen += 1) > 1) return false;
4722
+ if (node.child) stack.push(node.child);
4723
+ if (node.sibling) stack.push(node.sibling);
4724
+ }
4725
+ return seen === 1;
4726
+ }
4727
+ function readWrapperProps(fiber) {
4728
+ let node = fiber?.return;
4729
+ for (let depth = 0; node && depth < WRAPPER_PROP_SEARCH_DEPTH; depth += 1, node = node.return) {
4730
+ const props = node.memoizedProps;
4731
+ if (!props) continue;
4732
+ if (readInvalidProp(props) === void 0 && readRequiredProp(props) === void 0) continue;
4733
+ return wrapsExactlyOneInput(node) ? props : void 0;
4734
+ }
4735
+ return void 0;
4736
+ }
4542
4737
  function stringProp(props, name) {
4543
4738
  const value = props?.[name];
4544
4739
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -4594,6 +4789,8 @@ function isSwitchLike(fiber, props) {
4594
4789
  }
4595
4790
  function switchValue(props) {
4596
4791
  if (typeof props?.value === "boolean") return props.value;
4792
+ const aria = props?.["aria-checked"];
4793
+ if (typeof aria === "boolean") return aria;
4597
4794
  const checked = props?.accessibilityState?.checked;
4598
4795
  return checked === true;
4599
4796
  }
@@ -4987,6 +5184,11 @@ function walkSubtree(root, detectLists, startInModal = false) {
4987
5184
  const isSecure = !!props.secureTextEntry || isAppilotsSensitiveMarked(props);
4988
5185
  const rawValue = typeof props.value === "string" ? props.value : void 0;
4989
5186
  const safeValue = isSecure ? rawValue && rawValue.length > 0 ? "<hidden>" : void 0 : rawValue;
5187
+ const filled = typeof rawValue === "string" ? rawValue.length > 0 : void 0;
5188
+ const wrapper = readWrapperProps(fiber);
5189
+ const required = readRequiredProp(props) ?? (wrapper ? readRequiredProp(wrapper) : void 0);
5190
+ const invalid = readInvalidProp(props) ?? (wrapper ? readInvalidProp(wrapper) : void 0);
5191
+ const focused = isFiberFocused(fiber);
4990
5192
  const identity = resolveIdentity(props, inferLabel(props));
4991
5193
  const geom = readGeometry(fiber);
4992
5194
  result.inputs.push(
@@ -5000,6 +5202,13 @@ function walkSubtree(root, detectLists, startInModal = false) {
5000
5202
  editable: props.editable !== false,
5001
5203
  secure: isSecure,
5002
5204
  type: inferInputType(props),
5205
+ // Each one omitted rather than sent as `false` when the app
5206
+ // says nothing: absent is UNKNOWN on all four, and the wire
5207
+ // has to keep that apart from a declared negative.
5208
+ ...required !== void 0 ? { required } : {},
5209
+ ...invalid !== void 0 ? { invalid } : {},
5210
+ ...filled !== void 0 ? { filled } : {},
5211
+ ...focused !== void 0 ? { focused } : {},
5003
5212
  // A field whose return key submits (#398). On a search box or
5004
5213
  // a one-field login this IS the submit, and without the flag
5005
5214
  // the screen looks like a form with no way to send it.
@@ -5474,9 +5683,21 @@ function routesBelongToSameFeature(a, b) {
5474
5683
  // src/navigation/navigationState.ts
5475
5684
  var _currentScreen = null;
5476
5685
  var _navigationRef = null;
5686
+ var MAX_VISITED_SCREENS = 8;
5687
+ var _visited = [];
5688
+ function recordScreenVisit(screen) {
5689
+ if (!screen) return;
5690
+ if (_visited[_visited.length - 1] === screen) return;
5691
+ _visited.push(screen);
5692
+ if (_visited.length > MAX_VISITED_SCREENS) _visited.shift();
5693
+ }
5694
+ function getVisitedScreens() {
5695
+ return [..._visited];
5696
+ }
5477
5697
  function setCurrentScreen(screen) {
5478
5698
  appilotsDebugLog(`setCurrentScreen: "${_currentScreen}" \u2192 "${screen}"`);
5479
5699
  _currentScreen = screen;
5700
+ recordScreenVisit(screen);
5480
5701
  }
5481
5702
  function getCurrentScreen() {
5482
5703
  const activePath = getActiveRouteNames();
@@ -5490,6 +5711,7 @@ function getCurrentScreen() {
5490
5711
  }
5491
5712
  _currentScreen = deepest;
5492
5713
  }
5714
+ recordScreenVisit(deepest);
5493
5715
  if (deepest) return deepest;
5494
5716
  }
5495
5717
  if (_navigationRef) {
@@ -5501,6 +5723,7 @@ function getCurrentScreen() {
5501
5723
  );
5502
5724
  _currentScreen = liveScreen;
5503
5725
  }
5726
+ recordScreenVisit(liveScreen);
5504
5727
  return liveScreen;
5505
5728
  }
5506
5729
  }
@@ -5560,6 +5783,13 @@ function collectRouteNamesFromState(state, out = /* @__PURE__ */ new Set()) {
5560
5783
  }
5561
5784
  return out;
5562
5785
  }
5786
+ function backStackOf(state) {
5787
+ const deepest = getDeepestNavigatorState(state);
5788
+ const routes = deepest?.routes;
5789
+ if (!Array.isArray(routes)) return [];
5790
+ const index = typeof deepest.index === "number" ? deepest.index : routes.length - 1;
5791
+ return routes.slice(0, index + 1).map((route) => route?.name).filter((name) => typeof name === "string" && name.length > 0);
5792
+ }
5563
5793
  function getDeepestNavigatorState(state) {
5564
5794
  if (!state) return void 0;
5565
5795
  const route = state.routes?.[state.index ?? 0];
@@ -5574,7 +5804,8 @@ function getNavigationStateSnapshot() {
5574
5804
  activePath: current,
5575
5805
  rootRouteNames: current,
5576
5806
  currentRouteNames: current,
5577
- routeNames: current
5807
+ routeNames: current,
5808
+ ...getVisitedScreens().length > 0 ? { visited: getVisitedScreens() } : {}
5578
5809
  } : null;
5579
5810
  }
5580
5811
  try {
@@ -5587,20 +5818,27 @@ function getNavigationStateSnapshot() {
5587
5818
  rootRouteNames: current,
5588
5819
  currentRouteNames: current,
5589
5820
  routeNames: current,
5590
- canGoBack: typeof _navigationRef.canGoBack === "function" ? _navigationRef.canGoBack() : void 0
5821
+ canGoBack: typeof _navigationRef.canGoBack === "function" ? _navigationRef.canGoBack() : void 0,
5822
+ ...getVisitedScreens().length > 0 ? { visited: getVisitedScreens() } : {}
5591
5823
  } : null;
5592
5824
  }
5593
5825
  const activePath = getActiveRouteNamesFromState(state);
5594
5826
  const currentRouteName = activePath[activePath.length - 1] ?? _currentScreen ?? void 0;
5595
5827
  const deepestState = getDeepestNavigatorState(state);
5596
5828
  const routeNames = Array.from(collectRouteNamesFromState(state));
5829
+ const backStack = backStackOf(state);
5597
5830
  return {
5598
5831
  currentRouteName,
5599
5832
  activePath,
5600
5833
  rootRouteNames: routeNamesAtState(state),
5601
5834
  currentRouteNames: routeNamesAtState(deepestState),
5602
5835
  routeNames,
5603
- canGoBack: typeof _navigationRef.canGoBack === "function" ? _navigationRef.canGoBack() : void 0
5836
+ canGoBack: typeof _navigationRef.canGoBack === "function" ? _navigationRef.canGoBack() : void 0,
5837
+ // Both omitted when empty rather than sent as `[]`: an empty array
5838
+ // reads as "we looked and there is nowhere to go back to", and on
5839
+ // an app with no reachable navigator we did not look.
5840
+ ...backStack.length > 0 ? { backStack } : {},
5841
+ ...getVisitedScreens().length > 0 ? { visited: getVisitedScreens() } : {}
5604
5842
  };
5605
5843
  } catch {
5606
5844
  const current = _currentScreen ? [_currentScreen] : [];
@@ -5609,7 +5847,8 @@ function getNavigationStateSnapshot() {
5609
5847
  activePath: current,
5610
5848
  rootRouteNames: current,
5611
5849
  currentRouteNames: current,
5612
- routeNames: current
5850
+ routeNames: current,
5851
+ ...getVisitedScreens().length > 0 ? { visited: getVisitedScreens() } : {}
5613
5852
  } : null;
5614
5853
  }
5615
5854
  }
@@ -5763,13 +6002,37 @@ async function waitForLoadingSettle(opts = {}) {
5763
6002
  const stableTarget = opts.stableReads ?? 2;
5764
6003
  const pressedComponentId = opts.pressedComponentId ?? null;
5765
6004
  const fromScreen = opts.fromScreen ?? null;
6005
+ const busyMaxMs = Math.max(maxMs, opts.busyMaxMs ?? 0);
5766
6006
  const startMs = Date.now();
5767
- const remaining = () => maxMs - (Date.now() - startMs);
5768
6007
  let probe = probeLoadingState(pressedComponentId, getCurrentScreen());
5769
6008
  let probeCount = 1;
5770
6009
  let stableCount = 1;
5771
6010
  let lastFingerprint = probe.fingerprint;
5772
6011
  let interval = initialPollMs;
6012
+ let sawBusy = false;
6013
+ let busySpent = false;
6014
+ let extendedPastBudget = false;
6015
+ let graceDeadlineMs;
6016
+ const noteEvidence = () => {
6017
+ const elapsedMs = Date.now() - startMs;
6018
+ if (probe.loading) {
6019
+ if (busySpent) return;
6020
+ sawBusy = true;
6021
+ if (elapsedMs > maxMs) extendedPastBudget = true;
6022
+ return;
6023
+ }
6024
+ if (!sawBusy || busySpent) return;
6025
+ if (extendedPastBudget) {
6026
+ graceDeadlineMs = Math.min(busyMaxMs, elapsedMs + (stableTarget + 1) * pollMaxMs);
6027
+ }
6028
+ busySpent = true;
6029
+ };
6030
+ const remaining = () => {
6031
+ const elapsedMs = Date.now() - startMs;
6032
+ if (probe.loading && !busySpent) return busyMaxMs - elapsedMs;
6033
+ return Math.max(maxMs, graceDeadlineMs ?? 0) - elapsedMs;
6034
+ };
6035
+ noteEvidence();
5773
6036
  const looksImmediatelyClean = !probe.loading && !probe.pressedDisabled && (!pressedComponentId || probe.pressedFound);
5774
6037
  if (looksImmediatelyClean) {
5775
6038
  await sleepMs(Math.min(80, Math.max(0, remaining())));
@@ -5786,6 +6049,7 @@ async function waitForLoadingSettle(opts = {}) {
5786
6049
  };
5787
6050
  }
5788
6051
  probe = recheck;
6052
+ noteEvidence();
5789
6053
  lastFingerprint = recheck.fingerprint;
5790
6054
  stableCount = 1;
5791
6055
  }
@@ -5806,6 +6070,7 @@ async function waitForLoadingSettle(opts = {}) {
5806
6070
  }
5807
6071
  probe = probeLoadingState(pressedComponentId, currentScreen);
5808
6072
  probeCount++;
6073
+ noteEvidence();
5809
6074
  const quiescent = !probe.loading && !probe.pressedDisabled && probe.fingerprint === lastFingerprint;
5810
6075
  if (quiescent) {
5811
6076
  stableCount++;
@@ -5837,7 +6102,7 @@ async function waitForLoadingSettle(opts = {}) {
5837
6102
  }
5838
6103
 
5839
6104
  // src/version.ts
5840
- var SDK_VERSION2 = "0.11.0";
6105
+ var SDK_VERSION2 = "0.11.3";
5841
6106
  var FiberSentinel = class extends React2__default.default.Component {
5842
6107
  componentDidMount() {
5843
6108
  const fiber = this._reactInternals ?? this._reactInternalFiber ?? null;
@@ -5981,6 +6246,21 @@ function probeNameMangling(components) {
5981
6246
  return _lastProbe;
5982
6247
  }
5983
6248
 
6249
+ // src/platform/textInputFocus.ts
6250
+ function createTextInputFocusHost(textInput) {
6251
+ const read = textInput?.State?.currentlyFocusedInput;
6252
+ if (typeof read !== "function") return null;
6253
+ return {
6254
+ focusedInstance() {
6255
+ try {
6256
+ return textInput.State.currentlyFocusedInput() ?? null;
6257
+ } catch {
6258
+ return null;
6259
+ }
6260
+ }
6261
+ };
6262
+ }
6263
+
5984
6264
  // src/platform/fabricGeometry.ts
5985
6265
  function readRect(instance) {
5986
6266
  const candidate = instance;
@@ -6111,6 +6391,10 @@ function AppilotsProviderInner({
6111
6391
  setGeometryHost(createFabricGeometryHost(reactNative.Dimensions));
6112
6392
  return () => setGeometryHost(null);
6113
6393
  }, []);
6394
+ React2.useEffect(() => {
6395
+ setFocusHost(createTextInputFocusHost(reactNative.TextInput));
6396
+ return () => setFocusHost(null);
6397
+ }, []);
6114
6398
  const trackNativeDialogs = config.trackNativeDialogs !== false;
6115
6399
  React2.useEffect(() => {
6116
6400
  if (!trackNativeDialogs) return;
@@ -6234,6 +6518,7 @@ exports.componentRegistry = componentRegistry;
6234
6518
  exports.createComponentRegistry = createComponentRegistry;
6235
6519
  exports.createElementRegistry = createElementRegistry;
6236
6520
  exports.createListRegistry = createListRegistry;
6521
+ exports.createSnapshotDeltaTracker = createSnapshotDeltaTracker;
6237
6522
  exports.defaultDarkTheme = defaultDarkTheme;
6238
6523
  exports.defaultLightTheme = defaultLightTheme;
6239
6524
  exports.deriveInteractionElements = deriveInteractionElements;