@appilots/sdk 0.4.0 → 0.5.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,4 +1,4 @@
1
- import { getFiberRoot, getCurrentScreen, getActiveRouteNames, deriveInteractionElements, attachElementIdsToChoiceGroups, elementRegistry, appilotsDebugWarn, getDefaultRegistry, useAppilotsContext, ChatSessionMachine, getCurrentScreenSignature, ActionQueueMachine, getScreenMetadata, setCurrentScreen, useResolvedRegistry, listRegistry, componentRegistry, probeLoadingState, isListItemPressTarget, waitForLoadingSettle, waitForScreenSettle, screenDepartedBaseline, routesBelongToSameFeature, actionPressTargetId, getNavigationStateSnapshot, getNavigationRef, isOptionalStepAutomationFailure, OPTIONAL_STEP_AUTOMATION_HINT, normalize3, matchScore, idLooselyMatches, parseStableElementId, getAllScreens, snapshotShowsOpenCreateForm, looksDestructiveActionLabel, isGenericSelectValue } from './chunk-JBOLV5AM.mjs';
1
+ import { getFiberRoot, recordIntrospectionFailure, getCurrentScreen, getActiveRouteNames, deriveInteractionElements, attachElementIdsToChoiceGroups, elementRegistry, appilotsDebugWarn, getDefaultRegistry, useAppilotsContext, ChatSessionMachine, getCurrentScreenSignature, ActionQueueMachine, getScreenMetadata, setCurrentScreen, useResolvedRegistry, listRegistry, componentRegistry, probeLoadingState, isListItemPressTarget, waitForLoadingSettle, appilotsDebugLog, waitForScreenSettle, screenDepartedBaseline, routesBelongToSameFeature, actionPressTargetId, getNavigationStateSnapshot, getNavigationRef, isOptionalStepAutomationFailure, OPTIONAL_STEP_AUTOMATION_HINT, normalize3, matchScore, idLooselyMatches, parseStableElementId, getAllScreens, snapshotShowsOpenCreateForm, looksDestructiveActionLabel, isGenericSelectValue } from './chunk-BIEF6MNR.mjs';
2
2
  import { useMemo, useRef, useSyncExternalStore, useEffect, useCallback, useState } from 'react';
3
3
  import { Alert } from 'react-native';
4
4
 
@@ -748,6 +748,7 @@ function captureSnapshot() {
748
748
  console.warn(
749
749
  "[Appilots] captureSnapshot: fiber root not captured yet. Ensure <AppilotsProvider> has rendered before calling this."
750
750
  );
751
+ recordIntrospectionFailure("never-mounted", null);
751
752
  return {
752
753
  route: getCurrentScreen(),
753
754
  texts: [],
@@ -3267,7 +3268,9 @@ async function formFillHandler(payload, context) {
3267
3268
  const fieldEntry = findField(fieldId, registry);
3268
3269
  if (fieldEntry) {
3269
3270
  try {
3270
- console.log(`[Appilots] formFillHandler: found field "${fieldId}" via registry, calling setValue("${value}")`);
3271
+ appilotsDebugLog(
3272
+ `formFillHandler: found field "${fieldId}" via registry, calling setValue(<${String(value).length} chars>)`
3273
+ );
3271
3274
  const beforeValue = effectiveFieldType === "date" ? fieldEntry.getValue?.() ?? "" : void 0;
3272
3275
  fieldEntry.focus?.();
3273
3276
  fieldEntry.setValue(String(value));
@@ -3297,7 +3300,7 @@ async function formFillHandler(payload, context) {
3297
3300
  }
3298
3301
  if (!selectValueApplied(fieldId, value, after, registry)) {
3299
3302
  appilotsDebugWarn(
3300
- `formFillHandler: select field "${fieldId}" setValue("${value}") did not stick (current="${after}"), trying visible choice fallback`
3303
+ `formFillHandler: select field "${fieldId}" setValue did not stick (wrote <${String(value).length} chars>, current is <${String(after ?? "").length} chars>), trying visible choice fallback`
3301
3304
  );
3302
3305
  const selectResult = await selectVisibleChoice(fieldId, value, registry, {
3303
3306
  allowFirstVisibleWhenNoCue: !selectedChoiceThisBatch
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkV3TUIIT2_js = require('./chunk-V3TUIIT2.js');
3
+ var chunkPQHZTAFG_js = require('./chunk-PQHZTAFG.js');
4
4
  var React = require('react');
5
5
 
6
6
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -15,11 +15,11 @@ function getActiveRouteName(state) {
15
15
  return route.name;
16
16
  }
17
17
  function AppilotsNavigationContainer({ children }) {
18
- const { emit } = chunkV3TUIIT2_js.useAppilotsContext();
18
+ const { emit } = chunkPQHZTAFG_js.useAppilotsContext();
19
19
  const internalRef = React.useRef(null);
20
20
  const handleRef = React.useCallback((instance) => {
21
21
  internalRef.current = instance;
22
- chunkV3TUIIT2_js.setNavigationRef(instance);
22
+ chunkPQHZTAFG_js.setNavigationRef(instance);
23
23
  const childRef = children.ref;
24
24
  if (typeof childRef === "function") childRef(instance);
25
25
  else if (childRef && typeof childRef === "object") childRef.current = instance;
@@ -32,7 +32,7 @@ function AppilotsNavigationContainer({ children }) {
32
32
  const routeName = getActiveRouteName(state);
33
33
  if (routeName) {
34
34
  console.log(`[Appilots] Screen changed \u2192 "${routeName}"`);
35
- chunkV3TUIIT2_js.setCurrentScreen(routeName);
35
+ chunkPQHZTAFG_js.setCurrentScreen(routeName);
36
36
  emit({
37
37
  type: "navigation:change",
38
38
  timestamp: Date.now(),
@@ -48,7 +48,7 @@ function AppilotsNavigationContainer({ children }) {
48
48
  const route = internalRef.current?.getCurrentRoute?.();
49
49
  if (route?.name) {
50
50
  console.log(`[Appilots] Initial screen \u2192 "${route.name}"`);
51
- chunkV3TUIIT2_js.setCurrentScreen(route.name);
51
+ chunkPQHZTAFG_js.setCurrentScreen(route.name);
52
52
  emit({
53
53
  type: "navigation:change",
54
54
  timestamp: Date.now(),
@@ -44,6 +44,7 @@ var AppilotsClient = class {
44
44
  timeout;
45
45
  debug;
46
46
  mcpVersion;
47
+ introspectionReporter;
47
48
  user;
48
49
  sessionId = null;
49
50
  /** Guard so identify fires at most once per client instance. */
@@ -54,6 +55,7 @@ var AppilotsClient = class {
54
55
  this.timeout = options.timeout ?? 6e4;
55
56
  this.debug = options.debug ?? false;
56
57
  this.mcpVersion = options.mcpVersion;
58
+ this.introspectionReporter = options.introspectionReporter;
57
59
  this.user = options.user;
58
60
  this.headers = {
59
61
  "Content-Type": "application/json",
@@ -156,6 +158,21 @@ var AppilotsClient = class {
156
158
  getSessionId() {
157
159
  return this.sessionId;
158
160
  }
161
+ /**
162
+ * Wire fragment for introspection health. Empty on the happy path —
163
+ * a working client sends nothing, so the field costs a byte only
164
+ * when something is actually wrong. Never throws: a broken reporter
165
+ * must not take the message down with it.
166
+ */
167
+ introspectionFragment() {
168
+ try {
169
+ const report = this.introspectionReporter?.();
170
+ if (!report || report.captured !== false) return {};
171
+ return { introspection: report };
172
+ } catch {
173
+ return {};
174
+ }
175
+ }
159
176
  // ── Messages ──────────────────────────────────────────────────
160
177
  async sendMessage(content, context) {
161
178
  if (!this.sessionId) {
@@ -169,7 +186,8 @@ var AppilotsClient = class {
169
186
  context,
170
187
  // Optional MCP version bundled with this app build. Server
171
188
  // compares vs the active MCP doc to detect stale uploads.
172
- ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {}
189
+ ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
190
+ ...this.introspectionFragment()
173
191
  })
174
192
  });
175
193
  if (data.sessionId) {
@@ -396,7 +414,8 @@ var AppilotsClient = class {
396
414
  content,
397
415
  sessionId: this.sessionId,
398
416
  context,
399
- ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {}
417
+ ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
418
+ ...this.introspectionFragment()
400
419
  })
401
420
  );
402
421
  });
@@ -3622,15 +3641,32 @@ function getGlobalConfig() {
3622
3641
 
3623
3642
  // src/introspection/fiberRoot.ts
3624
3643
  var _fiberRoot = null;
3644
+ var _diagnostics = {
3645
+ captured: false,
3646
+ failureReason: null,
3647
+ reactVersion: null,
3648
+ failureCount: 0
3649
+ };
3625
3650
  function setFiberRoot(fiber) {
3626
3651
  if (_fiberRoot !== fiber) {
3627
3652
  console.log(`[Appilots] Introspection: fiber root captured (type=${typeName(fiber)})`);
3628
3653
  _fiberRoot = fiber;
3629
3654
  }
3655
+ _diagnostics.captured = true;
3630
3656
  }
3631
3657
  function getFiberRoot() {
3632
3658
  return _fiberRoot;
3633
3659
  }
3660
+ function recordIntrospectionFailure(reason, reactVersion) {
3661
+ _diagnostics.failureCount += 1;
3662
+ if (_diagnostics.failureReason === null) {
3663
+ _diagnostics.failureReason = reason;
3664
+ _diagnostics.reactVersion = reactVersion;
3665
+ }
3666
+ }
3667
+ function getIntrospectionDiagnostics() {
3668
+ return { ..._diagnostics };
3669
+ }
3634
3670
  function typeName(fiber) {
3635
3671
  const t = fiber?.type ?? fiber?.elementType;
3636
3672
  if (!t) return "unknown";
@@ -3639,15 +3675,16 @@ function typeName(fiber) {
3639
3675
  if (typeof t === "object") return t.displayName ?? t.render?.displayName ?? t.render?.name ?? "forwardRef/memo";
3640
3676
  return String(t);
3641
3677
  }
3642
-
3643
- // src/introspection/AppilotsFiberRoot.tsx
3644
3678
  var FiberSentinel = class extends React2.Component {
3645
3679
  componentDidMount() {
3646
3680
  const fiber = this._reactInternals ?? this._reactInternalFiber ?? null;
3647
3681
  if (!fiber) {
3682
+ const reactVersion = React2.version ?? null;
3648
3683
  console.warn(
3649
- "[Appilots] FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible."
3684
+ `[Appilots] FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible (react=${reactVersion}).`
3650
3685
  );
3686
+ recordIntrospectionFailure("sentinel-missing-internals", reactVersion);
3687
+ this.props.onUnavailable?.({ reactVersion });
3651
3688
  return;
3652
3689
  }
3653
3690
  let top = fiber;
@@ -3658,13 +3695,16 @@ var FiberSentinel = class extends React2.Component {
3658
3695
  return this.props.children ?? null;
3659
3696
  }
3660
3697
  };
3661
- function AppilotsFiberRoot({ children }) {
3698
+ function AppilotsFiberRoot({
3699
+ children,
3700
+ onUnavailable
3701
+ }) {
3662
3702
  const Internals = React2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
3663
3703
  const ownerFiber = Internals?.ReactCurrentOwner?.current;
3664
3704
  if (ownerFiber && !getFiberRoot()) {
3665
3705
  setFiberRoot(ownerFiber);
3666
3706
  }
3667
- return /* @__PURE__ */ React2.createElement(FiberSentinel, null, children);
3707
+ return /* @__PURE__ */ React2.createElement(FiberSentinel, { onUnavailable }, children);
3668
3708
  }
3669
3709
 
3670
3710
  // src/debug/logger.ts
@@ -3676,6 +3716,10 @@ function isDebugEnabled() {
3676
3716
  if (debugOverride !== void 0) return debugOverride;
3677
3717
  return getGlobalConfig()?.debug === true;
3678
3718
  }
3719
+ function appilotsDebugLog(...args) {
3720
+ if (!isDebugEnabled()) return;
3721
+ console.log("[Appilots]", ...args);
3722
+ }
3679
3723
  function appilotsDebugWarn(...args) {
3680
3724
  if (!isDebugEnabled()) return;
3681
3725
  console.log("[Appilots]", ...args);
@@ -3717,6 +3761,16 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3717
3761
  recordAppilotsEventTrace(event);
3718
3762
  listenersRef.current.forEach((handler) => handler(event));
3719
3763
  }, []);
3764
+ const handleIntrospectionUnavailable = useCallback(
3765
+ ({ reactVersion }) => {
3766
+ emit({
3767
+ type: "sdk:introspection:unavailable",
3768
+ timestamp: Date.now(),
3769
+ data: { reason: "sentinel-missing-internals", reactVersion }
3770
+ });
3771
+ },
3772
+ [emit]
3773
+ );
3720
3774
  const client = useMemo(
3721
3775
  () => externalClient ?? new AppilotsClient({
3722
3776
  projectId: config.projectId,
@@ -3725,7 +3779,9 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3725
3779
  debug: config.debug,
3726
3780
  appVersion: config.appVersion,
3727
3781
  mcpVersion: config.mcpVersion,
3728
- user: config.user
3782
+ user: config.user,
3783
+ // Only reports a FAILED reading; the happy path sends nothing.
3784
+ introspectionReporter: getIntrospectionDiagnostics
3729
3785
  }),
3730
3786
  [config.projectId, config.apiBaseUrl, config.apiKey, config.debug, config.appVersion, config.mcpVersion, config.user, externalClient]
3731
3787
  );
@@ -3746,7 +3802,7 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3746
3802
  () => ({ config, client, subscribe, emit, remotePersonalization }),
3747
3803
  [config, client, subscribe, emit, remotePersonalization]
3748
3804
  );
3749
- return /* @__PURE__ */ React2.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2.createElement(AppilotsFiberRoot, null, children));
3805
+ return /* @__PURE__ */ React2.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2.createElement(AppilotsFiberRoot, { onUnavailable: handleIntrospectionUnavailable }, children));
3750
3806
  }
3751
3807
  function useAppilotsContext() {
3752
3808
  const context = useContext(AppilotsContext);
@@ -4265,4 +4321,4 @@ function clearScreenRegistry() {
4265
4321
  screenRegistry.clear();
4266
4322
  }
4267
4323
 
4268
- export { ActionQueueMachine, AppilotsClient, AppilotsProvider, AppilotsRegistryProvider, ChatSessionMachine, OPTIONAL_STEP_AUTOMATION_HINT, SDK_VERSION, _patchJsxRuntimes, actionPressTargetId, appilotsDebugWarn, attachElementIdsToChoiceGroups, clearAppilotsDebugTraces, clearScreenRegistry, componentRegistry, createComponentRegistry, createElementRegistry, createListRegistry, defaultDarkTheme, defaultLightTheme, deriveInteractionElements, describeAction, elementRegistry, enableAppilotsAutoTracking, getActiveRouteNames, getAllScreens, getAppilotsDebugTraces, getCurrentScreen, getCurrentScreenSignature, getDefaultRegistry, getFiberRoot, getGlobalConfig, getNavigationRef, getNavigationStateSnapshot, getScreenMetadata, humanizeError, idLooselyMatches, initAppilots, isAutoTrackingEnabled, isGenericSelectValue, isListItemPressTarget, isOptionalStepAutomationFailure, listRegistry, looksDestructiveActionLabel, matchScore, mergeThemeTokens, normalize3, parseStableElementId, probeLoadingState, recordAppilotsDebugTrace, registerScreen, resolveBaseTheme, routesBelongToSameFeature, screenDepartedBaseline, setCurrentScreen, setNavigationRef, snapshotShowsOpenCreateForm, subscribeAppilotsDebugTraces, useAppilotsContext, useResolvedRegistry, waitForLoadingSettle, waitForScreenSettle };
4324
+ export { ActionQueueMachine, AppilotsClient, AppilotsProvider, AppilotsRegistryProvider, ChatSessionMachine, OPTIONAL_STEP_AUTOMATION_HINT, SDK_VERSION, _patchJsxRuntimes, actionPressTargetId, appilotsDebugLog, appilotsDebugWarn, attachElementIdsToChoiceGroups, clearAppilotsDebugTraces, clearScreenRegistry, componentRegistry, createComponentRegistry, createElementRegistry, createListRegistry, defaultDarkTheme, defaultLightTheme, deriveInteractionElements, describeAction, elementRegistry, enableAppilotsAutoTracking, getActiveRouteNames, getAllScreens, getAppilotsDebugTraces, getCurrentScreen, getCurrentScreenSignature, getDefaultRegistry, getFiberRoot, getGlobalConfig, getIntrospectionDiagnostics, getNavigationRef, getNavigationStateSnapshot, getScreenMetadata, humanizeError, idLooselyMatches, initAppilots, isAutoTrackingEnabled, isGenericSelectValue, isListItemPressTarget, isOptionalStepAutomationFailure, listRegistry, looksDestructiveActionLabel, matchScore, mergeThemeTokens, normalize3, parseStableElementId, probeLoadingState, recordAppilotsDebugTrace, recordIntrospectionFailure, registerScreen, resolveBaseTheme, routesBelongToSameFeature, screenDepartedBaseline, setCurrentScreen, setNavigationRef, snapshotShowsOpenCreateForm, subscribeAppilotsDebugTraces, useAppilotsContext, useResolvedRegistry, waitForLoadingSettle, waitForScreenSettle };
@@ -1,4 +1,4 @@
1
- import { useAppilotsContext, setNavigationRef, setCurrentScreen } from './chunk-JBOLV5AM.mjs';
1
+ import { useAppilotsContext, setNavigationRef, setCurrentScreen } from './chunk-BIEF6MNR.mjs';
2
2
  import React, { useRef, useCallback, useEffect } from 'react';
3
3
 
4
4
  function getActiveRouteName(state) {
@@ -50,6 +50,7 @@ var AppilotsClient = class {
50
50
  timeout;
51
51
  debug;
52
52
  mcpVersion;
53
+ introspectionReporter;
53
54
  user;
54
55
  sessionId = null;
55
56
  /** Guard so identify fires at most once per client instance. */
@@ -60,6 +61,7 @@ var AppilotsClient = class {
60
61
  this.timeout = options.timeout ?? 6e4;
61
62
  this.debug = options.debug ?? false;
62
63
  this.mcpVersion = options.mcpVersion;
64
+ this.introspectionReporter = options.introspectionReporter;
63
65
  this.user = options.user;
64
66
  this.headers = {
65
67
  "Content-Type": "application/json",
@@ -162,6 +164,21 @@ var AppilotsClient = class {
162
164
  getSessionId() {
163
165
  return this.sessionId;
164
166
  }
167
+ /**
168
+ * Wire fragment for introspection health. Empty on the happy path —
169
+ * a working client sends nothing, so the field costs a byte only
170
+ * when something is actually wrong. Never throws: a broken reporter
171
+ * must not take the message down with it.
172
+ */
173
+ introspectionFragment() {
174
+ try {
175
+ const report = this.introspectionReporter?.();
176
+ if (!report || report.captured !== false) return {};
177
+ return { introspection: report };
178
+ } catch {
179
+ return {};
180
+ }
181
+ }
165
182
  // ── Messages ──────────────────────────────────────────────────
166
183
  async sendMessage(content, context) {
167
184
  if (!this.sessionId) {
@@ -175,7 +192,8 @@ var AppilotsClient = class {
175
192
  context,
176
193
  // Optional MCP version bundled with this app build. Server
177
194
  // compares vs the active MCP doc to detect stale uploads.
178
- ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {}
195
+ ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
196
+ ...this.introspectionFragment()
179
197
  })
180
198
  });
181
199
  if (data.sessionId) {
@@ -402,7 +420,8 @@ var AppilotsClient = class {
402
420
  content,
403
421
  sessionId: this.sessionId,
404
422
  context,
405
- ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {}
423
+ ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
424
+ ...this.introspectionFragment()
406
425
  })
407
426
  );
408
427
  });
@@ -3628,15 +3647,32 @@ function getGlobalConfig() {
3628
3647
 
3629
3648
  // src/introspection/fiberRoot.ts
3630
3649
  var _fiberRoot = null;
3650
+ var _diagnostics = {
3651
+ captured: false,
3652
+ failureReason: null,
3653
+ reactVersion: null,
3654
+ failureCount: 0
3655
+ };
3631
3656
  function setFiberRoot(fiber) {
3632
3657
  if (_fiberRoot !== fiber) {
3633
3658
  console.log(`[Appilots] Introspection: fiber root captured (type=${typeName(fiber)})`);
3634
3659
  _fiberRoot = fiber;
3635
3660
  }
3661
+ _diagnostics.captured = true;
3636
3662
  }
3637
3663
  function getFiberRoot() {
3638
3664
  return _fiberRoot;
3639
3665
  }
3666
+ function recordIntrospectionFailure(reason, reactVersion) {
3667
+ _diagnostics.failureCount += 1;
3668
+ if (_diagnostics.failureReason === null) {
3669
+ _diagnostics.failureReason = reason;
3670
+ _diagnostics.reactVersion = reactVersion;
3671
+ }
3672
+ }
3673
+ function getIntrospectionDiagnostics() {
3674
+ return { ..._diagnostics };
3675
+ }
3640
3676
  function typeName(fiber) {
3641
3677
  const t = fiber?.type ?? fiber?.elementType;
3642
3678
  if (!t) return "unknown";
@@ -3645,15 +3681,16 @@ function typeName(fiber) {
3645
3681
  if (typeof t === "object") return t.displayName ?? t.render?.displayName ?? t.render?.name ?? "forwardRef/memo";
3646
3682
  return String(t);
3647
3683
  }
3648
-
3649
- // src/introspection/AppilotsFiberRoot.tsx
3650
3684
  var FiberSentinel = class extends React2__default.default.Component {
3651
3685
  componentDidMount() {
3652
3686
  const fiber = this._reactInternals ?? this._reactInternalFiber ?? null;
3653
3687
  if (!fiber) {
3688
+ const reactVersion = React2__default.default.version ?? null;
3654
3689
  console.warn(
3655
- "[Appilots] FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible."
3690
+ `[Appilots] FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible (react=${reactVersion}).`
3656
3691
  );
3692
+ recordIntrospectionFailure("sentinel-missing-internals", reactVersion);
3693
+ this.props.onUnavailable?.({ reactVersion });
3657
3694
  return;
3658
3695
  }
3659
3696
  let top = fiber;
@@ -3664,13 +3701,16 @@ var FiberSentinel = class extends React2__default.default.Component {
3664
3701
  return this.props.children ?? null;
3665
3702
  }
3666
3703
  };
3667
- function AppilotsFiberRoot({ children }) {
3704
+ function AppilotsFiberRoot({
3705
+ children,
3706
+ onUnavailable
3707
+ }) {
3668
3708
  const Internals = React2__default.default.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
3669
3709
  const ownerFiber = Internals?.ReactCurrentOwner?.current;
3670
3710
  if (ownerFiber && !getFiberRoot()) {
3671
3711
  setFiberRoot(ownerFiber);
3672
3712
  }
3673
- return /* @__PURE__ */ React2__default.default.createElement(FiberSentinel, null, children);
3713
+ return /* @__PURE__ */ React2__default.default.createElement(FiberSentinel, { onUnavailable }, children);
3674
3714
  }
3675
3715
 
3676
3716
  // src/debug/logger.ts
@@ -3682,6 +3722,10 @@ function isDebugEnabled() {
3682
3722
  if (debugOverride !== void 0) return debugOverride;
3683
3723
  return getGlobalConfig()?.debug === true;
3684
3724
  }
3725
+ function appilotsDebugLog(...args) {
3726
+ if (!isDebugEnabled()) return;
3727
+ console.log("[Appilots]", ...args);
3728
+ }
3685
3729
  function appilotsDebugWarn(...args) {
3686
3730
  if (!isDebugEnabled()) return;
3687
3731
  console.log("[Appilots]", ...args);
@@ -3723,6 +3767,16 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3723
3767
  recordAppilotsEventTrace(event);
3724
3768
  listenersRef.current.forEach((handler) => handler(event));
3725
3769
  }, []);
3770
+ const handleIntrospectionUnavailable = React2.useCallback(
3771
+ ({ reactVersion }) => {
3772
+ emit({
3773
+ type: "sdk:introspection:unavailable",
3774
+ timestamp: Date.now(),
3775
+ data: { reason: "sentinel-missing-internals", reactVersion }
3776
+ });
3777
+ },
3778
+ [emit]
3779
+ );
3726
3780
  const client = React2.useMemo(
3727
3781
  () => externalClient ?? new AppilotsClient({
3728
3782
  projectId: config.projectId,
@@ -3731,7 +3785,9 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3731
3785
  debug: config.debug,
3732
3786
  appVersion: config.appVersion,
3733
3787
  mcpVersion: config.mcpVersion,
3734
- user: config.user
3788
+ user: config.user,
3789
+ // Only reports a FAILED reading; the happy path sends nothing.
3790
+ introspectionReporter: getIntrospectionDiagnostics
3735
3791
  }),
3736
3792
  [config.projectId, config.apiBaseUrl, config.apiKey, config.debug, config.appVersion, config.mcpVersion, config.user, externalClient]
3737
3793
  );
@@ -3752,7 +3808,7 @@ function AppilotsProvider({ config: configProp, children, client: externalClient
3752
3808
  () => ({ config, client, subscribe, emit, remotePersonalization }),
3753
3809
  [config, client, subscribe, emit, remotePersonalization]
3754
3810
  );
3755
- return /* @__PURE__ */ React2__default.default.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2__default.default.createElement(AppilotsFiberRoot, null, children));
3811
+ return /* @__PURE__ */ React2__default.default.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2__default.default.createElement(AppilotsFiberRoot, { onUnavailable: handleIntrospectionUnavailable }, children));
3756
3812
  }
3757
3813
  function useAppilotsContext() {
3758
3814
  const context = React2.useContext(AppilotsContext);
@@ -4280,6 +4336,7 @@ exports.OPTIONAL_STEP_AUTOMATION_HINT = OPTIONAL_STEP_AUTOMATION_HINT;
4280
4336
  exports.SDK_VERSION = SDK_VERSION;
4281
4337
  exports._patchJsxRuntimes = _patchJsxRuntimes;
4282
4338
  exports.actionPressTargetId = actionPressTargetId;
4339
+ exports.appilotsDebugLog = appilotsDebugLog;
4283
4340
  exports.appilotsDebugWarn = appilotsDebugWarn;
4284
4341
  exports.attachElementIdsToChoiceGroups = attachElementIdsToChoiceGroups;
4285
4342
  exports.clearAppilotsDebugTraces = clearAppilotsDebugTraces;
@@ -4302,6 +4359,7 @@ exports.getCurrentScreenSignature = getCurrentScreenSignature;
4302
4359
  exports.getDefaultRegistry = getDefaultRegistry;
4303
4360
  exports.getFiberRoot = getFiberRoot;
4304
4361
  exports.getGlobalConfig = getGlobalConfig;
4362
+ exports.getIntrospectionDiagnostics = getIntrospectionDiagnostics;
4305
4363
  exports.getNavigationRef = getNavigationRef;
4306
4364
  exports.getNavigationStateSnapshot = getNavigationStateSnapshot;
4307
4365
  exports.getScreenMetadata = getScreenMetadata;
@@ -4320,6 +4378,7 @@ exports.normalize3 = normalize3;
4320
4378
  exports.parseStableElementId = parseStableElementId;
4321
4379
  exports.probeLoadingState = probeLoadingState;
4322
4380
  exports.recordAppilotsDebugTrace = recordAppilotsDebugTrace;
4381
+ exports.recordIntrospectionFailure = recordIntrospectionFailure;
4323
4382
  exports.registerScreen = registerScreen;
4324
4383
  exports.resolveBaseTheme = resolveBaseTheme;
4325
4384
  exports.routesBelongToSameFeature = routesBelongToSameFeature;