@appilots/sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GEM6TPAK.mjs → chunk-42WI3UDB.mjs} +312 -1635
- package/dist/{chunk-KGN3DNGX.js → chunk-CHY5D2PV.js} +5 -5
- package/dist/{chunk-KCDNL4HV.mjs → chunk-GEKCGVJN.mjs} +1 -1
- package/dist/{chunk-HDLZGXNF.mjs → chunk-JBOLV5AM.mjs} +2016 -96
- package/dist/{chunk-SIBKF7AA.js → chunk-V3TUIIT2.js} +2036 -97
- package/dist/{chunk-IASJ7YHC.js → chunk-Y7EA6R2G.js} +412 -1735
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +11 -11
- package/dist/hooks/index.mjs +2 -2
- package/dist/{index-BDb1WVNo.d.mts → index-Cb0D6uKH.d.mts} +367 -124
- package/dist/{index-BDb1WVNo.d.ts → index-Cb0D6uKH.d.ts} +367 -124
- package/dist/index.d.mts +109 -341
- package/dist/index.d.ts +109 -341
- package/dist/index.js +84 -462
- package/dist/index.mjs +10 -403
- package/dist/navigation/index.js +13 -13
- package/dist/navigation/index.mjs +2 -2
- package/hooks/package.json +9 -0
- package/metro.d.ts +49 -0
- package/metro.js +0 -37
- package/navigation/package.json +9 -0
- package/package.json +45 -25
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkV3TUIIT2_js = require('./chunk-V3TUIIT2.js');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var reactNative = require('react-native');
|
|
6
6
|
|
|
@@ -495,7 +495,7 @@ function walkSubtree(root, detectLists, startInModal = false) {
|
|
|
495
495
|
}
|
|
496
496
|
function walkFiber(root) {
|
|
497
497
|
const snapshot = {
|
|
498
|
-
route:
|
|
498
|
+
route: chunkV3TUIIT2_js.getCurrentScreen(),
|
|
499
499
|
texts: [],
|
|
500
500
|
inputs: [],
|
|
501
501
|
buttons: [],
|
|
@@ -656,225 +656,6 @@ function normalizeLabel(label) {
|
|
|
656
656
|
return label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase()).replace(/\s+/g, "");
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
-
// src/introspection/interactionGraph.ts
|
|
660
|
-
function normalizePart(value) {
|
|
661
|
-
return String(value ?? "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
662
|
-
}
|
|
663
|
-
function hashString(value) {
|
|
664
|
-
let hash = 2166136261;
|
|
665
|
-
for (let i = 0; i < value.length; i++) {
|
|
666
|
-
hash ^= value.charCodeAt(i);
|
|
667
|
-
hash = Math.imul(hash, 16777619);
|
|
668
|
-
}
|
|
669
|
-
return (hash >>> 0).toString(36);
|
|
670
|
-
}
|
|
671
|
-
function stableElementId(parts) {
|
|
672
|
-
const raw = parts.filter((p) => p !== void 0 && String(p).length > 0).join("|");
|
|
673
|
-
const readable = parts.map(normalizePart).filter(Boolean).join(":").slice(0, 120);
|
|
674
|
-
return `el:${readable}:${hashString(raw)}`;
|
|
675
|
-
}
|
|
676
|
-
function parseStableElementId(id) {
|
|
677
|
-
if (typeof id !== "string") return null;
|
|
678
|
-
const trimmed = id.trim();
|
|
679
|
-
if (!/^el:/i.test(trimmed)) return null;
|
|
680
|
-
const parts = trimmed.split(":");
|
|
681
|
-
if (parts.length < 3) return null;
|
|
682
|
-
const hash = parts[parts.length - 1];
|
|
683
|
-
const segments = parts.slice(1, -1);
|
|
684
|
-
if (segments.length === 0) return null;
|
|
685
|
-
const role = segments[0];
|
|
686
|
-
const result = { role, segments, hash };
|
|
687
|
-
if (role === "option" && segments.length >= 4) {
|
|
688
|
-
result.listId = segments[segments.length - 2];
|
|
689
|
-
result.itemKey = segments[segments.length - 1];
|
|
690
|
-
}
|
|
691
|
-
return result;
|
|
692
|
-
}
|
|
693
|
-
function uniqueTexts(texts) {
|
|
694
|
-
const seen = /* @__PURE__ */ new Set();
|
|
695
|
-
const out = [];
|
|
696
|
-
for (const text of texts) {
|
|
697
|
-
const t = typeof text === "string" ? text.trim() : "";
|
|
698
|
-
if (!t || seen.has(t)) continue;
|
|
699
|
-
seen.add(t);
|
|
700
|
-
out.push(t);
|
|
701
|
-
}
|
|
702
|
-
return out;
|
|
703
|
-
}
|
|
704
|
-
function labelForListItem(item) {
|
|
705
|
-
return uniqueTexts([
|
|
706
|
-
...item.texts ?? [],
|
|
707
|
-
...(item.buttons ?? []).map((button) => button.label ?? button.id)
|
|
708
|
-
]).slice(0, 2).join(" \xB7 ") || void 0;
|
|
709
|
-
}
|
|
710
|
-
function listItemElement(route, list, item) {
|
|
711
|
-
const texts = uniqueTexts([
|
|
712
|
-
...item.texts ?? [],
|
|
713
|
-
...(item.buttons ?? []).map((button) => button.label ?? button.id)
|
|
714
|
-
]);
|
|
715
|
-
const label = labelForListItem(item);
|
|
716
|
-
const key = item.itemKey ?? item.reactKey ?? label ?? item.syntheticId;
|
|
717
|
-
const id = stableElementId([
|
|
718
|
-
"option",
|
|
719
|
-
route ?? "unknown",
|
|
720
|
-
list.id ?? list.label ?? `list-${list.index}`,
|
|
721
|
-
key
|
|
722
|
-
]);
|
|
723
|
-
const inModal = (item.buttons ?? []).some((button) => button.inModal) || (item.inputs ?? []).some((input) => input.inModal) || (item.toggles ?? []).some((toggle) => toggle.inModal);
|
|
724
|
-
return {
|
|
725
|
-
id,
|
|
726
|
-
role: "option",
|
|
727
|
-
label,
|
|
728
|
-
texts,
|
|
729
|
-
actions: ["press"],
|
|
730
|
-
disabled: false,
|
|
731
|
-
source: "list",
|
|
732
|
-
targetId: item.syntheticId,
|
|
733
|
-
...inModal ? { inModal: true } : {},
|
|
734
|
-
listContext: {
|
|
735
|
-
listIndex: list.index,
|
|
736
|
-
listId: list.id,
|
|
737
|
-
listLabel: list.label,
|
|
738
|
-
itemIndex: item.index,
|
|
739
|
-
itemKey: item.itemKey,
|
|
740
|
-
reactKey: item.reactKey,
|
|
741
|
-
syntheticId: item.syntheticId
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
function buttonElement(route, button, index) {
|
|
746
|
-
const label = button.label ?? button.id;
|
|
747
|
-
const id = stableElementId([
|
|
748
|
-
"button",
|
|
749
|
-
route ?? "unknown",
|
|
750
|
-
button.id ?? label ?? `button-${index}`
|
|
751
|
-
]);
|
|
752
|
-
return {
|
|
753
|
-
id,
|
|
754
|
-
role: "button",
|
|
755
|
-
label,
|
|
756
|
-
texts: uniqueTexts([label]),
|
|
757
|
-
actions: ["press"],
|
|
758
|
-
disabled: button.disabled,
|
|
759
|
-
selected: button.selected,
|
|
760
|
-
source: "button",
|
|
761
|
-
targetId: button.id ?? button.label,
|
|
762
|
-
...button.inModal ? { inModal: true } : {}
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
function inputElement(route, input, index) {
|
|
766
|
-
const label = input.label ?? input.placeholder ?? input.id;
|
|
767
|
-
const id = stableElementId([
|
|
768
|
-
"input",
|
|
769
|
-
route ?? "unknown",
|
|
770
|
-
input.id ?? label ?? `input-${index}`
|
|
771
|
-
]);
|
|
772
|
-
return {
|
|
773
|
-
id,
|
|
774
|
-
role: "input",
|
|
775
|
-
label,
|
|
776
|
-
texts: uniqueTexts([label, input.placeholder]),
|
|
777
|
-
actions: ["focus", "setValue"],
|
|
778
|
-
disabled: input.editable === false,
|
|
779
|
-
source: "input",
|
|
780
|
-
targetId: input.id ?? input.label ?? input.placeholder,
|
|
781
|
-
...input.inModal ? { inModal: true } : {}
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
function toggleElement(route, toggle, index) {
|
|
785
|
-
const label = toggle.label ?? toggle.id;
|
|
786
|
-
const id = stableElementId([
|
|
787
|
-
"toggle",
|
|
788
|
-
route ?? "unknown",
|
|
789
|
-
toggle.id ?? label ?? `toggle-${index}`
|
|
790
|
-
]);
|
|
791
|
-
return {
|
|
792
|
-
id,
|
|
793
|
-
role: "toggle",
|
|
794
|
-
label,
|
|
795
|
-
texts: uniqueTexts([label]),
|
|
796
|
-
actions: ["toggle", "press"],
|
|
797
|
-
selected: toggle.value,
|
|
798
|
-
source: "toggle",
|
|
799
|
-
targetId: toggle.id ?? toggle.label,
|
|
800
|
-
...toggle.inModal ? { inModal: true } : {}
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
function sliderElement(route, slider, index) {
|
|
804
|
-
const label = slider.label ?? slider.id;
|
|
805
|
-
const id = stableElementId([
|
|
806
|
-
"slider",
|
|
807
|
-
route ?? "unknown",
|
|
808
|
-
slider.id ?? label ?? `slider-${index}`
|
|
809
|
-
]);
|
|
810
|
-
return {
|
|
811
|
-
id,
|
|
812
|
-
role: "slider",
|
|
813
|
-
label,
|
|
814
|
-
texts: uniqueTexts([label]),
|
|
815
|
-
actions: ["setValue"],
|
|
816
|
-
disabled: slider.disabled,
|
|
817
|
-
source: "slider",
|
|
818
|
-
targetId: slider.id ?? slider.label,
|
|
819
|
-
...slider.inModal ? { inModal: true } : {}
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
function deriveInteractionElements(snapshot) {
|
|
823
|
-
const elements = [];
|
|
824
|
-
const seen = /* @__PURE__ */ new Set();
|
|
825
|
-
const push = (element) => {
|
|
826
|
-
if (!element.id || seen.has(element.id)) return;
|
|
827
|
-
seen.add(element.id);
|
|
828
|
-
elements.push(element);
|
|
829
|
-
};
|
|
830
|
-
for (const list of snapshot.lists ?? []) {
|
|
831
|
-
for (const item of list.items ?? []) {
|
|
832
|
-
push(listItemElement(snapshot.route, list, item));
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
for (const [index, button] of (snapshot.buttons ?? []).entries()) {
|
|
836
|
-
push(buttonElement(snapshot.route, button, index));
|
|
837
|
-
}
|
|
838
|
-
for (const [index, input] of (snapshot.inputs ?? []).entries()) {
|
|
839
|
-
push(inputElement(snapshot.route, input, index));
|
|
840
|
-
}
|
|
841
|
-
for (const [index, toggle] of (snapshot.toggles ?? []).entries()) {
|
|
842
|
-
push(toggleElement(snapshot.route, toggle, index));
|
|
843
|
-
}
|
|
844
|
-
for (const [index, slider] of (snapshot.sliders ?? []).entries()) {
|
|
845
|
-
push(sliderElement(snapshot.route, slider, index));
|
|
846
|
-
}
|
|
847
|
-
return elements;
|
|
848
|
-
}
|
|
849
|
-
function findElementForChoice(elements, option) {
|
|
850
|
-
if (option.targetId) {
|
|
851
|
-
const byTarget = elements.find((element) => element.targetId === option.targetId);
|
|
852
|
-
if (byTarget) return byTarget;
|
|
853
|
-
}
|
|
854
|
-
const optionLabel = normalizePart(option.label ?? option.texts?.join(" "));
|
|
855
|
-
if (!optionLabel) return void 0;
|
|
856
|
-
return elements.find((element) => {
|
|
857
|
-
if (element.role !== "option" && element.role !== "button") return false;
|
|
858
|
-
const label = normalizePart(element.label ?? element.texts.join(" "));
|
|
859
|
-
return label === optionLabel || label.includes(optionLabel) || optionLabel.includes(label);
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
function attachElementIdsToChoiceGroups(choiceGroups, elements) {
|
|
863
|
-
return choiceGroups.map((group) => ({
|
|
864
|
-
...group,
|
|
865
|
-
options: group.options.map((option) => {
|
|
866
|
-
const element = findElementForChoice(elements, option);
|
|
867
|
-
if (!element) return option;
|
|
868
|
-
const handle = element.listContext?.syntheticId ?? element.id;
|
|
869
|
-
return {
|
|
870
|
-
...option,
|
|
871
|
-
syntheticId: handle,
|
|
872
|
-
targetId: handle
|
|
873
|
-
};
|
|
874
|
-
})
|
|
875
|
-
}));
|
|
876
|
-
}
|
|
877
|
-
|
|
878
659
|
// src/introspection/captureSnapshot.ts
|
|
879
660
|
function normalizeSliderKey(value) {
|
|
880
661
|
return (value ?? "").toLowerCase().replace(/^slider[-_]/, "");
|
|
@@ -890,9 +671,9 @@ function fiberSliderMatches(fiber, registryId, registryLabel) {
|
|
|
890
671
|
function reconcileSliders(snapshot) {
|
|
891
672
|
const fiberSliders = snapshot.sliders;
|
|
892
673
|
const out = [];
|
|
893
|
-
for (const { id, kind } of
|
|
674
|
+
for (const { id, kind } of chunkV3TUIIT2_js.componentRegistry.snapshot()) {
|
|
894
675
|
if (kind !== "slider") continue;
|
|
895
|
-
const entry =
|
|
676
|
+
const entry = chunkV3TUIIT2_js.componentRegistry.getSlider(id);
|
|
896
677
|
if (!entry) continue;
|
|
897
678
|
const fiber = fiberSliders.find((s) => fiberSliderMatches(s, id, entry.label));
|
|
898
679
|
const visible = !!fiber || typeof entry.screen === "string" && entry.screen === snapshot.route;
|
|
@@ -916,7 +697,7 @@ function reconcileSliders(snapshot) {
|
|
|
916
697
|
snapshot.sliders = out;
|
|
917
698
|
}
|
|
918
699
|
function mergeRegisteredLists(snapshot) {
|
|
919
|
-
const registered =
|
|
700
|
+
const registered = chunkV3TUIIT2_js.listRegistry.snapshot();
|
|
920
701
|
if (registered.length === 0) return;
|
|
921
702
|
const byId = /* @__PURE__ */ new Map();
|
|
922
703
|
for (const list of snapshot.lists) {
|
|
@@ -964,13 +745,13 @@ function mergeRegisteredLists(snapshot) {
|
|
|
964
745
|
}
|
|
965
746
|
}
|
|
966
747
|
function captureSnapshot() {
|
|
967
|
-
const root =
|
|
748
|
+
const root = chunkV3TUIIT2_js.getFiberRoot();
|
|
968
749
|
if (!root) {
|
|
969
750
|
console.warn(
|
|
970
751
|
"[Appilots] captureSnapshot: fiber root not captured yet. Ensure <AppilotsProvider> has rendered before calling this."
|
|
971
752
|
);
|
|
972
753
|
return {
|
|
973
|
-
route:
|
|
754
|
+
route: chunkV3TUIIT2_js.getCurrentScreen(),
|
|
974
755
|
texts: [],
|
|
975
756
|
inputs: [],
|
|
976
757
|
buttons: [],
|
|
@@ -985,17 +766,17 @@ function captureSnapshot() {
|
|
|
985
766
|
}
|
|
986
767
|
const start = Date.now();
|
|
987
768
|
const snapshot = walkFiber(root);
|
|
988
|
-
const activePath =
|
|
769
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
989
770
|
const deepestRoute = activePath[activePath.length - 1];
|
|
990
771
|
if (deepestRoute) snapshot.route = deepestRoute;
|
|
991
772
|
mergeRegisteredLists(snapshot);
|
|
992
773
|
reconcileSliders(snapshot);
|
|
993
|
-
snapshot.elements = deriveInteractionElements(snapshot);
|
|
994
|
-
snapshot.choiceGroups = attachElementIdsToChoiceGroups(
|
|
774
|
+
snapshot.elements = chunkV3TUIIT2_js.deriveInteractionElements(snapshot);
|
|
775
|
+
snapshot.choiceGroups = chunkV3TUIIT2_js.attachElementIdsToChoiceGroups(
|
|
995
776
|
snapshot.choiceGroups,
|
|
996
777
|
snapshot.elements
|
|
997
778
|
);
|
|
998
|
-
|
|
779
|
+
chunkV3TUIIT2_js.elementRegistry.replaceAll(snapshot.elements);
|
|
999
780
|
const elapsed = Date.now() - start;
|
|
1000
781
|
const totalListItems = snapshot.lists.reduce(
|
|
1001
782
|
(acc, l) => acc + l.items.length,
|
|
@@ -1188,7 +969,7 @@ function wrapTouchable(fiber, label) {
|
|
|
1188
969
|
};
|
|
1189
970
|
}
|
|
1190
971
|
function findInteractiveByIdentifier(query, options = {}) {
|
|
1191
|
-
const root =
|
|
972
|
+
const root = chunkV3TUIIT2_js.getFiberRoot();
|
|
1192
973
|
if (!root) {
|
|
1193
974
|
console.warn("[Appilots] findInteractiveByIdentifier: fiber root not captured yet");
|
|
1194
975
|
return null;
|
|
@@ -1215,938 +996,23 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
1215
996
|
const score = scoreMatch(fiber, queryNorm, queryRaw);
|
|
1216
997
|
if (score > bestScore) {
|
|
1217
998
|
bestScore = score;
|
|
1218
|
-
const props = fiber.memoizedProps ?? {};
|
|
1219
|
-
bestId2 = props.testID ?? props.accessibilityLabel ?? props.placeholder ?? (kind === "target" ? findChildText2(fiber) : void 0);
|
|
1220
|
-
if (kind === "field") bestHandle = wrapTextInput(fiber);
|
|
1221
|
-
else if (kind === "toggle") bestHandle = wrapSwitch(fiber);
|
|
1222
|
-
else bestHandle = wrapTouchable(fiber, findChildText2(fiber));
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
if (fiber.child) stack.push(fiber.child);
|
|
1226
|
-
}
|
|
1227
|
-
if (bestHandle) {
|
|
1228
|
-
console.log(
|
|
1229
|
-
`[Appilots] findInteractiveByIdentifier: query="${query}" \u2192 kind=${bestHandle.kind} label="${bestHandle.label ?? bestId2}" score=${bestScore}`
|
|
1230
|
-
);
|
|
1231
|
-
} else {
|
|
1232
|
-
console.log(`[Appilots] findInteractiveByIdentifier: query="${query}" \u2192 no match`);
|
|
1233
|
-
}
|
|
1234
|
-
return bestHandle;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
// src/hooks/fingerprint.ts
|
|
1238
|
-
function fingerprintAction(action, screen) {
|
|
1239
|
-
const p = action.payload ?? {};
|
|
1240
|
-
const scope = `@${screen ?? "?"}`;
|
|
1241
|
-
switch (action.type) {
|
|
1242
|
-
case "navigate":
|
|
1243
|
-
return `navigate${scope}:${p.screenName ?? "?"}`;
|
|
1244
|
-
case "form_fill": {
|
|
1245
|
-
const fields = p.fields ?? [];
|
|
1246
|
-
const ids = fields.map((f) => String(f?.fieldId ?? "?")).sort();
|
|
1247
|
-
return `form_fill${scope}:${ids.join(",")}`;
|
|
1248
|
-
}
|
|
1249
|
-
case "ui_interaction":
|
|
1250
|
-
return `ui_interaction${scope}:${p.action ?? "?"}:${p.componentId ?? p.targetId ?? "?"}`;
|
|
1251
|
-
case "confirm":
|
|
1252
|
-
return `confirm${scope}:${p.message ?? p.title ?? "?"}`;
|
|
1253
|
-
default:
|
|
1254
|
-
return `${action.type}${scope}:${JSON.stringify(p)}`;
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
// src/hooks/visibleMessages.ts
|
|
1259
|
-
var INTERNAL_MARKER_RE = /^\[(?:Permission policy notice|Recovery exhausted|User rejected the proposed action)[^\]]*\]:\s*/i;
|
|
1260
|
-
var TECHNICAL_TRACE_RE = /(^|\n)\s*(?:Results:|[-*]\s*[✓✗]\s+|(?:press|click|tap|navigate|form_fill|ui_interaction|scroll_list)\b|(?:Cliquei|Clicked)\s+em\s+(?:el:|list-\d+-item-\d+|[A-Za-z]+:))/i;
|
|
1261
|
-
var TECHNICAL_SELECTOR_RE = /\b(?:el:[^\s]+|list-\d+-item-\d+|[A-Za-z][\w-]*:(?!\/\/)[^\s]+|(?:testID|test-id|accessibilityId|accessibility-id)[:=_-][^\s]+)/i;
|
|
1262
|
-
function sanitizeVisibleAssistantContent(content) {
|
|
1263
|
-
return content.split("\n").map((line) => line.replace(INTERNAL_MARKER_RE, "").trimEnd()).filter((line) => !INTERNAL_MARKER_RE.test(line)).join("\n").trim();
|
|
1264
|
-
}
|
|
1265
|
-
function humanizeAssistantFallback(content) {
|
|
1266
|
-
let text = sanitizeVisibleAssistantContent(content).replace(/\blist-\d+-item-\d+\b/gi, "o item da lista").replace(/\bel:[^\s".]+/gi, "o elemento").replace(
|
|
1267
|
-
/"\s*(?:press|click|tap|navigate|form_fill|ui_interaction|scroll_list)\s+on\s+[^"]+"/gi,
|
|
1268
|
-
'"a a\xE7\xE3o solicitada"'
|
|
1269
|
-
).replace(/\bsubscription-cancel-[^\s"]+/gi, "cancelamento de assinatura").trim();
|
|
1270
|
-
if (text.length < 16) return null;
|
|
1271
|
-
const probe = {
|
|
1272
|
-
role: "assistant",
|
|
1273
|
-
content: text};
|
|
1274
|
-
if (isInternalTraceMessage(probe)) return null;
|
|
1275
|
-
return text;
|
|
1276
|
-
}
|
|
1277
|
-
function isInternalTraceMessage(message) {
|
|
1278
|
-
if (message.role !== "assistant") return false;
|
|
1279
|
-
const content = message.content.trim();
|
|
1280
|
-
if (!content) return true;
|
|
1281
|
-
return TECHNICAL_TRACE_RE.test(content) || TECHNICAL_SELECTOR_RE.test(content);
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
// src/hooks/continuationErrors.ts
|
|
1285
|
-
function normalize2(value) {
|
|
1286
|
-
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[‘’]/g, "'").toLowerCase();
|
|
1287
|
-
}
|
|
1288
|
-
function detectLocaleFromUserTexts(texts) {
|
|
1289
|
-
for (const raw of texts) {
|
|
1290
|
-
const normalized = normalize2(raw);
|
|
1291
|
-
if (/[ãõáéíóúâêôç]/i.test(raw) || /\b(voce|voces|pra|assinatura|cancela|canela|segunda|certo|quais|tenho|pode|posso)\b/.test(
|
|
1292
|
-
normalized
|
|
1293
|
-
)) {
|
|
1294
|
-
return "pt";
|
|
1295
|
-
}
|
|
1296
|
-
if (/[áéíóúñ¿¡]/i.test(raw) || /\b(cancela|segunda|suscripcion|puedo|cuales|hola)\b/.test(normalized)) {
|
|
1297
|
-
return "es";
|
|
1298
|
-
}
|
|
1299
|
-
if (/[àâçéèêëîïôûùüÿœ]/i.test(raw) || /\b(annuler|deuxieme|abonnement|bonjour)\b/.test(normalized)) {
|
|
1300
|
-
return "fr";
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
return "en";
|
|
1304
|
-
}
|
|
1305
|
-
var copy = {
|
|
1306
|
-
pt: {
|
|
1307
|
-
rateLimit: "O provedor de IA atingiu o limite de uso durante a automa\xE7\xE3o. Aguarde um momento e pe\xE7a para continuar, ou aumente o limite de tokens por minuto.",
|
|
1308
|
-
invalidInput: "N\xE3o consegui continuar a automa\xE7\xE3o por um problema interno de sincroniza\xE7\xE3o. Pode tentar de novo ou fazer essa parte manualmente?",
|
|
1309
|
-
generic: (detail) => `Perdi o fio da automa\xE7\xE3o (${detail}). Pode continuar manualmente ou me pedir de novo?`
|
|
1310
|
-
},
|
|
1311
|
-
es: {
|
|
1312
|
-
rateLimit: "El proveedor de IA alcanz\xF3 su l\xEDmite de uso durante la automatizaci\xF3n. Espera un momento y p\xEDdeme continuar, o aumenta el l\xEDmite de tokens por minuto.",
|
|
1313
|
-
invalidInput: "No pude continuar la automatizaci\xF3n por un problema interno de sincronizaci\xF3n. \xBFPuedes intentarlo de nuevo o hacer esta parte manualmente?",
|
|
1314
|
-
generic: (detail) => `Perd\xED el hilo de la automatizaci\xF3n (${detail}). \xBFPuedes continuar manualmente o ped\xEDrmelo de nuevo?`
|
|
1315
|
-
},
|
|
1316
|
-
fr: {
|
|
1317
|
-
rateLimit: "Le fournisseur d'IA a atteint sa limite d'utilisation pendant l'automatisation. Attendez un instant et demandez-moi de continuer, ou augmentez la limite de tokens par minute.",
|
|
1318
|
-
invalidInput: "Je n'ai pas pu poursuivre l'automatisation \xE0 cause d'un probl\xE8me interne de synchronisation. Pouvez-vous r\xE9essayer ou faire cette \xE9tape manuellement ?",
|
|
1319
|
-
generic: (detail) => `J'ai perdu le fil de l'automatisation (${detail}). Vous pouvez continuer manuellement ou me redemander ?`
|
|
1320
|
-
},
|
|
1321
|
-
en: {
|
|
1322
|
-
rateLimit: "Your AI provider hit its rate limit during automation. Wait a moment and ask me to continue, or raise your TPM limit.",
|
|
1323
|
-
invalidInput: "I could not continue the automation because of an internal sync issue. You can try again or take over manually.",
|
|
1324
|
-
generic: (detail) => `I lost my footing during automation (${detail}). You can take over from here.`
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
function buildContinuationFailureMessage(rawError, recentUserTexts) {
|
|
1328
|
-
const locale = detectLocaleFromUserTexts(recentUserTexts);
|
|
1329
|
-
const strings = copy[locale] ?? copy.en;
|
|
1330
|
-
if (/rate limit|tokens per min|tpm/i.test(rawError)) {
|
|
1331
|
-
return strings.rateLimit;
|
|
1332
|
-
}
|
|
1333
|
-
if (/invalid input|validation_error|422/i.test(rawError)) {
|
|
1334
|
-
return strings.invalidInput;
|
|
1335
|
-
}
|
|
1336
|
-
return strings.generic(rawError);
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
// src/executor/formWriteEffect.ts
|
|
1340
|
-
var CREATE_FORM_ROUTE_RE = /vehiclecreate|subscriptioncreate|vehicleedit|subscriptionedit|maintenancecreate|create|cadastr|register|novo|edit/i;
|
|
1341
|
-
var SUBMIT_BUTTON_RE = /cadastrar|registrar|salvar|submit|register|guardar|enviar|confirmar|save/i;
|
|
1342
|
-
var FORM_VALIDATION_TEXT_RE = /(inv[aá]lid|obrigat[oó]ri|required|invalid|j[aá]\s+existe|already\s+exists|duplicad|formato|format|too\s+short|muito\s+curto|n[aã]o\s+(?:foi|pode)|must\s+be|deve\s+ser|selecione|escolha|select\s+(?:the|a|o|an))/i;
|
|
1343
|
-
function normalize3(value) {
|
|
1344
|
-
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
1345
|
-
}
|
|
1346
|
-
function routeHintsCreateForm(routeNames) {
|
|
1347
|
-
return routeNames.some((name) => CREATE_FORM_ROUTE_RE.test(normalize3(name)));
|
|
1348
|
-
}
|
|
1349
|
-
function snapshotHasSubmitControl(snap) {
|
|
1350
|
-
return (snap.buttons ?? []).some(
|
|
1351
|
-
(btn) => SUBMIT_BUTTON_RE.test(normalize3(`${btn.label ?? btn.id ?? ""}`))
|
|
1352
|
-
);
|
|
1353
|
-
}
|
|
1354
|
-
function snapshotHasVisibleSubmitButton(snap) {
|
|
1355
|
-
return (snap.buttons ?? []).some(
|
|
1356
|
-
(btn) => !btn.disabled && SUBMIT_BUTTON_RE.test(normalize3(`${btn.label ?? btn.id ?? ""}`))
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
function snapshotHasVisibleValidation(snap) {
|
|
1360
|
-
return (snap.texts ?? []).some((text) => typeof text === "string" && FORM_VALIDATION_TEXT_RE.test(text));
|
|
1361
|
-
}
|
|
1362
|
-
function snapshotShowsOpenCreateForm(snap, activePath = []) {
|
|
1363
|
-
if (!snap) return false;
|
|
1364
|
-
const routeNames = [
|
|
1365
|
-
...typeof snap.route === "string" && snap.route ? [snap.route] : [],
|
|
1366
|
-
...activePath
|
|
1367
|
-
];
|
|
1368
|
-
const hasFormInputs = (snap.inputs?.length ?? 0) > 0;
|
|
1369
|
-
const hasValidation = snapshotHasVisibleValidation(snap);
|
|
1370
|
-
const hasFormContext = routeHintsCreateForm(routeNames) || hasFormInputs || hasValidation;
|
|
1371
|
-
if (!hasFormContext) return false;
|
|
1372
|
-
if (snap.loading === true) return true;
|
|
1373
|
-
if (snapshotHasVisibleSubmitButton(snap)) return true;
|
|
1374
|
-
if (snapshotHasSubmitControl(snap) && hasFormInputs) return true;
|
|
1375
|
-
if (hasValidation) return true;
|
|
1376
|
-
return false;
|
|
1377
|
-
}
|
|
1378
|
-
function formFillLackedWriteEffect(action, snap, activePath = []) {
|
|
1379
|
-
if (action.type !== "form_fill") return false;
|
|
1380
|
-
if (!snapshotShowsOpenCreateForm(snap, activePath)) return false;
|
|
1381
|
-
const payload = action.payload;
|
|
1382
|
-
if (payload?.submitAfterFill === true) return true;
|
|
1383
|
-
return Array.isArray(payload?.fields) && payload.fields.length > 0;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
// src/introspection/actionTargets.ts
|
|
1387
|
-
var LIST_ITEM_TARGET_RE = /^list-\d+-item-\d+$/i;
|
|
1388
|
-
var DESTRUCTIVE_LABEL_RE = /\b(cancelar?\w*|cancel(?:ar|led|ation|amento)?|delete\w*|exclu\w*|delet\w*|remov\w*|remove|apag\w*|destroy\w*|unsubscribe\w*)\b/i;
|
|
1389
|
-
function isListItemPressTarget(value) {
|
|
1390
|
-
if (typeof value !== "string" || !value.trim()) return false;
|
|
1391
|
-
return LIST_ITEM_TARGET_RE.test(value.trim());
|
|
1392
|
-
}
|
|
1393
|
-
function looksDestructiveActionLabel(value) {
|
|
1394
|
-
if (typeof value !== "string" || !value.trim()) return false;
|
|
1395
|
-
return DESTRUCTIVE_LABEL_RE.test(value.trim());
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
// src/hooks/turnContinuation.ts
|
|
1399
|
-
function actionPressTargetId(action) {
|
|
1400
|
-
if (action.type !== "ui_interaction") return null;
|
|
1401
|
-
const payload = action.payload;
|
|
1402
|
-
const componentId = payload?.componentId;
|
|
1403
|
-
if (typeof componentId === "string" && componentId.trim()) return componentId.trim();
|
|
1404
|
-
const targetId = payload?.targetId;
|
|
1405
|
-
if (typeof targetId === "string" && targetId.trim()) return targetId.trim();
|
|
1406
|
-
return null;
|
|
1407
|
-
}
|
|
1408
|
-
function turnHasConfirmActions(actions) {
|
|
1409
|
-
return actions.some((action) => action.type === "confirm");
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// src/hooks/useAppilotsChat.ts
|
|
1413
|
-
var ESCALATION_RECONNECT_MAX_MS = 3e4;
|
|
1414
|
-
function resolveScreenMetadata() {
|
|
1415
|
-
const activePath = chunkSIBKF7AA_js.getActiveRouteNames();
|
|
1416
|
-
for (let i = activePath.length - 1; i >= 0; i--) {
|
|
1417
|
-
const name = activePath[i];
|
|
1418
|
-
const meta = name ? chunkSIBKF7AA_js.getScreenMetadata(name) : void 0;
|
|
1419
|
-
if (meta && (Array.isArray(meta.fields) && meta.fields.length > 0 || Array.isArray(meta.actions) && meta.actions.length > 0)) {
|
|
1420
|
-
return meta;
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
const screen = chunkSIBKF7AA_js.getCurrentScreen();
|
|
1424
|
-
return screen ? chunkSIBKF7AA_js.getScreenMetadata(screen) : void 0;
|
|
1425
|
-
}
|
|
1426
|
-
function buildAgentContext(extras = {}) {
|
|
1427
|
-
const context = {};
|
|
1428
|
-
const screen = chunkSIBKF7AA_js.getCurrentScreen();
|
|
1429
|
-
if (screen) context.currentScreen = screen;
|
|
1430
|
-
const navigationState = chunkSIBKF7AA_js.getNavigationStateSnapshot();
|
|
1431
|
-
if (navigationState) context.navigationState = navigationState;
|
|
1432
|
-
const screenMeta = resolveScreenMetadata();
|
|
1433
|
-
if (screenMeta) context.screenMetadata = screenMeta;
|
|
1434
|
-
try {
|
|
1435
|
-
context.snapshot = captureSnapshot();
|
|
1436
|
-
} catch (err) {
|
|
1437
|
-
chunkSIBKF7AA_js.appilotsDebugWarn("buildAgentContext: captureSnapshot failed", err);
|
|
1438
|
-
}
|
|
1439
|
-
const registry = chunkSIBKF7AA_js.componentRegistry.snapshot();
|
|
1440
|
-
const filtered = screen ? registry.filter((c) => !c.screen || c.screen === screen) : registry;
|
|
1441
|
-
if (filtered.length > 0) context.registeredComponents = filtered;
|
|
1442
|
-
const registeredLists = chunkSIBKF7AA_js.listRegistry.snapshot();
|
|
1443
|
-
if (registeredLists.length > 0) {
|
|
1444
|
-
context.registeredLists = registeredLists.map(
|
|
1445
|
-
({ scrollToIndex, scrollToOffset, getScrollMetrics, dataPreview, ...rest }) => rest
|
|
1446
|
-
);
|
|
1447
|
-
}
|
|
1448
|
-
if (extras.loadingPending === true) context.loadingPending = true;
|
|
1449
|
-
return context;
|
|
1450
|
-
}
|
|
1451
|
-
function extractPressedComponentId(actions) {
|
|
1452
|
-
for (let i = actions.length - 1; i >= 0; i--) {
|
|
1453
|
-
const a = actions[i];
|
|
1454
|
-
if (!a || a.type !== "ui_interaction") continue;
|
|
1455
|
-
const p = a.payload;
|
|
1456
|
-
const interaction = p?.action ?? "press";
|
|
1457
|
-
if (interaction !== "press" && interaction !== "longPress") continue;
|
|
1458
|
-
const id = p?.componentId;
|
|
1459
|
-
if (typeof id === "string" && id.length > 0) return id;
|
|
1460
|
-
}
|
|
1461
|
-
return null;
|
|
1462
|
-
}
|
|
1463
|
-
function extractNavigateTargetScreen(actions) {
|
|
1464
|
-
for (let i = actions.length - 1; i >= 0; i--) {
|
|
1465
|
-
const a = actions[i];
|
|
1466
|
-
if (!a || a.type !== "navigate") continue;
|
|
1467
|
-
const screenName = a.payload?.screenName;
|
|
1468
|
-
if (typeof screenName === "string" && screenName.length > 0) return screenName;
|
|
1469
|
-
}
|
|
1470
|
-
return null;
|
|
1471
|
-
}
|
|
1472
|
-
function idLooselyMatches(candidate, target) {
|
|
1473
|
-
if (!candidate || !target) return false;
|
|
1474
|
-
if (candidate === target) return true;
|
|
1475
|
-
const c = candidate.toLowerCase();
|
|
1476
|
-
const t = target.toLowerCase();
|
|
1477
|
-
if (c === t) return true;
|
|
1478
|
-
return c.endsWith(`-${t}`) || c.endsWith(`_${t}`) || t.endsWith(`-${c}`) || t.endsWith(`_${c}`);
|
|
1479
|
-
}
|
|
1480
|
-
function resolveActionHints(screen, pressedComponentId, actionPayload) {
|
|
1481
|
-
if (actionPayload && typeof actionPayload === "object") {
|
|
1482
|
-
const inline = actionPayload._appilotsInferred;
|
|
1483
|
-
if (inline && typeof inline === "object") {
|
|
1484
|
-
return inline;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
if (!screen || !pressedComponentId) return void 0;
|
|
1488
|
-
const meta = chunkSIBKF7AA_js.getScreenMetadata(screen);
|
|
1489
|
-
const actions = meta?.actions;
|
|
1490
|
-
if (!actions || actions.length === 0) return void 0;
|
|
1491
|
-
for (const a of actions) {
|
|
1492
|
-
if (typeof a === "string") continue;
|
|
1493
|
-
if (idLooselyMatches(a.id, pressedComponentId)) {
|
|
1494
|
-
const inferred = a.appilotsInferred;
|
|
1495
|
-
if (inferred && typeof inferred === "object") return inferred;
|
|
1496
|
-
return void 0;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
return void 0;
|
|
1500
|
-
}
|
|
1501
|
-
function summarizeAction(action) {
|
|
1502
|
-
const p = action.payload;
|
|
1503
|
-
switch (action.type) {
|
|
1504
|
-
case "navigate":
|
|
1505
|
-
return `navigate to ${p?.screenName ?? "?"}`;
|
|
1506
|
-
case "form_fill": {
|
|
1507
|
-
const fields = p?.fields ?? [];
|
|
1508
|
-
if (fields.length === 0) return "form_fill (no fields)";
|
|
1509
|
-
const parts = fields.slice(0, 3).map((f) => `${f.fieldId}=${JSON.stringify(f.value)}`);
|
|
1510
|
-
const extra = fields.length > 3 ? ` +${fields.length - 3} more` : "";
|
|
1511
|
-
return `form_fill ${parts.join(", ")}${extra}`;
|
|
1512
|
-
}
|
|
1513
|
-
case "ui_interaction":
|
|
1514
|
-
return `${p?.action ?? "press"} on ${p?.componentId ?? "?"}`;
|
|
1515
|
-
case "scroll_list": {
|
|
1516
|
-
const target = typeof p?.toIndex === "number" ? `to item #${p.toIndex}` : p?.direction ?? "down";
|
|
1517
|
-
return `scroll_list ${p?.listId ? `"${p.listId}" ` : ""}${target}`;
|
|
1518
|
-
}
|
|
1519
|
-
case "confirm":
|
|
1520
|
-
return `confirm: ${p?.message ?? p?.title ?? ""}`;
|
|
1521
|
-
default:
|
|
1522
|
-
return action.type;
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
var MAX_RECOVERY_ATTEMPTS_PER_ACTION = 2;
|
|
1526
|
-
var POST_ACTION_LOADING_MAX_MS = 6e3;
|
|
1527
|
-
function useAppilotsChat(options = {}) {
|
|
1528
|
-
const { client, emit, subscribe } = chunkSIBKF7AA_js.useAppilotsContext();
|
|
1529
|
-
const errorPrefix = options.errorPrefix ?? "Algo deu errado";
|
|
1530
|
-
const streamingEnabled = options.streaming !== false;
|
|
1531
|
-
const abortRef = react.useRef(null);
|
|
1532
|
-
const [messages, setMessages] = react.useState([]);
|
|
1533
|
-
const [pendingActions, setPendingActions] = react.useState([]);
|
|
1534
|
-
const [isLoading, setIsLoading] = react.useState(false);
|
|
1535
|
-
const [loadingStatusKey, setLoadingStatusKey] = react.useState("thinking");
|
|
1536
|
-
const [error, setError] = react.useState(null);
|
|
1537
|
-
const messageIdCounter = react.useRef(0);
|
|
1538
|
-
const [escalation, setEscalation] = react.useState(null);
|
|
1539
|
-
const escalationRef = react.useRef(null);
|
|
1540
|
-
escalationRef.current = escalation;
|
|
1541
|
-
const [escalationStreamEpoch, setEscalationStreamEpoch] = react.useState(0);
|
|
1542
|
-
const escalationReconnectAttemptRef = react.useRef(0);
|
|
1543
|
-
const escalationReconnectTimerRef = react.useRef(null);
|
|
1544
|
-
const escalationSinceRef = react.useRef(void 0);
|
|
1545
|
-
const seenHumanMessageIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
1546
|
-
const escalationStrings = options.escalationStrings;
|
|
1547
|
-
const currentTurnRef = react.useRef(null);
|
|
1548
|
-
const continuationInFlightRef = react.useRef(false);
|
|
1549
|
-
const recoveryCountByFingerprintRef = react.useRef(/* @__PURE__ */ new Map());
|
|
1550
|
-
const pendingActionsRef = react.useRef([]);
|
|
1551
|
-
pendingActionsRef.current = pendingActions;
|
|
1552
|
-
const messagesRef = react.useRef([]);
|
|
1553
|
-
messagesRef.current = messages;
|
|
1554
|
-
const addAssistantMessage = react.useCallback((message) => {
|
|
1555
|
-
const visibleContent = message.role === "assistant" ? sanitizeVisibleAssistantContent(message.content) : message.content;
|
|
1556
|
-
const candidate = { ...message, content: visibleContent };
|
|
1557
|
-
if (isInternalTraceMessage(candidate)) {
|
|
1558
|
-
chunkSIBKF7AA_js.recordAppilotsDebugTrace({
|
|
1559
|
-
type: "agent:trace",
|
|
1560
|
-
timestamp: Date.now(),
|
|
1561
|
-
messageId: candidate.id,
|
|
1562
|
-
summary: "Suppressed internal assistant trace from visible chat",
|
|
1563
|
-
data: { role: candidate.role, content: message.content }
|
|
1564
|
-
});
|
|
1565
|
-
const fallback = humanizeAssistantFallback(message.content);
|
|
1566
|
-
if (fallback) {
|
|
1567
|
-
setMessages((prev) => [...prev, { ...candidate, content: fallback }]);
|
|
1568
|
-
}
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
|
-
setMessages((prev) => [...prev, candidate]);
|
|
1572
|
-
}, []);
|
|
1573
|
-
const appendLocalSystemMessage = react.useCallback(
|
|
1574
|
-
(content, metadata) => {
|
|
1575
|
-
if (!content) return;
|
|
1576
|
-
setMessages((prev) => [
|
|
1577
|
-
...prev,
|
|
1578
|
-
{
|
|
1579
|
-
id: `system_${++messageIdCounter.current}`,
|
|
1580
|
-
role: "system",
|
|
1581
|
-
content,
|
|
1582
|
-
timestamp: Date.now(),
|
|
1583
|
-
...metadata ? { metadata } : {}
|
|
1584
|
-
}
|
|
1585
|
-
]);
|
|
1586
|
-
},
|
|
1587
|
-
[]
|
|
1588
|
-
);
|
|
1589
|
-
const requestHuman = react.useCallback(
|
|
1590
|
-
async (reason = "user_requested") => {
|
|
1591
|
-
if (escalationRef.current) return;
|
|
1592
|
-
try {
|
|
1593
|
-
const lastError = messagesRef.current.slice().reverse().find((m) => m.role === "assistant" && m.id.startsWith("error_"));
|
|
1594
|
-
const result = await client.requestEscalation(reason, lastError?.content);
|
|
1595
|
-
setEscalation({ id: result.escalationId, status: result.status });
|
|
1596
|
-
escalationReconnectAttemptRef.current = 0;
|
|
1597
|
-
appendLocalSystemMessage(escalationStrings?.requested ?? "");
|
|
1598
|
-
emit({
|
|
1599
|
-
type: "chat:escalation:start",
|
|
1600
|
-
timestamp: Date.now(),
|
|
1601
|
-
data: { escalationId: result.escalationId, reason }
|
|
1602
|
-
});
|
|
1603
|
-
} catch (err) {
|
|
1604
|
-
setError(err instanceof Error ? err.message : "Failed to request a human");
|
|
1605
|
-
}
|
|
1606
|
-
},
|
|
1607
|
-
[client, emit, appendLocalSystemMessage, escalationStrings]
|
|
1608
|
-
);
|
|
1609
|
-
react.useEffect(() => {
|
|
1610
|
-
if (!escalation?.id) return;
|
|
1611
|
-
const escalationId = escalation.id;
|
|
1612
|
-
const close = client.openEscalationStream(escalationId, {
|
|
1613
|
-
since: escalationSinceRef.current,
|
|
1614
|
-
onHumanMessage: (message) => {
|
|
1615
|
-
escalationReconnectAttemptRef.current = 0;
|
|
1616
|
-
if (seenHumanMessageIdsRef.current.has(message.id)) return;
|
|
1617
|
-
seenHumanMessageIdsRef.current.add(message.id);
|
|
1618
|
-
escalationSinceRef.current = message.createdAt;
|
|
1619
|
-
setMessages((prev) => [
|
|
1620
|
-
...prev,
|
|
1621
|
-
{
|
|
1622
|
-
id: message.id,
|
|
1623
|
-
role: "human_agent",
|
|
1624
|
-
content: message.content,
|
|
1625
|
-
timestamp: Date.now()
|
|
1626
|
-
}
|
|
1627
|
-
]);
|
|
1628
|
-
emit({
|
|
1629
|
-
type: "chat:escalation:message",
|
|
1630
|
-
timestamp: Date.now(),
|
|
1631
|
-
data: { escalationId, messageId: message.id }
|
|
1632
|
-
});
|
|
1633
|
-
},
|
|
1634
|
-
onStatus: (status, operatorName) => {
|
|
1635
|
-
escalationReconnectAttemptRef.current = 0;
|
|
1636
|
-
if (status === "active") {
|
|
1637
|
-
setEscalation((prev) => {
|
|
1638
|
-
if (!prev || prev.id !== escalationId) return prev;
|
|
1639
|
-
if (prev.status === "active") return prev;
|
|
1640
|
-
appendLocalSystemMessage(escalationStrings?.connected ?? "");
|
|
1641
|
-
return { ...prev, status: "active", operatorName };
|
|
1642
|
-
});
|
|
1643
|
-
} else if (status === "resolved") {
|
|
1644
|
-
appendLocalSystemMessage(escalationStrings?.resolved ?? "");
|
|
1645
|
-
setEscalation(null);
|
|
1646
|
-
emit({
|
|
1647
|
-
type: "chat:escalation:end",
|
|
1648
|
-
timestamp: Date.now(),
|
|
1649
|
-
data: { escalationId }
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
},
|
|
1653
|
-
onClose: () => {
|
|
1654
|
-
if (!escalationRef.current || escalationRef.current.id !== escalationId) return;
|
|
1655
|
-
const attempt = ++escalationReconnectAttemptRef.current;
|
|
1656
|
-
const delay = Math.min(
|
|
1657
|
-
ESCALATION_RECONNECT_MAX_MS,
|
|
1658
|
-
1e3 * 2 ** Math.min(attempt, 5)
|
|
1659
|
-
);
|
|
1660
|
-
escalationReconnectTimerRef.current = setTimeout(() => {
|
|
1661
|
-
setEscalationStreamEpoch((n) => n + 1);
|
|
1662
|
-
}, delay);
|
|
1663
|
-
}
|
|
1664
|
-
});
|
|
1665
|
-
return () => {
|
|
1666
|
-
if (escalationReconnectTimerRef.current) {
|
|
1667
|
-
clearTimeout(escalationReconnectTimerRef.current);
|
|
1668
|
-
escalationReconnectTimerRef.current = null;
|
|
1669
|
-
}
|
|
1670
|
-
close();
|
|
1671
|
-
};
|
|
1672
|
-
}, [client, emit, appendLocalSystemMessage, escalationStrings, escalation?.id, escalationStreamEpoch]);
|
|
1673
|
-
const sendEscalationMessage = react.useCallback(
|
|
1674
|
-
async (content) => {
|
|
1675
|
-
const current = escalationRef.current;
|
|
1676
|
-
if (!current) return;
|
|
1677
|
-
const userMessage = {
|
|
1678
|
-
id: `local_${++messageIdCounter.current}`,
|
|
1679
|
-
role: "user",
|
|
1680
|
-
content,
|
|
1681
|
-
timestamp: Date.now()
|
|
1682
|
-
};
|
|
1683
|
-
setMessages((prev) => [...prev, userMessage]);
|
|
1684
|
-
try {
|
|
1685
|
-
await client.sendEscalationMessage(current.id, content);
|
|
1686
|
-
} catch (err) {
|
|
1687
|
-
setError(err instanceof Error ? err.message : "Failed to send message");
|
|
1688
|
-
}
|
|
1689
|
-
},
|
|
1690
|
-
[client]
|
|
1691
|
-
);
|
|
1692
|
-
const beginTurn = react.useCallback(
|
|
1693
|
-
(assistantMessageId, actions, hop) => {
|
|
1694
|
-
if (!actions || actions.length === 0) {
|
|
1695
|
-
currentTurnRef.current = null;
|
|
1696
|
-
return;
|
|
1697
|
-
}
|
|
1698
|
-
const stamped = actions.map((a) => ({ ...a, messageId: assistantMessageId }));
|
|
1699
|
-
setPendingActions((prev) => [...prev, ...stamped]);
|
|
1700
|
-
const turn = {
|
|
1701
|
-
messageId: assistantMessageId,
|
|
1702
|
-
actionIds: new Set(stamped.map((a) => a.id)),
|
|
1703
|
-
results: /* @__PURE__ */ new Map(),
|
|
1704
|
-
reportedIds: /* @__PURE__ */ new Set(),
|
|
1705
|
-
hop,
|
|
1706
|
-
hadNavigate: stamped.some((a) => a.type === "navigate"),
|
|
1707
|
-
// Snapshot the current screen NOW, before any navigate
|
|
1708
|
-
// dispatches. waitForScreenSettle will use this as the
|
|
1709
|
-
// baseline to detect that the navigate completed.
|
|
1710
|
-
preNavigateScreen: chunkSIBKF7AA_js.getCurrentScreen(),
|
|
1711
|
-
// Full route+params signature — lets settle detect navigate
|
|
1712
|
-
// to the same route name with different params.
|
|
1713
|
-
preNavigateSignature: chunkSIBKF7AA_js.getCurrentScreenSignature()
|
|
1714
|
-
};
|
|
1715
|
-
const originScreen = chunkSIBKF7AA_js.getCurrentScreen() ?? void 0;
|
|
1716
|
-
for (const a of stamped) {
|
|
1717
|
-
turn.results.set(a.id, {
|
|
1718
|
-
type: a.type,
|
|
1719
|
-
success: false,
|
|
1720
|
-
summary: summarizeAction(a),
|
|
1721
|
-
fingerprint: fingerprintAction(a, originScreen)
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
|
-
currentTurnRef.current = turn;
|
|
1725
|
-
for (const action of stamped) {
|
|
1726
|
-
emit({
|
|
1727
|
-
type: "agent:action:start",
|
|
1728
|
-
timestamp: Date.now(),
|
|
1729
|
-
data: { action }
|
|
1730
|
-
});
|
|
1731
|
-
}
|
|
1732
|
-
if (turnHasConfirmActions(stamped)) {
|
|
1733
|
-
setIsLoading(false);
|
|
1734
|
-
setLoadingStatusKey("thinking");
|
|
1735
|
-
}
|
|
1736
|
-
},
|
|
1737
|
-
[emit]
|
|
1738
|
-
);
|
|
1739
|
-
const registerSynthesizedTurnAction = react.useCallback((action) => {
|
|
1740
|
-
const turn = currentTurnRef.current;
|
|
1741
|
-
if (!turn || turn.actionIds.has(action.id)) return;
|
|
1742
|
-
const stamped = { ...action, messageId: turn.messageId };
|
|
1743
|
-
turn.actionIds.add(stamped.id);
|
|
1744
|
-
turn.results.set(stamped.id, {
|
|
1745
|
-
type: stamped.type,
|
|
1746
|
-
success: false,
|
|
1747
|
-
summary: summarizeAction(stamped),
|
|
1748
|
-
fingerprint: fingerprintAction(stamped, chunkSIBKF7AA_js.getCurrentScreen() ?? void 0)
|
|
1749
|
-
});
|
|
1750
|
-
setPendingActions((prev) => {
|
|
1751
|
-
if (prev.some((candidate) => candidate.id === stamped.id)) return prev;
|
|
1752
|
-
return [...prev, stamped];
|
|
1753
|
-
});
|
|
1754
|
-
if (stamped.type === "confirm") {
|
|
1755
|
-
setIsLoading(false);
|
|
1756
|
-
setLoadingStatusKey("thinking");
|
|
1757
|
-
}
|
|
1758
|
-
}, []);
|
|
1759
|
-
const runContinuation = react.useCallback(
|
|
1760
|
-
async (turn) => {
|
|
1761
|
-
if (continuationInFlightRef.current) return;
|
|
1762
|
-
continuationInFlightRef.current = true;
|
|
1763
|
-
try {
|
|
1764
|
-
setIsLoading(true);
|
|
1765
|
-
setLoadingStatusKey("statusWaitingApp");
|
|
1766
|
-
const toolWithoutEffectIds = /* @__PURE__ */ new Set();
|
|
1767
|
-
let loadingPending = false;
|
|
1768
|
-
const turnActions = pendingActionsRef.current.filter(
|
|
1769
|
-
(a) => turn.actionIds.has(a.id)
|
|
1770
|
-
);
|
|
1771
|
-
const pressedId = extractPressedComponentId(turnActions);
|
|
1772
|
-
const navigateTargetScreen = extractNavigateTargetScreen(turnActions);
|
|
1773
|
-
if (turn.hadNavigate) {
|
|
1774
|
-
const settle = await chunkSIBKF7AA_js.waitForScreenSettle({
|
|
1775
|
-
expectingChange: true,
|
|
1776
|
-
fromScreen: turn.preNavigateScreen ?? null,
|
|
1777
|
-
fromSignature: turn.preNavigateSignature ?? null,
|
|
1778
|
-
targetScreen: navigateTargetScreen,
|
|
1779
|
-
maxMs: 3500
|
|
1780
|
-
});
|
|
1781
|
-
console.log(
|
|
1782
|
-
`[Appilots] runContinuation: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
1783
|
-
);
|
|
1784
|
-
const navigationMoved = settle.transitioned || chunkSIBKF7AA_js.screenDepartedBaseline(turn.preNavigateScreen ?? null, turn.preNavigateSignature ?? null) || !!navigateTargetScreen && !!settle.screen && chunkSIBKF7AA_js.routesBelongToSameFeature(settle.screen, navigateTargetScreen);
|
|
1785
|
-
if (!navigationMoved) {
|
|
1786
|
-
chunkSIBKF7AA_js.appilotsDebugWarn(
|
|
1787
|
-
`tool-without-effect: navigate emitted but screen did not change (stayed at "${turn.preNavigateScreen ?? "unknown"}", waitedMs=${settle.waitedMs}). Likely causes: target screenName missing from MCP doc, nested-nav path absent, or React Navigation rejected the route.`
|
|
1788
|
-
);
|
|
1789
|
-
for (const actionId of turn.actionIds) {
|
|
1790
|
-
const r = turn.results.get(actionId);
|
|
1791
|
-
if (r && r.type === "navigate") {
|
|
1792
|
-
toolWithoutEffectIds.add(actionId);
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
const loadSettle = await chunkSIBKF7AA_js.waitForLoadingSettle({
|
|
1797
|
-
fromScreen: settle.screen ?? null,
|
|
1798
|
-
maxMs: 3e3
|
|
1799
|
-
});
|
|
1800
|
-
console.log(
|
|
1801
|
-
`[Appilots] runContinuation: post-navigate loading settle \u2014 settled=${loadSettle.settled} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs}`
|
|
1802
|
-
);
|
|
1803
|
-
if (loadSettle.loadingPending) loadingPending = true;
|
|
1804
|
-
} else {
|
|
1805
|
-
const pressedAction = turnActions.find(
|
|
1806
|
-
(a) => a.type === "ui_interaction" && a.payload?.componentId === pressedId
|
|
1807
|
-
);
|
|
1808
|
-
const hints = resolveActionHints(
|
|
1809
|
-
chunkSIBKF7AA_js.getCurrentScreen(),
|
|
1810
|
-
pressedId,
|
|
1811
|
-
pressedAction?.payload
|
|
1812
|
-
);
|
|
1813
|
-
const settleCapMs = hints?.isAsyncTrigger === true ? 1e4 : POST_ACTION_LOADING_MAX_MS;
|
|
1814
|
-
const loadSettle = await chunkSIBKF7AA_js.waitForLoadingSettle({
|
|
1815
|
-
pressedComponentId: pressedId,
|
|
1816
|
-
fromScreen: chunkSIBKF7AA_js.getCurrentScreen(),
|
|
1817
|
-
maxMs: settleCapMs
|
|
1818
|
-
});
|
|
1819
|
-
console.log(
|
|
1820
|
-
`[Appilots] runContinuation: 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)" : ""}`
|
|
1821
|
-
);
|
|
1822
|
-
if (loadSettle.loadingPending) loadingPending = true;
|
|
1823
|
-
const listPressActions = turnActions.filter((action) => {
|
|
1824
|
-
const target = actionPressTargetId(action);
|
|
1825
|
-
return target ? isListItemPressTarget(target) : false;
|
|
1826
|
-
});
|
|
1827
|
-
if (listPressActions.length > 0) {
|
|
1828
|
-
const baselineFingerprint = chunkSIBKF7AA_js.probeLoadingState(
|
|
1829
|
-
pressedId,
|
|
1830
|
-
chunkSIBKF7AA_js.getCurrentScreen()
|
|
1831
|
-
).fingerprint;
|
|
1832
|
-
const postPressSettle = await chunkSIBKF7AA_js.waitForScreenSettle({
|
|
1833
|
-
expectingChange: true,
|
|
1834
|
-
fromScreen: turn.preNavigateScreen ?? null,
|
|
1835
|
-
fromSignature: turn.preNavigateSignature ?? null,
|
|
1836
|
-
maxMs: 2500
|
|
1837
|
-
});
|
|
1838
|
-
await chunkSIBKF7AA_js.waitForLoadingSettle({
|
|
1839
|
-
pressedComponentId: pressedId,
|
|
1840
|
-
fromScreen: chunkSIBKF7AA_js.getCurrentScreen(),
|
|
1841
|
-
maxMs: 4e3
|
|
1842
|
-
});
|
|
1843
|
-
const afterFingerprint = chunkSIBKF7AA_js.probeLoadingState(
|
|
1844
|
-
pressedId,
|
|
1845
|
-
chunkSIBKF7AA_js.getCurrentScreen()
|
|
1846
|
-
).fingerprint;
|
|
1847
|
-
console.log(
|
|
1848
|
-
`[Appilots] runContinuation: post-list-press settle \u2014 screen="${postPressSettle.screen}" transitioned=${postPressSettle.transitioned} timedOut=${postPressSettle.timedOut} waitedMs=${postPressSettle.waitedMs}`
|
|
1849
|
-
);
|
|
1850
|
-
if (!postPressSettle.transitioned && baselineFingerprint === afterFingerprint) {
|
|
1851
|
-
for (const action of listPressActions) {
|
|
1852
|
-
const result = turn.results.get(action.id);
|
|
1853
|
-
if (result?.success) {
|
|
1854
|
-
toolWithoutEffectIds.add(action.id);
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
const context = buildAgentContext({ loadingPending });
|
|
1861
|
-
const snap = context.snapshot;
|
|
1862
|
-
const activePath = context.navigationState?.activePath ?? [];
|
|
1863
|
-
for (const action of turnActions) {
|
|
1864
|
-
const result = turn.results.get(action.id);
|
|
1865
|
-
if (result?.success && formFillLackedWriteEffect(action, snap, activePath)) {
|
|
1866
|
-
toolWithoutEffectIds.add(action.id);
|
|
1867
|
-
chunkSIBKF7AA_js.appilotsDebugWarn(
|
|
1868
|
-
`tool-without-effect: form_fill succeeded but create form is still open (actionId="${action.id}")`
|
|
1869
|
-
);
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
const results = Array.from(turn.actionIds).map((actionId) => {
|
|
1873
|
-
const r = turn.results.get(actionId);
|
|
1874
|
-
if (!r) {
|
|
1875
|
-
return {
|
|
1876
|
-
actionId,
|
|
1877
|
-
type: "unknown",
|
|
1878
|
-
success: false,
|
|
1879
|
-
error: "no result reported",
|
|
1880
|
-
summary: actionId,
|
|
1881
|
-
diagnose: void 0,
|
|
1882
|
-
recoveryAttempt: 0,
|
|
1883
|
-
recoveryExhausted: false,
|
|
1884
|
-
toolWithoutEffect: false,
|
|
1885
|
-
effect: "unknown"
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
const hasToolWithoutEffect = toolWithoutEffectIds.has(actionId);
|
|
1889
|
-
let recoveryAttempt = 0;
|
|
1890
|
-
let recoveryExhausted = false;
|
|
1891
|
-
if (!r.success || hasToolWithoutEffect) {
|
|
1892
|
-
const counts = recoveryCountByFingerprintRef.current;
|
|
1893
|
-
const previous = counts.get(r.fingerprint) ?? 0;
|
|
1894
|
-
const next = previous + 1;
|
|
1895
|
-
counts.set(r.fingerprint, next);
|
|
1896
|
-
recoveryAttempt = next;
|
|
1897
|
-
recoveryExhausted = next >= MAX_RECOVERY_ATTEMPTS_PER_ACTION;
|
|
1898
|
-
}
|
|
1899
|
-
return {
|
|
1900
|
-
actionId,
|
|
1901
|
-
type: r.type,
|
|
1902
|
-
success: r.success,
|
|
1903
|
-
error: r.error,
|
|
1904
|
-
summary: r.summary,
|
|
1905
|
-
diagnose: r.diagnose,
|
|
1906
|
-
recoveryAttempt,
|
|
1907
|
-
recoveryExhausted,
|
|
1908
|
-
toolWithoutEffect: hasToolWithoutEffect,
|
|
1909
|
-
effect: hasToolWithoutEffect ? "none" : r.effect,
|
|
1910
|
-
userVisibleStatusKey: r.userVisibleStatusKey
|
|
1911
|
-
};
|
|
1912
|
-
});
|
|
1913
|
-
const exhausted = results.filter((r) => r.recoveryExhausted);
|
|
1914
|
-
setLoadingStatusKey(
|
|
1915
|
-
results.some((r) => !r.success || r.toolWithoutEffect) ? "statusAdjusting" : "statusAnalyzing"
|
|
1916
|
-
);
|
|
1917
|
-
console.log(
|
|
1918
|
-
`[Appilots] useAppilotsChat: running continuation hop=${turn.hop} (${results.filter((r) => r.success).length}/${results.length} succeeded` + (exhausted.length > 0 ? `, ${exhausted.length} recovery-exhausted` : "") + ")"
|
|
1919
|
-
);
|
|
1920
|
-
const response = await client.continueAgent(results, context, turn.hop);
|
|
1921
|
-
addAssistantMessage(response.message);
|
|
1922
|
-
emit({
|
|
1923
|
-
type: "agent:message",
|
|
1924
|
-
timestamp: Date.now(),
|
|
1925
|
-
data: { message: response.message, usage: response.usage }
|
|
1926
|
-
});
|
|
1927
|
-
if (response.isDone || !response.actions || response.actions.length === 0) {
|
|
1928
|
-
currentTurnRef.current = null;
|
|
1929
|
-
setIsLoading(false);
|
|
1930
|
-
setLoadingStatusKey("thinking");
|
|
1931
|
-
return;
|
|
1932
|
-
}
|
|
1933
|
-
beginTurn(response.message.id, response.actions, response.hop);
|
|
1934
|
-
} catch (err) {
|
|
1935
|
-
const raw = err instanceof Error ? err.message : "Continuation failed";
|
|
1936
|
-
chunkSIBKF7AA_js.appilotsDebugWarn("useAppilotsChat: continuation failed \u2014", raw);
|
|
1937
|
-
const recentUserTexts = messagesRef.current.filter((m) => m.role === "user").slice(-4).map((m) => m.content);
|
|
1938
|
-
const friendly = buildContinuationFailureMessage(raw, recentUserTexts);
|
|
1939
|
-
addAssistantMessage({
|
|
1940
|
-
id: `error_${++messageIdCounter.current}`,
|
|
1941
|
-
role: "assistant",
|
|
1942
|
-
content: friendly,
|
|
1943
|
-
timestamp: Date.now()
|
|
1944
|
-
});
|
|
1945
|
-
if (!escalationRef.current && escalationStrings?.offer) {
|
|
1946
|
-
appendLocalSystemMessage(escalationStrings.offer, { escalationOffer: true });
|
|
1947
|
-
}
|
|
1948
|
-
if (turn) {
|
|
1949
|
-
setPendingActions(
|
|
1950
|
-
(prev) => prev.map(
|
|
1951
|
-
(a) => turn.actionIds.has(a.id) && (a.status === "pending" || a.status === "executing") ? { ...a, status: "failed", error: friendly } : a
|
|
1952
|
-
)
|
|
1953
|
-
);
|
|
1954
|
-
for (const actionId of turn.actionIds) {
|
|
1955
|
-
if (turn.reportedIds.has(actionId)) continue;
|
|
1956
|
-
emit({
|
|
1957
|
-
type: "agent:action:error",
|
|
1958
|
-
timestamp: Date.now(),
|
|
1959
|
-
data: { actionId, error: friendly, abandoned: true }
|
|
1960
|
-
});
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
currentTurnRef.current = null;
|
|
1964
|
-
setIsLoading(false);
|
|
1965
|
-
setLoadingStatusKey("thinking");
|
|
1966
|
-
} finally {
|
|
1967
|
-
continuationInFlightRef.current = false;
|
|
1968
|
-
}
|
|
1969
|
-
},
|
|
1970
|
-
[client, emit, addAssistantMessage, beginTurn]
|
|
1971
|
-
);
|
|
1972
|
-
react.useEffect(() => {
|
|
1973
|
-
return subscribe((event) => {
|
|
1974
|
-
if (event.type === "agent:action:start") {
|
|
1975
|
-
const action = event.data?.action;
|
|
1976
|
-
if (!action?.id || action.type !== "confirm") return;
|
|
1977
|
-
const injectedBy = action.payload?.injectedBy;
|
|
1978
|
-
if (injectedBy !== "sdk-local") return;
|
|
1979
|
-
registerSynthesizedTurnAction(action);
|
|
1980
|
-
return;
|
|
1981
|
-
}
|
|
1982
|
-
if (event.type !== "agent:action:complete" && event.type !== "agent:action:error") return;
|
|
1983
|
-
const turn = currentTurnRef.current;
|
|
1984
|
-
if (!turn) return;
|
|
1985
|
-
const actionId = event.data?.actionId;
|
|
1986
|
-
if (!actionId || !turn.actionIds.has(actionId)) return;
|
|
1987
|
-
if (turn.reportedIds.has(actionId)) return;
|
|
1988
|
-
const success = event.type === "agent:action:complete";
|
|
1989
|
-
const errorMsg = event.data?.error || void 0;
|
|
1990
|
-
const diagnose = event.data?.diagnose;
|
|
1991
|
-
const effect = event.data?.effect;
|
|
1992
|
-
const userVisibleStatusKey = event.data?.userVisibleStatusKey;
|
|
1993
|
-
const existing = turn.results.get(actionId);
|
|
1994
|
-
if (existing) {
|
|
1995
|
-
existing.success = success;
|
|
1996
|
-
existing.error = errorMsg;
|
|
1997
|
-
existing.diagnose = diagnose;
|
|
1998
|
-
existing.effect = effect;
|
|
1999
|
-
existing.userVisibleStatusKey = userVisibleStatusKey;
|
|
2000
|
-
}
|
|
2001
|
-
turn.reportedIds.add(actionId);
|
|
2002
|
-
if (turn.reportedIds.size >= turn.actionIds.size) {
|
|
2003
|
-
void runContinuation(turn);
|
|
2004
|
-
}
|
|
2005
|
-
});
|
|
2006
|
-
}, [subscribe, runContinuation, registerSynthesizedTurnAction]);
|
|
2007
|
-
const sendMessage = react.useCallback(
|
|
2008
|
-
async (content) => {
|
|
2009
|
-
if (!content.trim() || isLoading) return;
|
|
2010
|
-
if (escalationRef.current) {
|
|
2011
|
-
await sendEscalationMessage(content.trim());
|
|
2012
|
-
return;
|
|
2013
|
-
}
|
|
2014
|
-
setError(null);
|
|
2015
|
-
setIsLoading(true);
|
|
2016
|
-
setLoadingStatusKey("statusAnalyzing");
|
|
2017
|
-
currentTurnRef.current = null;
|
|
2018
|
-
recoveryCountByFingerprintRef.current = /* @__PURE__ */ new Map();
|
|
2019
|
-
const userMessage = {
|
|
2020
|
-
id: `local_${++messageIdCounter.current}`,
|
|
2021
|
-
role: "user",
|
|
2022
|
-
content: content.trim(),
|
|
2023
|
-
timestamp: Date.now()
|
|
2024
|
-
};
|
|
2025
|
-
setMessages((prev) => [...prev, userMessage]);
|
|
2026
|
-
const streamingId = `streaming_${++messageIdCounter.current}`;
|
|
2027
|
-
let streamedVisible = false;
|
|
2028
|
-
const removeStreamingPlaceholder = () => {
|
|
2029
|
-
if (!streamedVisible) return;
|
|
2030
|
-
streamedVisible = false;
|
|
2031
|
-
setMessages((prev) => prev.filter((m) => m.id !== streamingId));
|
|
2032
|
-
};
|
|
2033
|
-
const abortController = new AbortController();
|
|
2034
|
-
abortRef.current = abortController;
|
|
2035
|
-
try {
|
|
2036
|
-
const context = buildAgentContext();
|
|
2037
|
-
console.log(
|
|
2038
|
-
`[Appilots] useAppilotsChat 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}}`
|
|
2039
|
-
);
|
|
2040
|
-
let response;
|
|
2041
|
-
if (streamingEnabled) {
|
|
2042
|
-
try {
|
|
2043
|
-
response = await client.sendMessageStream(content.trim(), context, {
|
|
2044
|
-
signal: abortController.signal,
|
|
2045
|
-
onTextDelta: (_chunk, fullText) => {
|
|
2046
|
-
const visible = sanitizeVisibleAssistantContent(fullText);
|
|
2047
|
-
if (!streamedVisible) {
|
|
2048
|
-
streamedVisible = true;
|
|
2049
|
-
setMessages((prev) => [
|
|
2050
|
-
...prev,
|
|
2051
|
-
{ id: streamingId, role: "assistant", content: visible, timestamp: Date.now() }
|
|
2052
|
-
]);
|
|
2053
|
-
} else {
|
|
2054
|
-
setMessages(
|
|
2055
|
-
(prev) => prev.map((m) => m.id === streamingId ? { ...m, content: visible } : m)
|
|
2056
|
-
);
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
});
|
|
2060
|
-
} catch (err) {
|
|
2061
|
-
if (err instanceof chunkSIBKF7AA_js.StreamTransportError) {
|
|
2062
|
-
console.warn(`[Appilots] streaming unavailable, falling back to JSON: ${err.message}`);
|
|
2063
|
-
response = await client.sendMessage(content.trim(), context);
|
|
2064
|
-
} else {
|
|
2065
|
-
throw err;
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
} else {
|
|
2069
|
-
response = await client.sendMessage(content.trim(), context);
|
|
2070
|
-
}
|
|
2071
|
-
console.log(
|
|
2072
|
-
`[Appilots] useAppilotsChat sendMessage: response received \u2014 actions=${response.actions?.length ?? 0}, hasSummary=${!!response.summaryMessage}`
|
|
2073
|
-
);
|
|
2074
|
-
removeStreamingPlaceholder();
|
|
2075
|
-
addAssistantMessage(response.message);
|
|
2076
|
-
beginTurn(response.message.id, response.actions, 1);
|
|
2077
|
-
if (response.summaryMessage) {
|
|
2078
|
-
addAssistantMessage(response.summaryMessage);
|
|
2079
|
-
}
|
|
2080
|
-
emit({
|
|
2081
|
-
type: "agent:message",
|
|
2082
|
-
timestamp: Date.now(),
|
|
2083
|
-
data: { message: response.message, usage: response.usage }
|
|
2084
|
-
});
|
|
2085
|
-
if (!response.actions || response.actions.length === 0) {
|
|
2086
|
-
setIsLoading(false);
|
|
2087
|
-
setLoadingStatusKey("thinking");
|
|
2088
|
-
}
|
|
2089
|
-
} catch (err) {
|
|
2090
|
-
if (err instanceof chunkSIBKF7AA_js.StreamAbortError || err instanceof Error && err.name === "AbortError") {
|
|
2091
|
-
setIsLoading(false);
|
|
2092
|
-
setLoadingStatusKey("thinking");
|
|
2093
|
-
return;
|
|
2094
|
-
}
|
|
2095
|
-
removeStreamingPlaceholder();
|
|
2096
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to send message";
|
|
2097
|
-
setError(errorMessage);
|
|
2098
|
-
const errorMsg = {
|
|
2099
|
-
id: `error_${++messageIdCounter.current}`,
|
|
2100
|
-
role: "assistant",
|
|
2101
|
-
content: `${errorPrefix}: ${errorMessage}`,
|
|
2102
|
-
timestamp: Date.now()
|
|
2103
|
-
};
|
|
2104
|
-
addAssistantMessage(errorMsg);
|
|
2105
|
-
setIsLoading(false);
|
|
2106
|
-
setLoadingStatusKey("thinking");
|
|
2107
|
-
} finally {
|
|
2108
|
-
if (abortRef.current === abortController) {
|
|
2109
|
-
abortRef.current = null;
|
|
2110
|
-
}
|
|
999
|
+
const props = fiber.memoizedProps ?? {};
|
|
1000
|
+
bestId2 = props.testID ?? props.accessibilityLabel ?? props.placeholder ?? (kind === "target" ? findChildText2(fiber) : void 0);
|
|
1001
|
+
if (kind === "field") bestHandle = wrapTextInput(fiber);
|
|
1002
|
+
else if (kind === "toggle") bestHandle = wrapSwitch(fiber);
|
|
1003
|
+
else bestHandle = wrapTouchable(fiber, findChildText2(fiber));
|
|
2111
1004
|
}
|
|
2112
|
-
},
|
|
2113
|
-
[client, emit, isLoading, addAssistantMessage, beginTurn, errorPrefix, streamingEnabled]
|
|
2114
|
-
);
|
|
2115
|
-
const cancelMessage = react.useCallback(() => {
|
|
2116
|
-
abortRef.current?.abort();
|
|
2117
|
-
}, []);
|
|
2118
|
-
const clearMessages = react.useCallback(() => {
|
|
2119
|
-
setMessages([]);
|
|
2120
|
-
setPendingActions([]);
|
|
2121
|
-
setError(null);
|
|
2122
|
-
setIsLoading(false);
|
|
2123
|
-
setLoadingStatusKey("thinking");
|
|
2124
|
-
currentTurnRef.current = null;
|
|
2125
|
-
continuationInFlightRef.current = false;
|
|
2126
|
-
recoveryCountByFingerprintRef.current = /* @__PURE__ */ new Map();
|
|
2127
|
-
if (escalationRef.current) {
|
|
2128
|
-
void client.abandonEscalation(escalationRef.current.id);
|
|
2129
1005
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
loadingStatusKey,
|
|
2141
|
-
error,
|
|
2142
|
-
pendingActions,
|
|
2143
|
-
escalation,
|
|
2144
|
-
requestHuman,
|
|
2145
|
-
sendMessage,
|
|
2146
|
-
cancelMessage,
|
|
2147
|
-
clearMessages,
|
|
2148
|
-
clearError
|
|
2149
|
-
};
|
|
1006
|
+
if (fiber.child) stack.push(fiber.child);
|
|
1007
|
+
}
|
|
1008
|
+
if (bestHandle) {
|
|
1009
|
+
console.log(
|
|
1010
|
+
`[Appilots] findInteractiveByIdentifier: query="${query}" \u2192 kind=${bestHandle.kind} label="${bestHandle.label ?? bestId2}" score=${bestScore}`
|
|
1011
|
+
);
|
|
1012
|
+
} else {
|
|
1013
|
+
console.log(`[Appilots] findInteractiveByIdentifier: query="${query}" \u2192 no match`);
|
|
1014
|
+
}
|
|
1015
|
+
return bestHandle;
|
|
2150
1016
|
}
|
|
2151
1017
|
|
|
2152
1018
|
// src/executor/handlers/navigateHandler.ts
|
|
@@ -2332,7 +1198,7 @@ function markedItemIndexFromFiber(fiber) {
|
|
|
2332
1198
|
const value = fiber?.memoizedProps?.__appilotsItemIndex;
|
|
2333
1199
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2334
1200
|
}
|
|
2335
|
-
function
|
|
1201
|
+
function normalize2(value) {
|
|
2336
1202
|
return String(value ?? "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "");
|
|
2337
1203
|
}
|
|
2338
1204
|
function hasPressHandler3(fiber) {
|
|
@@ -2344,7 +1210,7 @@ function isPressableFiber(fiber) {
|
|
|
2344
1210
|
return TOUCHABLE_NAMES.has(typeName) || hasPressHandler3(fiber);
|
|
2345
1211
|
}
|
|
2346
1212
|
function findListContainers(options = {}) {
|
|
2347
|
-
const root =
|
|
1213
|
+
const root = chunkV3TUIIT2_js.getFiberRoot();
|
|
2348
1214
|
if (!root) return [];
|
|
2349
1215
|
const containers = [];
|
|
2350
1216
|
const visited = /* @__PURE__ */ new WeakSet();
|
|
@@ -2503,7 +1369,7 @@ function describeTouchable(fiber) {
|
|
|
2503
1369
|
}
|
|
2504
1370
|
function touchableLooksDestructive(fiber) {
|
|
2505
1371
|
const descriptor = describeTouchable(fiber);
|
|
2506
|
-
if (descriptor && looksDestructiveActionLabel(descriptor)) return true;
|
|
1372
|
+
if (descriptor && chunkV3TUIIT2_js.looksDestructiveActionLabel(descriptor)) return true;
|
|
2507
1373
|
const props = fiber?.memoizedProps ?? {};
|
|
2508
1374
|
const haystack = [
|
|
2509
1375
|
props.testID,
|
|
@@ -2511,7 +1377,7 @@ function touchableLooksDestructive(fiber) {
|
|
|
2511
1377
|
props.accessibilityHint,
|
|
2512
1378
|
props.label
|
|
2513
1379
|
].filter((value) => typeof value === "string").join(" ");
|
|
2514
|
-
return looksDestructiveActionLabel(haystack);
|
|
1380
|
+
return chunkV3TUIIT2_js.looksDestructiveActionLabel(haystack);
|
|
2515
1381
|
}
|
|
2516
1382
|
function describeItemCandidate(listIndex, zeroBasedIndex, item) {
|
|
2517
1383
|
const syntheticId = `list-${listIndex}-item-${zeroBasedIndex + 1}`;
|
|
@@ -2591,9 +1457,9 @@ function pressResolvedListItem(list, itemFiber, listIndex, itemIndex) {
|
|
|
2591
1457
|
}
|
|
2592
1458
|
}
|
|
2593
1459
|
function itemTextScore(item, locator) {
|
|
2594
|
-
const needles = [locator.label, ...locator.texts ?? []].map(
|
|
1460
|
+
const needles = [locator.label, ...locator.texts ?? []].map(normalize2).filter((text) => text.length >= 2);
|
|
2595
1461
|
if (needles.length === 0) return 0;
|
|
2596
|
-
const haystack = collectTexts(item).map(
|
|
1462
|
+
const haystack = collectTexts(item).map(normalize2).filter(Boolean);
|
|
2597
1463
|
if (haystack.length === 0) return 0;
|
|
2598
1464
|
let score = 0;
|
|
2599
1465
|
for (const needle of needles) {
|
|
@@ -2611,11 +1477,11 @@ function scoreItemCandidate(item, zeroBasedIndex, locator) {
|
|
|
2611
1477
|
const markedIndex = markedItemIndexFromFiber(item);
|
|
2612
1478
|
if (locator.itemKey) {
|
|
2613
1479
|
if (itemKey === locator.itemKey) score += 1e3;
|
|
2614
|
-
else if (itemKey &&
|
|
1480
|
+
else if (itemKey && normalize2(itemKey) === normalize2(locator.itemKey)) score += 950;
|
|
2615
1481
|
}
|
|
2616
1482
|
if (locator.reactKey) {
|
|
2617
1483
|
if (reactKey === locator.reactKey) score += 900;
|
|
2618
|
-
else if (reactKey &&
|
|
1484
|
+
else if (reactKey && normalize2(reactKey) === normalize2(locator.reactKey)) score += 850;
|
|
2619
1485
|
}
|
|
2620
1486
|
if (locator.itemIndex && zeroBasedIndex === locator.itemIndex - 1) score += 750;
|
|
2621
1487
|
if (locator.itemIndex && markedIndex !== void 0 && (markedIndex === locator.itemIndex || markedIndex === locator.itemIndex - 1)) {
|
|
@@ -2626,8 +1492,8 @@ function scoreItemCandidate(item, zeroBasedIndex, locator) {
|
|
|
2626
1492
|
}
|
|
2627
1493
|
function candidateLists(containers, locator) {
|
|
2628
1494
|
if (locator.listId) {
|
|
2629
|
-
const normalizedId =
|
|
2630
|
-
const matches = containers.map((list, listIndex) => ({ list, listIndex })).filter(({ list }) =>
|
|
1495
|
+
const normalizedId = normalize2(locator.listId);
|
|
1496
|
+
const matches = containers.map((list, listIndex) => ({ list, listIndex })).filter(({ list }) => normalize2(list.listId) === normalizedId);
|
|
2631
1497
|
if (matches.length > 0) return matches;
|
|
2632
1498
|
}
|
|
2633
1499
|
if (typeof locator.listIndex === "number") {
|
|
@@ -2635,14 +1501,14 @@ function candidateLists(containers, locator) {
|
|
|
2635
1501
|
return list ? [{ list, listIndex: locator.listIndex }] : [];
|
|
2636
1502
|
}
|
|
2637
1503
|
if (locator.listLabel) {
|
|
2638
|
-
const normalizedLabel =
|
|
2639
|
-
const matches = containers.map((list, listIndex) => ({ list, listIndex })).filter(({ list }) =>
|
|
1504
|
+
const normalizedLabel = normalize2(locator.listLabel);
|
|
1505
|
+
const matches = containers.map((list, listIndex) => ({ list, listIndex })).filter(({ list }) => normalize2(list.listLabel).includes(normalizedLabel));
|
|
2640
1506
|
if (matches.length > 0) return matches;
|
|
2641
1507
|
}
|
|
2642
1508
|
return containers.map((list, listIndex) => ({ list, listIndex }));
|
|
2643
1509
|
}
|
|
2644
1510
|
function pressListItemAtOrdinal(listIndex, itemIndex) {
|
|
2645
|
-
if (!
|
|
1511
|
+
if (!chunkV3TUIIT2_js.getFiberRoot()) return { ok: false, reason: "no-fiber-root" };
|
|
2646
1512
|
const containers = findListContainers({ allowSingleHeuristic: true });
|
|
2647
1513
|
if (listIndex < 0 || listIndex >= containers.length) {
|
|
2648
1514
|
return { ok: false, reason: "list-not-found" };
|
|
@@ -2656,7 +1522,7 @@ function pressListItemAtOrdinal(listIndex, itemIndex) {
|
|
|
2656
1522
|
return pressResolvedListItem(list, items[zeroBased], listIndex, itemIndex);
|
|
2657
1523
|
}
|
|
2658
1524
|
function pressListItemByIdentity(locator) {
|
|
2659
|
-
if (!
|
|
1525
|
+
if (!chunkV3TUIIT2_js.getFiberRoot()) return { ok: false, reason: "no-fiber-root" };
|
|
2660
1526
|
const parsed = locator.syntheticId ? parseSyntheticListItemId(locator.syntheticId) : null;
|
|
2661
1527
|
const effective = parsed ? { ...locator, listIndex: parsed.listIndex, itemIndex: parsed.itemIndex } : locator;
|
|
2662
1528
|
const containers = findListContainers({ allowSingleHeuristic: true });
|
|
@@ -2696,7 +1562,7 @@ function screenDiagnose(targetId, category = "component-not-found") {
|
|
|
2696
1562
|
return {
|
|
2697
1563
|
category,
|
|
2698
1564
|
targetId,
|
|
2699
|
-
screen:
|
|
1565
|
+
screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0
|
|
2700
1566
|
};
|
|
2701
1567
|
}
|
|
2702
1568
|
function isPressLike(action) {
|
|
@@ -2713,10 +1579,10 @@ function elementLookupKey(value) {
|
|
|
2713
1579
|
return canonicalElementId(value).toLowerCase().replace(/[\s_]+/g, "-");
|
|
2714
1580
|
}
|
|
2715
1581
|
function findRegisteredElement(elementId) {
|
|
2716
|
-
const direct =
|
|
1582
|
+
const direct = chunkV3TUIIT2_js.elementRegistry.get(elementId) ?? chunkV3TUIIT2_js.elementRegistry.get(canonicalElementId(elementId));
|
|
2717
1583
|
if (direct) return direct;
|
|
2718
1584
|
const wanted = elementLookupKey(elementId);
|
|
2719
|
-
return
|
|
1585
|
+
return chunkV3TUIIT2_js.elementRegistry.snapshot().find((element) => elementLookupKey(element.id) === wanted);
|
|
2720
1586
|
}
|
|
2721
1587
|
function resolveInteractionElement(elementId) {
|
|
2722
1588
|
return getLatestElement(elementId);
|
|
@@ -2936,7 +1802,7 @@ function dispatchElement(element, registry, action) {
|
|
|
2936
1802
|
}
|
|
2937
1803
|
function dispatchStructuredElementId(identifier, action) {
|
|
2938
1804
|
if (!isElementIdentifier(identifier) || !isPressLike(action)) return null;
|
|
2939
|
-
const parsed = parseStableElementId(identifier);
|
|
1805
|
+
const parsed = chunkV3TUIIT2_js.parseStableElementId(identifier);
|
|
2940
1806
|
if (!parsed || parsed.role !== "option") return null;
|
|
2941
1807
|
if (!parsed.itemKey && !parsed.listId) return null;
|
|
2942
1808
|
const pressed = pressListItemByIdentity({
|
|
@@ -2972,24 +1838,24 @@ function pressElementOrTarget(identifier, registry) {
|
|
|
2972
1838
|
// src/executor/formFieldRewrite.ts
|
|
2973
1839
|
var LIST_ORDINAL_RE = /^list-\d+-item-\d+$/i;
|
|
2974
1840
|
var PLATE_RE = /^[A-Z]{3}-?\d[A-Z0-9]\d{2}$/i;
|
|
2975
|
-
function
|
|
1841
|
+
function normalize4(value) {
|
|
2976
1842
|
return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
2977
1843
|
}
|
|
2978
1844
|
function resolveScreenForFormFields() {
|
|
2979
|
-
const activePath =
|
|
1845
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
2980
1846
|
for (let i = activePath.length - 1; i >= 0; i--) {
|
|
2981
1847
|
const name = activePath[i];
|
|
2982
|
-
const meta = name ?
|
|
1848
|
+
const meta = name ? chunkV3TUIIT2_js.getScreenMetadata(name) : void 0;
|
|
2983
1849
|
if (name && Array.isArray(meta?.fields) && meta.fields.length > 0) return name;
|
|
2984
1850
|
}
|
|
2985
|
-
return
|
|
1851
|
+
return chunkV3TUIIT2_js.getCurrentScreen();
|
|
2986
1852
|
}
|
|
2987
1853
|
function collectKnownFormFields(registry) {
|
|
2988
1854
|
const registryFields = registry.snapshot().filter((entry) => entry.kind === "field" || entry.kind === "slider").map((entry) => ({ id: entry.id, label: entry.label }));
|
|
2989
1855
|
if (registryFields.length >= 2) return registryFields;
|
|
2990
1856
|
const screen = resolveScreenForFormFields();
|
|
2991
1857
|
if (screen) {
|
|
2992
|
-
const metaFields =
|
|
1858
|
+
const metaFields = chunkV3TUIIT2_js.getScreenMetadata(screen)?.fields;
|
|
2993
1859
|
if (Array.isArray(metaFields) && metaFields.length > 0) {
|
|
2994
1860
|
return metaFields.filter((field) => !!field && typeof field.id === "string").map((field) => ({
|
|
2995
1861
|
id: field.id,
|
|
@@ -2999,7 +1865,7 @@ function collectKnownFormFields(registry) {
|
|
|
2999
1865
|
}));
|
|
3000
1866
|
}
|
|
3001
1867
|
}
|
|
3002
|
-
for (const meta of
|
|
1868
|
+
for (const meta of chunkV3TUIIT2_js.getAllScreens()) {
|
|
3003
1869
|
if (Array.isArray(meta.fields) && meta.fields.length > 0) {
|
|
3004
1870
|
return meta.fields.filter((field) => !!field && typeof field.id === "string").map((field) => ({
|
|
3005
1871
|
id: field.id,
|
|
@@ -3012,14 +1878,14 @@ function collectKnownFormFields(registry) {
|
|
|
3012
1878
|
return registryFields;
|
|
3013
1879
|
}
|
|
3014
1880
|
function fieldHaystack(field) {
|
|
3015
|
-
return
|
|
1881
|
+
return normalize4(`${field.id} ${field.label ?? ""}`);
|
|
3016
1882
|
}
|
|
3017
1883
|
function isMisplacedFieldId(fieldId, knownIds) {
|
|
3018
1884
|
const trimmed = fieldId.trim();
|
|
3019
1885
|
if (!trimmed) return true;
|
|
3020
1886
|
if (LIST_ORDINAL_RE.test(trimmed)) return true;
|
|
3021
1887
|
if (/^\d+$/.test(trimmed)) return true;
|
|
3022
|
-
const normalized =
|
|
1888
|
+
const normalized = normalize4(trimmed);
|
|
3023
1889
|
if (knownIds.has(normalized)) return false;
|
|
3024
1890
|
for (const known of knownIds) {
|
|
3025
1891
|
if (known.length >= 4 && (known.includes(normalized) || normalized.includes(known))) {
|
|
@@ -3029,18 +1895,18 @@ function isMisplacedFieldId(fieldId, knownIds) {
|
|
|
3029
1895
|
return true;
|
|
3030
1896
|
}
|
|
3031
1897
|
function valueMatchesOption(field, rawValue) {
|
|
3032
|
-
const value =
|
|
1898
|
+
const value = normalize4(String(rawValue ?? ""));
|
|
3033
1899
|
if (!value || !Array.isArray(field.options)) return false;
|
|
3034
1900
|
return field.options.some((option) => {
|
|
3035
|
-
const label =
|
|
3036
|
-
const optionValue =
|
|
1901
|
+
const label = normalize4(option.label ?? "");
|
|
1902
|
+
const optionValue = normalize4(option.value ?? "");
|
|
3037
1903
|
return label === value || optionValue === value || value.includes(label) || value.includes(optionValue);
|
|
3038
1904
|
});
|
|
3039
1905
|
}
|
|
3040
1906
|
function inferFieldIdForValue(rawValue, fields, used) {
|
|
3041
1907
|
const value = String(rawValue ?? "").trim();
|
|
3042
1908
|
if (!value) return void 0;
|
|
3043
|
-
const normalizedValue =
|
|
1909
|
+
const normalizedValue = normalize4(value);
|
|
3044
1910
|
for (const field of fields) {
|
|
3045
1911
|
if (used.has(field.id)) continue;
|
|
3046
1912
|
if (valueMatchesOption(field, value)) return field.id;
|
|
@@ -3077,7 +1943,7 @@ function inferFieldIdForValue(rawValue, fields, used) {
|
|
|
3077
1943
|
}
|
|
3078
1944
|
const nextText = fields.find((field) => {
|
|
3079
1945
|
if (used.has(field.id)) return false;
|
|
3080
|
-
const type =
|
|
1946
|
+
const type = normalize4(field.type ?? "text");
|
|
3081
1947
|
return type === "text" || type === "input" || type === "number" || type === "";
|
|
3082
1948
|
});
|
|
3083
1949
|
return nextText?.id;
|
|
@@ -3085,7 +1951,7 @@ function inferFieldIdForValue(rawValue, fields, used) {
|
|
|
3085
1951
|
function rewriteMisplacedFormFillFields(fields, registry) {
|
|
3086
1952
|
const knownFields = collectKnownFormFields(registry);
|
|
3087
1953
|
if (knownFields.length === 0) return fields;
|
|
3088
|
-
const knownIds = new Set(knownFields.map((field) =>
|
|
1954
|
+
const knownIds = new Set(knownFields.map((field) => normalize4(field.id)));
|
|
3089
1955
|
const needsRewrite = fields.some((field) => isMisplacedFieldId(field.fieldId, knownIds));
|
|
3090
1956
|
if (!needsRewrite) return fields;
|
|
3091
1957
|
const used = /* @__PURE__ */ new Set();
|
|
@@ -3097,7 +1963,7 @@ function rewriteMisplacedFormFillFields(fields, registry) {
|
|
|
3097
1963
|
const inferred = inferFieldIdForValue(String(field.value ?? ""), knownFields, used);
|
|
3098
1964
|
if (!inferred) return field;
|
|
3099
1965
|
used.add(inferred);
|
|
3100
|
-
|
|
1966
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
3101
1967
|
`formFieldRewrite: ${field.fieldId} \u2192 ${inferred} (value=${JSON.stringify(field.value)})`
|
|
3102
1968
|
);
|
|
3103
1969
|
return { ...field, fieldId: inferred };
|
|
@@ -3105,7 +1971,7 @@ function rewriteMisplacedFormFillFields(fields, registry) {
|
|
|
3105
1971
|
}
|
|
3106
1972
|
|
|
3107
1973
|
// src/executor/formValidation.ts
|
|
3108
|
-
var
|
|
1974
|
+
var FORM_VALIDATION_TEXT_RE = /(inv[aá]lid|obrigat[oó]ri|required|invalid|j[aá]\s+existe|already\s+exists|duplicad|formato|format|too\s+short|muito\s+curto|n[aã]o\s+(?:foi|pode)|must\s+be|deve\s+ser|selecione|escolha|select\s+(?:the|a|o|an))/i;
|
|
3109
1975
|
var FORM_SUCCESS_TEXT_RE = /\b(cadastrad[oa]s?\s+com\s+sucesso|registrad[oa]s?\s+com\s+sucesso|salv[oa]\s+com\s+sucesso|foi\s+cadastrad[oa]|foi\s+registrad[oa]|foi\s+salv[oa]|successfully\s+(?:registered|saved|created)|(?:has\s+been|was)\s+(?:registered|saved|created))\b/i;
|
|
3110
1976
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3111
1977
|
var FIELD_HINTS = [
|
|
@@ -3125,7 +1991,7 @@ function inferValidationFieldId(message) {
|
|
|
3125
1991
|
function readVisibleFormValidation(snap) {
|
|
3126
1992
|
if (!snap) return void 0;
|
|
3127
1993
|
for (const text of snap.texts ?? []) {
|
|
3128
|
-
if (!text?.trim() || !
|
|
1994
|
+
if (!text?.trim() || !FORM_VALIDATION_TEXT_RE.test(text)) continue;
|
|
3129
1995
|
return { fieldId: inferValidationFieldId(text), message: text };
|
|
3130
1996
|
}
|
|
3131
1997
|
return void 0;
|
|
@@ -3135,7 +2001,7 @@ function readVisibleFormSuccess(snap) {
|
|
|
3135
2001
|
return (snap.texts ?? []).find((text) => text?.trim() && FORM_SUCCESS_TEXT_RE.test(text));
|
|
3136
2002
|
}
|
|
3137
2003
|
function submitAsyncStillPending(submitTargetId) {
|
|
3138
|
-
const probe =
|
|
2004
|
+
const probe = chunkV3TUIIT2_js.probeLoadingState(submitTargetId ?? null, chunkV3TUIIT2_js.getCurrentScreen());
|
|
3139
2005
|
if (probe.loading) return true;
|
|
3140
2006
|
if (submitTargetId && probe.pressedFound && probe.pressedDisabled) return true;
|
|
3141
2007
|
return false;
|
|
@@ -3147,7 +2013,7 @@ function evaluatePostSubmitSnapshot(snap, activePath) {
|
|
|
3147
2013
|
}
|
|
3148
2014
|
const successText = readVisibleFormSuccess(snap);
|
|
3149
2015
|
if (successText) return { status: "success", message: successText };
|
|
3150
|
-
if (!snapshotShowsOpenCreateForm(snap, activePath)) {
|
|
2016
|
+
if (!chunkV3TUIIT2_js.snapshotShowsOpenCreateForm(snap, activePath)) {
|
|
3151
2017
|
return { status: "success" };
|
|
3152
2018
|
}
|
|
3153
2019
|
return void 0;
|
|
@@ -3163,7 +2029,7 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
3163
2029
|
continue;
|
|
3164
2030
|
}
|
|
3165
2031
|
const snap2 = captureSnapshot();
|
|
3166
|
-
const activePath2 =
|
|
2032
|
+
const activePath2 = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
3167
2033
|
const outcome2 = evaluatePostSubmitSnapshot(snap2, activePath2);
|
|
3168
2034
|
if (outcome2) return outcome2;
|
|
3169
2035
|
await sleep(pollMs);
|
|
@@ -3175,7 +2041,7 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
3175
2041
|
};
|
|
3176
2042
|
}
|
|
3177
2043
|
const snap = captureSnapshot();
|
|
3178
|
-
const activePath =
|
|
2044
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
3179
2045
|
const outcome = evaluatePostSubmitSnapshot(snap, activePath);
|
|
3180
2046
|
if (outcome) return outcome;
|
|
3181
2047
|
return {
|
|
@@ -3184,27 +2050,6 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
3184
2050
|
};
|
|
3185
2051
|
}
|
|
3186
2052
|
|
|
3187
|
-
// src/executor/handlers/formFillMatching.ts
|
|
3188
|
-
function normalize6(s) {
|
|
3189
|
-
return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
3190
|
-
}
|
|
3191
|
-
function matchScore(needle, hay) {
|
|
3192
|
-
if (!needle || !hay) return 0;
|
|
3193
|
-
if (needle === hay) return 100;
|
|
3194
|
-
if (hay.startsWith(needle) || needle.startsWith(hay)) return 75;
|
|
3195
|
-
if (hay.endsWith(needle) || needle.endsWith(hay)) return 60;
|
|
3196
|
-
if (hay.includes(needle) || needle.includes(hay)) return 40;
|
|
3197
|
-
return 0;
|
|
3198
|
-
}
|
|
3199
|
-
function isGenericSelectValue(value) {
|
|
3200
|
-
if (value === void 0 || value === null || value === "") return true;
|
|
3201
|
-
const normalized = normalize6(String(value));
|
|
3202
|
-
if (!normalized) return true;
|
|
3203
|
-
return /^(any|qualquer|random|aleatorio|aleatoria|auto|automatico|automatica|mock|mockado|mockada|mockados|mockadas|dummy|sample|exemplo|default|padrao|teste|test)$/.test(
|
|
3204
|
-
normalized
|
|
3205
|
-
);
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
2053
|
// ../shared/dist/chunk-ERITSWKE.mjs
|
|
3209
2054
|
var DEFAULT_FUZZY_FLOOR = 4;
|
|
3210
2055
|
function normalizeId(value) {
|
|
@@ -3290,39 +2135,6 @@ function resolveTarget(requested, candidates, opts = {}) {
|
|
|
3290
2135
|
return { status: "matched", id: ids[0], tier: "fuzzy" };
|
|
3291
2136
|
}
|
|
3292
2137
|
|
|
3293
|
-
// src/executor/interactionGuidance.ts
|
|
3294
|
-
var LIST_ORDINAL_RE2 = /^list-\d+-item-\d+$/i;
|
|
3295
|
-
function normalizeId2(value) {
|
|
3296
|
-
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
3297
|
-
}
|
|
3298
|
-
function isOptionalCosmeticTargetId(targetId) {
|
|
3299
|
-
if (typeof targetId !== "string" || !targetId.trim()) return false;
|
|
3300
|
-
const id = normalizeId2(targetId);
|
|
3301
|
-
return /foto|photo|avatar|icone|icon|emoji|sheetaction|sheet-action|btn-foto/.test(id) || id.includes("photo-sheet");
|
|
3302
|
-
}
|
|
3303
|
-
function routeLooksLikeFormContext(route) {
|
|
3304
|
-
const haystack = normalizeId2(route ?? "");
|
|
3305
|
-
return /create|edit|form|cadastr|register|novo|vehiclecreate|subscriptioncreate|vehicleedit|subscriptioncreate/.test(
|
|
3306
|
-
haystack
|
|
3307
|
-
);
|
|
3308
|
-
}
|
|
3309
|
-
function screenPathParts(screen) {
|
|
3310
|
-
if (!screen) return [];
|
|
3311
|
-
return screen.split("/").map((part) => part.trim()).filter(Boolean);
|
|
3312
|
-
}
|
|
3313
|
-
function isMisappliedListOrdinalOnForm(targetId, screen, activePath) {
|
|
3314
|
-
if (typeof targetId !== "string" || !LIST_ORDINAL_RE2.test(targetId.trim())) {
|
|
3315
|
-
return false;
|
|
3316
|
-
}
|
|
3317
|
-
const routes = [...screenPathParts(screen), ...activePath ?? []];
|
|
3318
|
-
if (routes.length === 0) return routeLooksLikeFormContext(screen);
|
|
3319
|
-
return routes.some((route) => routeLooksLikeFormContext(route));
|
|
3320
|
-
}
|
|
3321
|
-
function isOptionalStepAutomationFailure(targetId, screen, activePath) {
|
|
3322
|
-
return isOptionalCosmeticTargetId(targetId) || isMisappliedListOrdinalOnForm(targetId, screen, activePath);
|
|
3323
|
-
}
|
|
3324
|
-
var OPTIONAL_STEP_AUTOMATION_HINT = "optional-step-automation-failed";
|
|
3325
|
-
|
|
3326
2138
|
// src/executor/handlers/uiInteractionHandler.ts
|
|
3327
2139
|
function findSnapshotButton(targetId) {
|
|
3328
2140
|
try {
|
|
@@ -3335,16 +2147,16 @@ function findSnapshotButton(targetId) {
|
|
|
3335
2147
|
}
|
|
3336
2148
|
}
|
|
3337
2149
|
function buildUiDiagnose(targetId, cause) {
|
|
3338
|
-
const activePath =
|
|
3339
|
-
const screen = activePath[activePath.length - 1] ??
|
|
2150
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
2151
|
+
const screen = activePath[activePath.length - 1] ?? chunkV3TUIIT2_js.getCurrentScreen() ?? void 0;
|
|
3340
2152
|
const screenPath = activePath.length > 1 ? activePath.join("/") : screen;
|
|
3341
2153
|
const diagnose = {
|
|
3342
2154
|
category: cause === "not-found" ? "component-not-found" : cause === "disabled" ? "disabled" : "unknown",
|
|
3343
2155
|
targetId,
|
|
3344
2156
|
screen: screenPath
|
|
3345
2157
|
};
|
|
3346
|
-
if (isOptionalStepAutomationFailure(targetId, screenPath, activePath)) {
|
|
3347
|
-
diagnose.visibleMessage = OPTIONAL_STEP_AUTOMATION_HINT;
|
|
2158
|
+
if (chunkV3TUIIT2_js.isOptionalStepAutomationFailure(targetId, screenPath, activePath)) {
|
|
2159
|
+
diagnose.visibleMessage = chunkV3TUIIT2_js.OPTIONAL_STEP_AUTOMATION_HINT;
|
|
3348
2160
|
diagnose.recoverable = true;
|
|
3349
2161
|
}
|
|
3350
2162
|
return diagnose;
|
|
@@ -3406,9 +2218,9 @@ function resolveComponentId(componentId, registry, action) {
|
|
|
3406
2218
|
return comp.id;
|
|
3407
2219
|
}
|
|
3408
2220
|
}
|
|
3409
|
-
const currentScreen =
|
|
2221
|
+
const currentScreen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3410
2222
|
if (currentScreen) {
|
|
3411
|
-
const screenMeta =
|
|
2223
|
+
const screenMeta = chunkV3TUIIT2_js.getScreenMetadata(currentScreen);
|
|
3412
2224
|
const actions = screenMeta?.actions;
|
|
3413
2225
|
if (actions && Array.isArray(actions)) {
|
|
3414
2226
|
const metaAction = actions.find(
|
|
@@ -3562,7 +2374,7 @@ function checkModalOcclusion(rawComponentId, action) {
|
|
|
3562
2374
|
return null;
|
|
3563
2375
|
}
|
|
3564
2376
|
if (typeof rawComponentId !== "string" || rawComponentId.trim().length === 0) return null;
|
|
3565
|
-
const fiberRoot =
|
|
2377
|
+
const fiberRoot = chunkV3TUIIT2_js.getFiberRoot();
|
|
3566
2378
|
if (!fiberRoot) return null;
|
|
3567
2379
|
let snap;
|
|
3568
2380
|
try {
|
|
@@ -3625,7 +2437,7 @@ function checkModalOcclusion(rawComponentId, action) {
|
|
|
3625
2437
|
}
|
|
3626
2438
|
function uiInteractionHandler(payload, context) {
|
|
3627
2439
|
const { permissions } = context;
|
|
3628
|
-
const registry = context.registry ??
|
|
2440
|
+
const registry = context.registry ?? chunkV3TUIIT2_js.componentRegistry;
|
|
3629
2441
|
if (!permissions.canInteractUI) {
|
|
3630
2442
|
return {
|
|
3631
2443
|
success: false,
|
|
@@ -3684,7 +2496,7 @@ function uiInteractionHandler(payload, context) {
|
|
|
3684
2496
|
);
|
|
3685
2497
|
return { success: true };
|
|
3686
2498
|
}
|
|
3687
|
-
const screen =
|
|
2499
|
+
const screen = chunkV3TUIIT2_js.getCurrentScreen() ?? void 0;
|
|
3688
2500
|
if (result.reason === "list-not-found" || result.reason === "item-not-found") {
|
|
3689
2501
|
return {
|
|
3690
2502
|
success: false,
|
|
@@ -3825,18 +2637,18 @@ function uiInteractionHandler(payload, context) {
|
|
|
3825
2637
|
// src/executor/formSubmitResolve.ts
|
|
3826
2638
|
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;
|
|
3827
2639
|
var AGENT_CHROME_TARGET_RE = /\b(fechar|close|dismiss|cancelar|cancel|limpar|clear|chat\s+do\s+assistente|assistant\s+chat)\b/i;
|
|
3828
|
-
function
|
|
2640
|
+
function normalize5(value) {
|
|
3829
2641
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
3830
2642
|
}
|
|
3831
2643
|
function isAgentChromeTarget(id, label) {
|
|
3832
|
-
const hay =
|
|
2644
|
+
const hay = normalize5(`${id ?? ""} ${label ?? ""}`);
|
|
3833
2645
|
return AGENT_CHROME_TARGET_RE.test(hay);
|
|
3834
2646
|
}
|
|
3835
2647
|
function isSubmitLikeTarget(id, label) {
|
|
3836
2648
|
if (!id && !label) return false;
|
|
3837
2649
|
if (isAgentChromeTarget(id, label)) return false;
|
|
3838
|
-
const idNorm =
|
|
3839
|
-
|
|
2650
|
+
const idNorm = normalize5(id ?? "");
|
|
2651
|
+
normalize5(label ?? "");
|
|
3840
2652
|
if (/^submit[-_]/i.test(id ?? "")) return true;
|
|
3841
2653
|
if (submitLabelHintsSubmit(label ?? "")) return true;
|
|
3842
2654
|
if (submitLabelHintsSubmit(id ?? "")) return true;
|
|
@@ -3849,7 +2661,7 @@ function submitLabelHintsSubmit(label) {
|
|
|
3849
2661
|
return SUBMIT_LABEL_HINT_RE.test(trimmed);
|
|
3850
2662
|
}
|
|
3851
2663
|
function screenDeclaresSubmitAction(screenName) {
|
|
3852
|
-
const meta =
|
|
2664
|
+
const meta = chunkV3TUIIT2_js.getScreenMetadata(screenName);
|
|
3853
2665
|
return Array.isArray(meta?.actions) && meta.actions.some(
|
|
3854
2666
|
(action) => typeof action === "object" && action !== null && action.type === "submit"
|
|
3855
2667
|
);
|
|
@@ -3882,7 +2694,7 @@ function resolveFormScreensForSubmit(screen, activePath, registry) {
|
|
|
3882
2694
|
} else {
|
|
3883
2695
|
push(screen);
|
|
3884
2696
|
}
|
|
3885
|
-
push(
|
|
2697
|
+
push(chunkV3TUIIT2_js.getCurrentScreen() ?? void 0);
|
|
3886
2698
|
const withSubmit = [];
|
|
3887
2699
|
const rest = [];
|
|
3888
2700
|
for (const name of ordered) {
|
|
@@ -3900,7 +2712,7 @@ function collectSubmitTargetCandidates(screens, registry) {
|
|
|
3900
2712
|
candidates.push(id);
|
|
3901
2713
|
};
|
|
3902
2714
|
for (const screenName of screens) {
|
|
3903
|
-
const meta =
|
|
2715
|
+
const meta = chunkV3TUIIT2_js.getScreenMetadata(screenName);
|
|
3904
2716
|
const submitMeta = Array.isArray(meta?.actions) ? meta.actions.find(
|
|
3905
2717
|
(action) => typeof action === "object" && action !== null && action.type === "submit"
|
|
3906
2718
|
) : void 0;
|
|
@@ -3935,7 +2747,7 @@ function readVisibleValidation(fieldId, label) {
|
|
|
3935
2747
|
);
|
|
3936
2748
|
for (const text of texts) {
|
|
3937
2749
|
const lower = text.toLowerCase();
|
|
3938
|
-
if (
|
|
2750
|
+
if (FORM_VALIDATION_TEXT_RE.test(lower)) {
|
|
3939
2751
|
for (const needle of needles) {
|
|
3940
2752
|
if (lower.includes(needle.toLowerCase())) return text;
|
|
3941
2753
|
}
|
|
@@ -3946,8 +2758,8 @@ function readVisibleValidation(fieldId, label) {
|
|
|
3946
2758
|
return void 0;
|
|
3947
2759
|
}
|
|
3948
2760
|
function resolveScreenContext() {
|
|
3949
|
-
const activePath =
|
|
3950
|
-
const screen = activePath[activePath.length - 1] ??
|
|
2761
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
2762
|
+
const screen = activePath[activePath.length - 1] ?? chunkV3TUIIT2_js.getCurrentScreen() ?? void 0;
|
|
3951
2763
|
return { screen, activePath };
|
|
3952
2764
|
}
|
|
3953
2765
|
function selectValueApplied(fieldId, requestedValue, currentValue, registry) {
|
|
@@ -3955,36 +2767,36 @@ function selectValueApplied(fieldId, requestedValue, currentValue, registry) {
|
|
|
3955
2767
|
if (!requested) return false;
|
|
3956
2768
|
const meta = getMetadataField(fieldId, registry);
|
|
3957
2769
|
const options = Array.isArray(meta?.options) ? meta.options : [];
|
|
3958
|
-
const requestedNorm =
|
|
3959
|
-
const currentNorm =
|
|
2770
|
+
const requestedNorm = chunkV3TUIIT2_js.normalize3(requested);
|
|
2771
|
+
const currentNorm = chunkV3TUIIT2_js.normalize3(currentValue);
|
|
3960
2772
|
if (currentNorm === requestedNorm) return true;
|
|
3961
2773
|
for (const option of options) {
|
|
3962
2774
|
const value = typeof option?.value === "string" ? option.value : "";
|
|
3963
2775
|
const label = typeof option?.label === "string" ? option.label : "";
|
|
3964
|
-
if (
|
|
3965
|
-
if (
|
|
3966
|
-
if (
|
|
2776
|
+
if (chunkV3TUIIT2_js.normalize3(value) === requestedNorm && chunkV3TUIIT2_js.normalize3(label) === currentNorm) return true;
|
|
2777
|
+
if (chunkV3TUIIT2_js.normalize3(label) === requestedNorm && chunkV3TUIIT2_js.normalize3(label) === currentNorm) return true;
|
|
2778
|
+
if (chunkV3TUIIT2_js.normalize3(value) === requestedNorm && chunkV3TUIIT2_js.normalize3(value) === currentNorm) return true;
|
|
3967
2779
|
}
|
|
3968
2780
|
return false;
|
|
3969
2781
|
}
|
|
3970
2782
|
function getMetadataField(fieldId, registry) {
|
|
3971
2783
|
const screens = /* @__PURE__ */ new Set();
|
|
3972
|
-
for (const name of
|
|
3973
|
-
const current =
|
|
2784
|
+
for (const name of chunkV3TUIIT2_js.getActiveRouteNames()) screens.add(name);
|
|
2785
|
+
const current = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3974
2786
|
if (current) screens.add(current);
|
|
3975
2787
|
if (registry) {
|
|
3976
2788
|
for (const comp of registry.snapshot()) {
|
|
3977
2789
|
if (comp.kind === "field" && comp.screen) screens.add(comp.screen);
|
|
3978
2790
|
}
|
|
3979
2791
|
}
|
|
3980
|
-
const needle =
|
|
2792
|
+
const needle = chunkV3TUIIT2_js.normalize3(fieldId);
|
|
3981
2793
|
for (const screenName of screens) {
|
|
3982
|
-
const screenMeta =
|
|
2794
|
+
const screenMeta = chunkV3TUIIT2_js.getScreenMetadata(screenName);
|
|
3983
2795
|
const fields = Array.isArray(screenMeta?.fields) ? screenMeta.fields : [];
|
|
3984
2796
|
const match = fields.find((field) => {
|
|
3985
2797
|
if (!field || typeof field !== "object") return false;
|
|
3986
|
-
const id = typeof field.id === "string" ?
|
|
3987
|
-
const label = typeof field.label === "string" ?
|
|
2798
|
+
const id = typeof field.id === "string" ? chunkV3TUIIT2_js.normalize3(field.id) : "";
|
|
2799
|
+
const label = typeof field.label === "string" ? chunkV3TUIIT2_js.normalize3(field.label) : "";
|
|
3988
2800
|
return id === needle || label === needle;
|
|
3989
2801
|
});
|
|
3990
2802
|
if (match) return match;
|
|
@@ -3996,11 +2808,11 @@ function resolveSelectOption(fieldId, value, registry) {
|
|
|
3996
2808
|
if (!requested) return void 0;
|
|
3997
2809
|
const meta = getMetadataField(fieldId, registry);
|
|
3998
2810
|
const options = Array.isArray(meta?.options) ? meta.options : [];
|
|
3999
|
-
const requestedNorm =
|
|
2811
|
+
const requestedNorm = chunkV3TUIIT2_js.normalize3(requested);
|
|
4000
2812
|
for (const option of options) {
|
|
4001
2813
|
const optionValue = typeof option?.value === "string" ? option.value : "";
|
|
4002
2814
|
const optionLabel = typeof option?.label === "string" ? option.label : "";
|
|
4003
|
-
if (
|
|
2815
|
+
if (chunkV3TUIIT2_js.normalize3(optionValue) === requestedNorm || chunkV3TUIIT2_js.normalize3(optionLabel) === requestedNorm) {
|
|
4004
2816
|
return { value: optionValue, label: optionLabel };
|
|
4005
2817
|
}
|
|
4006
2818
|
}
|
|
@@ -4050,12 +2862,12 @@ async function tryPressSelectOption(fieldId, value, registry, options = {}) {
|
|
|
4050
2862
|
return {
|
|
4051
2863
|
success: false,
|
|
4052
2864
|
error: `No pressable option found for select field "${fieldId}"`,
|
|
4053
|
-
diagnose: { category: "component-not-found", fieldId, screen:
|
|
2865
|
+
diagnose: { category: "component-not-found", fieldId, screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0 }
|
|
4054
2866
|
};
|
|
4055
2867
|
}
|
|
4056
2868
|
function normalizeFieldType(type) {
|
|
4057
2869
|
if (typeof type !== "string") return void 0;
|
|
4058
|
-
const normalized =
|
|
2870
|
+
const normalized = chunkV3TUIIT2_js.normalize3(type);
|
|
4059
2871
|
if (normalized === "select" || normalized === "picker" || normalized === "dropdown") return "select";
|
|
4060
2872
|
if (normalized === "toggle" || normalized === "switch" || normalized === "checkbox") return "toggle";
|
|
4061
2873
|
if (normalized === "date") return "date";
|
|
@@ -4083,7 +2895,7 @@ function fieldCueTerms(fieldId, label) {
|
|
|
4083
2895
|
);
|
|
4084
2896
|
const terms = /* @__PURE__ */ new Set();
|
|
4085
2897
|
for (const value of raw) {
|
|
4086
|
-
const normalized =
|
|
2898
|
+
const normalized = chunkV3TUIIT2_js.normalize3(value);
|
|
4087
2899
|
if (normalized.length >= 3 && normalized !== "id") terms.add(normalized);
|
|
4088
2900
|
}
|
|
4089
2901
|
return [...terms];
|
|
@@ -4094,17 +2906,17 @@ function snapshotHasFieldCue(snap, fieldId, label) {
|
|
|
4094
2906
|
const visible = [
|
|
4095
2907
|
...snap.texts ?? [],
|
|
4096
2908
|
...(snap.buttons ?? []).map((b) => b.label ?? b.id ?? "")
|
|
4097
|
-
].map((text) =>
|
|
2909
|
+
].map((text) => chunkV3TUIIT2_js.normalize3(text)).filter(Boolean);
|
|
4098
2910
|
return visible.some((text) => terms.some((term) => text.includes(term) || term.includes(text)));
|
|
4099
2911
|
}
|
|
4100
2912
|
function scoreCandidateText(value, texts) {
|
|
4101
|
-
const valueNorm =
|
|
2913
|
+
const valueNorm = chunkV3TUIIT2_js.normalize3(String(value ?? ""));
|
|
4102
2914
|
if (!valueNorm) return 0;
|
|
4103
2915
|
let best = 0;
|
|
4104
2916
|
for (const text of texts) {
|
|
4105
|
-
const textNorm =
|
|
2917
|
+
const textNorm = chunkV3TUIIT2_js.normalize3(text);
|
|
4106
2918
|
if (!textNorm) continue;
|
|
4107
|
-
best = Math.max(best, matchScore(valueNorm, textNorm));
|
|
2919
|
+
best = Math.max(best, chunkV3TUIIT2_js.matchScore(valueNorm, textNorm));
|
|
4108
2920
|
}
|
|
4109
2921
|
return best;
|
|
4110
2922
|
}
|
|
@@ -4118,7 +2930,7 @@ function optionTexts(option) {
|
|
|
4118
2930
|
function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue, registry) {
|
|
4119
2931
|
const fieldLabel = getFieldLabel(fieldId, registry);
|
|
4120
2932
|
const fieldCueVisible = snapshotHasFieldCue(snap, fieldId, fieldLabel);
|
|
4121
|
-
const genericValue = isGenericSelectValue(value);
|
|
2933
|
+
const genericValue = chunkV3TUIIT2_js.isGenericSelectValue(value);
|
|
4122
2934
|
const candidates = [];
|
|
4123
2935
|
let order = 0;
|
|
4124
2936
|
const choiceGroups = snap.choiceGroups ?? [];
|
|
@@ -4130,7 +2942,7 @@ function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue,
|
|
|
4130
2942
|
const groupCueScore = Math.max(
|
|
4131
2943
|
globalCueScore,
|
|
4132
2944
|
...groupTexts.map((text) => {
|
|
4133
|
-
const normalized =
|
|
2945
|
+
const normalized = chunkV3TUIIT2_js.normalize3(text);
|
|
4134
2946
|
return fieldCueTerms(fieldId, fieldLabel).some(
|
|
4135
2947
|
(term) => normalized.includes(term) || term.includes(normalized)
|
|
4136
2948
|
) ? 80 : 0;
|
|
@@ -4167,7 +2979,7 @@ function buildChoiceCandidates(snap, fieldId, value, allowFirstVisibleWhenNoCue,
|
|
|
4167
2979
|
const elementCueScore = Math.max(
|
|
4168
2980
|
fieldCueVisible && elements.length <= 12 ? 50 : 0,
|
|
4169
2981
|
...cueTexts.map((text) => {
|
|
4170
|
-
const normalized =
|
|
2982
|
+
const normalized = chunkV3TUIIT2_js.normalize3(text);
|
|
4171
2983
|
return fieldTerms.some(
|
|
4172
2984
|
(term) => normalized.includes(term) || term.includes(normalized)
|
|
4173
2985
|
) ? 80 : 0;
|
|
@@ -4227,7 +3039,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
|
|
|
4227
3039
|
return {
|
|
4228
3040
|
success: false,
|
|
4229
3041
|
error: err?.message ?? "Could not capture visible choices",
|
|
4230
|
-
diagnose: { category: "unknown", fieldId, screen:
|
|
3042
|
+
diagnose: { category: "unknown", fieldId, screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0 }
|
|
4231
3043
|
};
|
|
4232
3044
|
}
|
|
4233
3045
|
const candidates = buildChoiceCandidates(
|
|
@@ -4242,7 +3054,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
|
|
|
4242
3054
|
return {
|
|
4243
3055
|
success: false,
|
|
4244
3056
|
error: `No visible option found for select field "${fieldId}"`,
|
|
4245
|
-
diagnose: { category: "component-not-found", fieldId, screen:
|
|
3057
|
+
diagnose: { category: "component-not-found", fieldId, screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0 }
|
|
4246
3058
|
};
|
|
4247
3059
|
}
|
|
4248
3060
|
const pressed = pressTargetByIdOrLabel(candidate.targetId, registry);
|
|
@@ -4255,7 +3067,7 @@ async function selectVisibleChoiceAsync(fieldId, value, registry, options) {
|
|
|
4255
3067
|
}
|
|
4256
3068
|
function isContinuationButton(label) {
|
|
4257
3069
|
if (!label) return false;
|
|
4258
|
-
return /^(next|continue|proximo|prox|continuar|avancar|avançar)$/i.test(
|
|
3070
|
+
return /^(next|continue|proximo|prox|continuar|avancar|avançar)$/i.test(chunkV3TUIIT2_js.normalize3(label));
|
|
4259
3071
|
}
|
|
4260
3072
|
function pressContinuationButton(registry) {
|
|
4261
3073
|
const snap = captureSnapshot();
|
|
@@ -4268,7 +3080,7 @@ function pressContinuationButton(registry) {
|
|
|
4268
3080
|
return {
|
|
4269
3081
|
success: false,
|
|
4270
3082
|
error: "No enabled continuation button is visible",
|
|
4271
|
-
diagnose: { category: "component-not-found", screen:
|
|
3083
|
+
diagnose: { category: "component-not-found", screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0 }
|
|
4272
3084
|
};
|
|
4273
3085
|
}
|
|
4274
3086
|
return pressTargetByIdOrLabel(id, registry);
|
|
@@ -4288,18 +3100,18 @@ function findField(fieldId, registry) {
|
|
|
4288
3100
|
return registry.getField(comp.id);
|
|
4289
3101
|
}
|
|
4290
3102
|
}
|
|
4291
|
-
const currentScreenForTieBreak =
|
|
4292
|
-
const needle =
|
|
3103
|
+
const currentScreenForTieBreak = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3104
|
+
const needle = chunkV3TUIIT2_js.normalize3(fieldId);
|
|
4293
3105
|
let bestComp;
|
|
4294
3106
|
let bestScore = 0;
|
|
4295
3107
|
let bestOnCurrentScreen = false;
|
|
4296
3108
|
for (const comp of snapshot) {
|
|
4297
3109
|
if (comp.kind !== "field") continue;
|
|
4298
|
-
const candidates = [
|
|
4299
|
-
if (comp.label) candidates.push(
|
|
3110
|
+
const candidates = [chunkV3TUIIT2_js.normalize3(comp.id)];
|
|
3111
|
+
if (comp.label) candidates.push(chunkV3TUIIT2_js.normalize3(comp.label));
|
|
4300
3112
|
let candidateScore = 0;
|
|
4301
3113
|
for (const cand of candidates) {
|
|
4302
|
-
const s = matchScore(needle, cand);
|
|
3114
|
+
const s = chunkV3TUIIT2_js.matchScore(needle, cand);
|
|
4303
3115
|
if (s > candidateScore) candidateScore = s;
|
|
4304
3116
|
}
|
|
4305
3117
|
if (candidateScore === 0) continue;
|
|
@@ -4317,9 +3129,9 @@ function findField(fieldId, registry) {
|
|
|
4317
3129
|
);
|
|
4318
3130
|
return registry.getField(bestComp.id);
|
|
4319
3131
|
}
|
|
4320
|
-
const currentScreen =
|
|
3132
|
+
const currentScreen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
4321
3133
|
if (currentScreen) {
|
|
4322
|
-
const screenMeta =
|
|
3134
|
+
const screenMeta = chunkV3TUIIT2_js.getScreenMetadata(currentScreen);
|
|
4323
3135
|
const fields = screenMeta?.fields;
|
|
4324
3136
|
if (fields && Array.isArray(fields)) {
|
|
4325
3137
|
const metaField = fields.find(
|
|
@@ -4327,12 +3139,12 @@ function findField(fieldId, registry) {
|
|
|
4327
3139
|
);
|
|
4328
3140
|
if (metaField && typeof metaField === "object" && metaField.label) {
|
|
4329
3141
|
const metaLabel = metaField.label;
|
|
4330
|
-
const metaNeedle =
|
|
3142
|
+
const metaNeedle = chunkV3TUIIT2_js.normalize3(metaLabel);
|
|
4331
3143
|
let mBest;
|
|
4332
3144
|
let mBestScore = 0;
|
|
4333
3145
|
for (const comp of snapshot) {
|
|
4334
3146
|
if (comp.kind !== "field" || !comp.label) continue;
|
|
4335
|
-
const score = matchScore(metaNeedle,
|
|
3147
|
+
const score = chunkV3TUIIT2_js.matchScore(metaNeedle, chunkV3TUIIT2_js.normalize3(comp.label));
|
|
4336
3148
|
if (score > mBestScore) {
|
|
4337
3149
|
mBestScore = score;
|
|
4338
3150
|
mBest = comp;
|
|
@@ -4353,7 +3165,7 @@ function findField(fieldId, registry) {
|
|
|
4353
3165
|
);
|
|
4354
3166
|
return registry.getField(bestComp.id);
|
|
4355
3167
|
}
|
|
4356
|
-
|
|
3168
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4357
3169
|
`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"}`
|
|
4358
3170
|
);
|
|
4359
3171
|
return void 0;
|
|
@@ -4373,7 +3185,7 @@ function findToggle(fieldId, registry) {
|
|
|
4373
3185
|
return registry.getToggle(comp.id);
|
|
4374
3186
|
}
|
|
4375
3187
|
}
|
|
4376
|
-
|
|
3188
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4377
3189
|
`findToggle: NOT FOUND \u2014 fieldId="${fieldId}" not in registry. Available toggles: ${snapshot.filter((c) => c.kind === "toggle").map((c) => `"${c.id}"`).join(", ") || "none"}`
|
|
4378
3190
|
);
|
|
4379
3191
|
return void 0;
|
|
@@ -4393,7 +3205,7 @@ function findSlider(fieldId, registry) {
|
|
|
4393
3205
|
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4394
3206
|
async function formFillHandler(payload, context) {
|
|
4395
3207
|
const { permissions } = context;
|
|
4396
|
-
const registry = context.registry ??
|
|
3208
|
+
const registry = context.registry ?? chunkV3TUIIT2_js.componentRegistry;
|
|
4397
3209
|
const { screen, activePath } = resolveScreenContext();
|
|
4398
3210
|
const screenPath = activePath.length > 1 ? activePath.join("/") : screen;
|
|
4399
3211
|
if (!permissions.canFillForms) {
|
|
@@ -4412,7 +3224,7 @@ async function formFillHandler(payload, context) {
|
|
|
4412
3224
|
}
|
|
4413
3225
|
const registrySnapshot = registry.snapshot();
|
|
4414
3226
|
if (registrySnapshot.length === 0) {
|
|
4415
|
-
|
|
3227
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4416
3228
|
"formFillHandler: registry is EMPTY \u2014 auto-tracking may not be active. Ensure enableAppilotsAutoTracking() runs before components mount."
|
|
4417
3229
|
);
|
|
4418
3230
|
} else {
|
|
@@ -4486,7 +3298,7 @@ async function formFillHandler(payload, context) {
|
|
|
4486
3298
|
after = fieldEntry.getValue?.() ?? "";
|
|
4487
3299
|
}
|
|
4488
3300
|
if (!selectValueApplied(fieldId, value, after, registry)) {
|
|
4489
|
-
|
|
3301
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4490
3302
|
`formFillHandler: select field "${fieldId}" setValue("${value}") did not stick (current="${after}"), trying visible choice fallback`
|
|
4491
3303
|
);
|
|
4492
3304
|
const selectResult = await selectVisibleChoice(fieldId, value, registry, {
|
|
@@ -4659,14 +3471,14 @@ async function formFillHandler(payload, context) {
|
|
|
4659
3471
|
if (payload.submitAfterFill === true) {
|
|
4660
3472
|
if (!allSuccess) {
|
|
4661
3473
|
const failedIds = fieldResults.filter((result) => !result.success).map((result) => result.fieldId);
|
|
4662
|
-
|
|
3474
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4663
3475
|
"formFillHandler: submitAfterFill=true requested but some fields failed; NOT submitting" + (failedIds.length > 0 ? ` (failed: ${failedIds.join(", ")})` : "")
|
|
4664
3476
|
);
|
|
4665
3477
|
} else {
|
|
4666
3478
|
await sleep2(120);
|
|
4667
3479
|
const submitResult = pressSubmitButton({ screen, screenPath, activePath }, registry);
|
|
4668
3480
|
if (!submitResult.success) {
|
|
4669
|
-
|
|
3481
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(`formFillHandler: submitAfterFill failed \u2014 ${submitResult.error}`);
|
|
4670
3482
|
return {
|
|
4671
3483
|
success: false,
|
|
4672
3484
|
error: submitResult.error,
|
|
@@ -4678,7 +3490,7 @@ async function formFillHandler(payload, context) {
|
|
|
4678
3490
|
submitTargetId: submitResult.pressedTargetId
|
|
4679
3491
|
});
|
|
4680
3492
|
if (postSubmit.status === "pending") {
|
|
4681
|
-
|
|
3493
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(`formFillHandler: submit still pending \u2014 ${postSubmit.message}`);
|
|
4682
3494
|
return {
|
|
4683
3495
|
success: false,
|
|
4684
3496
|
error: postSubmit.message,
|
|
@@ -4687,7 +3499,7 @@ async function formFillHandler(payload, context) {
|
|
|
4687
3499
|
};
|
|
4688
3500
|
}
|
|
4689
3501
|
if (postSubmit.status === "failure") {
|
|
4690
|
-
|
|
3502
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4691
3503
|
`formFillHandler: submit rejected by visible validation \u2014 ${postSubmit.message}`
|
|
4692
3504
|
);
|
|
4693
3505
|
return {
|
|
@@ -4714,10 +3526,10 @@ async function formFillHandler(payload, context) {
|
|
|
4714
3526
|
};
|
|
4715
3527
|
}
|
|
4716
3528
|
function pressSubmitButton(context, registry) {
|
|
4717
|
-
const activePath = context.activePath ??
|
|
3529
|
+
const activePath = context.activePath ?? chunkV3TUIIT2_js.getActiveRouteNames();
|
|
4718
3530
|
const hintScreen = context.screen ?? context.screenPath;
|
|
4719
3531
|
const formScreens = resolveFormScreensForSubmit(hintScreen, activePath, registry);
|
|
4720
|
-
const resolvedScreen = formScreens.find((name) => screenDeclaresSubmit(name)) ?? formScreens[0] ?? hintScreen ??
|
|
3532
|
+
const resolvedScreen = formScreens.find((name) => screenDeclaresSubmit(name)) ?? formScreens[0] ?? hintScreen ?? chunkV3TUIIT2_js.getCurrentScreen() ?? void 0;
|
|
4721
3533
|
const candidates = collectSubmitTargetCandidates(formScreens, registry);
|
|
4722
3534
|
for (const candidate of candidates) {
|
|
4723
3535
|
const target = registry.getTarget(candidate);
|
|
@@ -4738,7 +3550,7 @@ function pressSubmitButton(context, registry) {
|
|
|
4738
3550
|
}
|
|
4739
3551
|
}
|
|
4740
3552
|
for (const screenName of formScreens) {
|
|
4741
|
-
const screenMeta =
|
|
3553
|
+
const screenMeta = chunkV3TUIIT2_js.getScreenMetadata(screenName);
|
|
4742
3554
|
const submitMeta = Array.isArray(screenMeta?.actions) ? screenMeta.actions.find(
|
|
4743
3555
|
(action) => typeof action === "object" && action !== null && action.type === "submit"
|
|
4744
3556
|
) : void 0;
|
|
@@ -4837,23 +3649,23 @@ var FALLBACK_PAGE_PX = 600;
|
|
|
4837
3649
|
var PAGE_FRACTION = 0.85;
|
|
4838
3650
|
var POST_SCROLL_MOUNT_MS = 400;
|
|
4839
3651
|
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4840
|
-
function
|
|
3652
|
+
function normalizeId2(value) {
|
|
4841
3653
|
return String(value ?? "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "");
|
|
4842
3654
|
}
|
|
4843
3655
|
function buildDiagnose(category, targetId, candidates) {
|
|
4844
3656
|
return {
|
|
4845
3657
|
category,
|
|
4846
3658
|
targetId,
|
|
4847
|
-
screen:
|
|
3659
|
+
screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0,
|
|
4848
3660
|
...candidates && candidates.length > 0 ? { candidates: candidates.slice(0, 8) } : {}
|
|
4849
3661
|
};
|
|
4850
3662
|
}
|
|
4851
3663
|
function resolveListEntry(listId) {
|
|
4852
|
-
const entries =
|
|
3664
|
+
const entries = chunkV3TUIIT2_js.listRegistry.snapshot();
|
|
4853
3665
|
if (entries.length === 0) return {};
|
|
4854
3666
|
if (listId) {
|
|
4855
|
-
const wanted =
|
|
4856
|
-
const match = entries.find((e) =>
|
|
3667
|
+
const wanted = normalizeId2(listId);
|
|
3668
|
+
const match = entries.find((e) => normalizeId2(e.id) === wanted);
|
|
4857
3669
|
if (match) return { entry: match };
|
|
4858
3670
|
return { candidates: entries.map((e) => e.id) };
|
|
4859
3671
|
}
|
|
@@ -4960,23 +3772,23 @@ function targetIdFromPayload(payload) {
|
|
|
4960
3772
|
}
|
|
4961
3773
|
async function waitForPostInteractionSettle(payload) {
|
|
4962
3774
|
const targetId = targetIdFromPayload(payload);
|
|
4963
|
-
const fromScreen =
|
|
4964
|
-
const baseline =
|
|
3775
|
+
const fromScreen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3776
|
+
const baseline = chunkV3TUIIT2_js.probeLoadingState(targetId || null, fromScreen);
|
|
4965
3777
|
await sleepMs(100);
|
|
4966
|
-
const afterPress =
|
|
3778
|
+
const afterPress = chunkV3TUIIT2_js.probeLoadingState(targetId || null, chunkV3TUIIT2_js.getCurrentScreen());
|
|
4967
3779
|
const modalOrLoading = afterPress.modalOpen || afterPress.loading || afterPress.pressedDisabled;
|
|
4968
3780
|
const fingerprintChanged = afterPress.fingerprint !== baseline.fingerprint;
|
|
4969
|
-
const capMs = isListItemPressTarget(targetId) ? 5e3 : 1e4;
|
|
3781
|
+
const capMs = chunkV3TUIIT2_js.isListItemPressTarget(targetId) ? 5e3 : 1e4;
|
|
4970
3782
|
if (modalOrLoading || fingerprintChanged) {
|
|
4971
|
-
await
|
|
3783
|
+
await chunkV3TUIIT2_js.waitForLoadingSettle({
|
|
4972
3784
|
pressedComponentId: targetId || null,
|
|
4973
3785
|
fromScreen,
|
|
4974
3786
|
maxMs: capMs
|
|
4975
3787
|
});
|
|
4976
3788
|
return;
|
|
4977
3789
|
}
|
|
4978
|
-
if (isListItemPressTarget(targetId)) {
|
|
4979
|
-
await
|
|
3790
|
+
if (chunkV3TUIIT2_js.isListItemPressTarget(targetId)) {
|
|
3791
|
+
await chunkV3TUIIT2_js.waitForLoadingSettle({
|
|
4980
3792
|
fromScreen,
|
|
4981
3793
|
maxMs: 3e3
|
|
4982
3794
|
});
|
|
@@ -4996,7 +3808,7 @@ function validateActionPayload(action) {
|
|
|
4996
3808
|
error: message,
|
|
4997
3809
|
diagnose: {
|
|
4998
3810
|
category: "validation",
|
|
4999
|
-
screen:
|
|
3811
|
+
screen: chunkV3TUIIT2_js.getCurrentScreen() ?? void 0,
|
|
5000
3812
|
...ids ?? {}
|
|
5001
3813
|
}
|
|
5002
3814
|
});
|
|
@@ -5063,7 +3875,7 @@ async function executeAction(action, context) {
|
|
|
5063
3875
|
let result;
|
|
5064
3876
|
const validationFailure = validateActionPayload(action);
|
|
5065
3877
|
if (validationFailure) {
|
|
5066
|
-
|
|
3878
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
5067
3879
|
`executeAction: payload validation failed for type="${action.type}" id="${action.id}": ${validationFailure.error}`
|
|
5068
3880
|
);
|
|
5069
3881
|
context.emit({
|
|
@@ -5090,13 +3902,13 @@ async function executeAction(action, context) {
|
|
|
5090
3902
|
case "form_fill":
|
|
5091
3903
|
result = await formFillHandler(action.payload, {
|
|
5092
3904
|
permissions,
|
|
5093
|
-
registry: context.registry ??
|
|
3905
|
+
registry: context.registry ?? chunkV3TUIIT2_js.getDefaultRegistry()
|
|
5094
3906
|
});
|
|
5095
3907
|
break;
|
|
5096
3908
|
case "ui_interaction":
|
|
5097
3909
|
result = uiInteractionHandler(action.payload, {
|
|
5098
3910
|
permissions,
|
|
5099
|
-
registry: context.registry ??
|
|
3911
|
+
registry: context.registry ?? chunkV3TUIIT2_js.getDefaultRegistry()
|
|
5100
3912
|
});
|
|
5101
3913
|
if (result.success) {
|
|
5102
3914
|
await waitForPostInteractionSettle(action.payload);
|
|
@@ -5126,7 +3938,7 @@ async function executeAction(action, context) {
|
|
|
5126
3938
|
};
|
|
5127
3939
|
}
|
|
5128
3940
|
} catch (err) {
|
|
5129
|
-
|
|
3941
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
5130
3942
|
`executeAction: exception during action type="${action.type}" id="${action.id}": ${err?.message}`
|
|
5131
3943
|
);
|
|
5132
3944
|
result = {
|
|
@@ -5150,90 +3962,156 @@ async function executeAction(action, context) {
|
|
|
5150
3962
|
});
|
|
5151
3963
|
return result;
|
|
5152
3964
|
}
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
}
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
canNavigate: true,
|
|
5166
|
-
canFillForms: true,
|
|
5167
|
-
canInteractUI: true,
|
|
5168
|
-
canSubmitForms: true
|
|
5169
|
-
};
|
|
5170
|
-
function readPayload(action) {
|
|
5171
|
-
return action.payload ?? {};
|
|
5172
|
-
}
|
|
5173
|
-
function isStructuredRisky(value) {
|
|
5174
|
-
return value === true || value === "destructive" || value === "danger" || value === "high";
|
|
3965
|
+
var POST_ACTION_LOADING_MAX_MS = 6e3;
|
|
3966
|
+
function resolveScreenMetadata() {
|
|
3967
|
+
const activePath = chunkV3TUIIT2_js.getActiveRouteNames();
|
|
3968
|
+
for (let i = activePath.length - 1; i >= 0; i--) {
|
|
3969
|
+
const name = activePath[i];
|
|
3970
|
+
const meta = name ? chunkV3TUIIT2_js.getScreenMetadata(name) : void 0;
|
|
3971
|
+
if (meta && (Array.isArray(meta.fields) && meta.fields.length > 0 || Array.isArray(meta.actions) && meta.actions.length > 0)) {
|
|
3972
|
+
return meta;
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
const screen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3976
|
+
return screen ? chunkV3TUIIT2_js.getScreenMetadata(screen) : void 0;
|
|
5175
3977
|
}
|
|
5176
|
-
function
|
|
5177
|
-
|
|
5178
|
-
|
|
3978
|
+
function buildAgentContext(extras = {}) {
|
|
3979
|
+
const context = {};
|
|
3980
|
+
context.platform = "react-native";
|
|
3981
|
+
const screen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
3982
|
+
if (screen) context.currentScreen = screen;
|
|
3983
|
+
const navigationState = chunkV3TUIIT2_js.getNavigationStateSnapshot();
|
|
3984
|
+
if (navigationState) context.navigationState = navigationState;
|
|
3985
|
+
const screenMeta = resolveScreenMetadata();
|
|
3986
|
+
if (screenMeta) context.screenMetadata = screenMeta;
|
|
3987
|
+
try {
|
|
3988
|
+
context.snapshot = captureSnapshot();
|
|
3989
|
+
} catch (err) {
|
|
3990
|
+
chunkV3TUIIT2_js.appilotsDebugWarn("buildAgentContext: captureSnapshot failed", err);
|
|
3991
|
+
}
|
|
3992
|
+
const registry = chunkV3TUIIT2_js.componentRegistry.snapshot();
|
|
3993
|
+
const filtered = screen ? registry.filter((c) => !c.screen || c.screen === screen) : registry;
|
|
3994
|
+
if (filtered.length > 0) context.registeredComponents = filtered;
|
|
3995
|
+
const registeredLists = chunkV3TUIIT2_js.listRegistry.snapshot();
|
|
3996
|
+
if (registeredLists.length > 0) {
|
|
3997
|
+
context.registeredLists = registeredLists.map(
|
|
3998
|
+
({ scrollToIndex, scrollToOffset, getScrollMetrics, dataPreview, ...rest }) => rest
|
|
3999
|
+
);
|
|
5179
4000
|
}
|
|
5180
|
-
|
|
4001
|
+
if (extras.loadingPending === true) context.loadingPending = true;
|
|
4002
|
+
return context;
|
|
5181
4003
|
}
|
|
5182
|
-
function
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
source: "action-policy"
|
|
5189
|
-
};
|
|
4004
|
+
function resolveActionHints(screen, pressedComponentId, actionPayload) {
|
|
4005
|
+
if (actionPayload && typeof actionPayload === "object") {
|
|
4006
|
+
const inline = actionPayload._appilotsInferred;
|
|
4007
|
+
if (inline && typeof inline === "object") {
|
|
4008
|
+
return inline;
|
|
4009
|
+
}
|
|
5190
4010
|
}
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
4011
|
+
if (!screen || !pressedComponentId) return void 0;
|
|
4012
|
+
const meta = chunkV3TUIIT2_js.getScreenMetadata(screen);
|
|
4013
|
+
const actions = meta?.actions;
|
|
4014
|
+
if (!actions || actions.length === 0) return void 0;
|
|
4015
|
+
for (const a of actions) {
|
|
4016
|
+
if (typeof a === "string") continue;
|
|
4017
|
+
if (chunkV3TUIIT2_js.idLooselyMatches(a.id, pressedComponentId)) {
|
|
4018
|
+
const inferred = a.appilotsInferred;
|
|
4019
|
+
if (inferred && typeof inferred === "object") return inferred;
|
|
4020
|
+
return void 0;
|
|
5200
4021
|
}
|
|
5201
4022
|
}
|
|
5202
|
-
return
|
|
5203
|
-
}
|
|
5204
|
-
function isConfirmGateFor(confirm, target) {
|
|
5205
|
-
if (confirm.type !== "confirm") return false;
|
|
5206
|
-
const payload = readPayload(confirm);
|
|
5207
|
-
const injectedBy = payload.injectedBy;
|
|
5208
|
-
return payload.gatedActionId === target.id && (injectedBy === "permissions" || injectedBy === "sdk-local");
|
|
5209
|
-
}
|
|
5210
|
-
function toDestructiveConfirmPayload(confirm, policy, fallbackLabel) {
|
|
5211
|
-
const payload = readPayload(confirm);
|
|
5212
|
-
const existingDescription = typeof payload.actionDescription === "string" && payload.actionDescription ? payload.actionDescription : void 0;
|
|
5213
|
-
const labelText = policy.label ?? existingDescription ?? fallbackLabel ?? "esta a\xE7\xE3o";
|
|
5214
|
-
return {
|
|
5215
|
-
...payload,
|
|
5216
|
-
title: "A\xE7\xE3o destrutiva",
|
|
5217
|
-
message: `Tem certeza que deseja "${labelText}"?`,
|
|
5218
|
-
actionDescription: labelText,
|
|
5219
|
-
severity: "danger",
|
|
5220
|
-
destructive: true,
|
|
5221
|
-
confirmLabel: typeof payload.confirmLabel === "string" && payload.confirmLabel ? payload.confirmLabel : "Sim, executar",
|
|
5222
|
-
cancelLabel: typeof payload.cancelLabel === "string" && payload.cancelLabel ? payload.cancelLabel : "Cancelar"
|
|
5223
|
-
};
|
|
5224
|
-
}
|
|
5225
|
-
function confirmNeedsDestructiveUpgrade(confirm) {
|
|
5226
|
-
const payload = readPayload(confirm);
|
|
5227
|
-
return confirm.destructive !== true || payload.destructive !== true || payload.severity !== "danger";
|
|
4023
|
+
return void 0;
|
|
5228
4024
|
}
|
|
5229
|
-
function
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
4025
|
+
async function settleTurn(input) {
|
|
4026
|
+
const toolWithoutEffectIds = /* @__PURE__ */ new Set();
|
|
4027
|
+
let loadingPending = false;
|
|
4028
|
+
const { turnActions, pressedComponentId: pressedId, navigateTargetScreen } = input;
|
|
4029
|
+
const successById = new Map(input.results.map((r) => [r.actionId, r.success]));
|
|
4030
|
+
const typeById = new Map(input.results.map((r) => [r.actionId, r.type]));
|
|
4031
|
+
if (input.hadNavigate) {
|
|
4032
|
+
const settle = await chunkV3TUIIT2_js.waitForScreenSettle({
|
|
4033
|
+
expectingChange: true,
|
|
4034
|
+
fromScreen: input.preNavigateScreen ?? null,
|
|
4035
|
+
fromSignature: input.preNavigateSignature ?? null,
|
|
4036
|
+
targetScreen: navigateTargetScreen,
|
|
4037
|
+
maxMs: 3500
|
|
4038
|
+
});
|
|
4039
|
+
console.log(
|
|
4040
|
+
`[Appilots] settleTurn: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
4041
|
+
);
|
|
4042
|
+
const navigationMoved = settle.transitioned || chunkV3TUIIT2_js.screenDepartedBaseline(
|
|
4043
|
+
input.preNavigateScreen ?? null,
|
|
4044
|
+
input.preNavigateSignature ?? null
|
|
4045
|
+
) || !!navigateTargetScreen && !!settle.screen && chunkV3TUIIT2_js.routesBelongToSameFeature(settle.screen, navigateTargetScreen);
|
|
4046
|
+
if (!navigationMoved) {
|
|
4047
|
+
chunkV3TUIIT2_js.appilotsDebugWarn(
|
|
4048
|
+
`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.`
|
|
4049
|
+
);
|
|
4050
|
+
for (const [actionId, type] of typeById) {
|
|
4051
|
+
if (type === "navigate") toolWithoutEffectIds.add(actionId);
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
const loadSettle = await chunkV3TUIIT2_js.waitForLoadingSettle({
|
|
4055
|
+
fromScreen: settle.screen ?? null,
|
|
4056
|
+
maxMs: 3e3
|
|
4057
|
+
});
|
|
4058
|
+
console.log(
|
|
4059
|
+
`[Appilots] settleTurn: post-navigate loading settle \u2014 settled=${loadSettle.settled} loadingPending=${loadSettle.loadingPending} probes=${loadSettle.probeCount} waitedMs=${loadSettle.waitedMs}`
|
|
4060
|
+
);
|
|
4061
|
+
if (loadSettle.loadingPending) loadingPending = true;
|
|
4062
|
+
} else {
|
|
4063
|
+
const pressedAction = turnActions.find(
|
|
4064
|
+
(a) => a.type === "ui_interaction" && a.payload?.componentId === pressedId
|
|
4065
|
+
);
|
|
4066
|
+
const hints = resolveActionHints(chunkV3TUIIT2_js.getCurrentScreen(), pressedId, pressedAction?.payload);
|
|
4067
|
+
const settleCapMs = hints?.isAsyncTrigger === true ? 1e4 : POST_ACTION_LOADING_MAX_MS;
|
|
4068
|
+
const loadSettle = await chunkV3TUIIT2_js.waitForLoadingSettle({
|
|
4069
|
+
pressedComponentId: pressedId,
|
|
4070
|
+
fromScreen: chunkV3TUIIT2_js.getCurrentScreen(),
|
|
4071
|
+
maxMs: settleCapMs
|
|
4072
|
+
});
|
|
4073
|
+
console.log(
|
|
4074
|
+
`[Appilots] 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)" : ""}`
|
|
4075
|
+
);
|
|
4076
|
+
if (loadSettle.loadingPending) loadingPending = true;
|
|
4077
|
+
const listPressActions = turnActions.filter((action) => {
|
|
4078
|
+
const target = chunkV3TUIIT2_js.actionPressTargetId(action);
|
|
4079
|
+
return target ? chunkV3TUIIT2_js.isListItemPressTarget(target) : false;
|
|
4080
|
+
});
|
|
4081
|
+
if (listPressActions.length > 0) {
|
|
4082
|
+
const baselineFingerprint = chunkV3TUIIT2_js.probeLoadingState(pressedId, chunkV3TUIIT2_js.getCurrentScreen()).fingerprint;
|
|
4083
|
+
const postPressSettle = await chunkV3TUIIT2_js.waitForScreenSettle({
|
|
4084
|
+
expectingChange: true,
|
|
4085
|
+
fromScreen: input.preNavigateScreen ?? null,
|
|
4086
|
+
fromSignature: input.preNavigateSignature ?? null,
|
|
4087
|
+
maxMs: 2500
|
|
4088
|
+
});
|
|
4089
|
+
await chunkV3TUIIT2_js.waitForLoadingSettle({
|
|
4090
|
+
pressedComponentId: pressedId,
|
|
4091
|
+
fromScreen: chunkV3TUIIT2_js.getCurrentScreen(),
|
|
4092
|
+
maxMs: 4e3
|
|
4093
|
+
});
|
|
4094
|
+
const afterFingerprint = chunkV3TUIIT2_js.probeLoadingState(pressedId, chunkV3TUIIT2_js.getCurrentScreen()).fingerprint;
|
|
4095
|
+
console.log(
|
|
4096
|
+
`[Appilots] settleTurn: post-list-press settle \u2014 screen="${postPressSettle.screen}" transitioned=${postPressSettle.transitioned} timedOut=${postPressSettle.timedOut} waitedMs=${postPressSettle.waitedMs}`
|
|
4097
|
+
);
|
|
4098
|
+
if (!postPressSettle.transitioned && baselineFingerprint === afterFingerprint) {
|
|
4099
|
+
for (const action of listPressActions) {
|
|
4100
|
+
if (successById.get(action.id)) {
|
|
4101
|
+
toolWithoutEffectIds.add(action.id);
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
return { loadingPending, toolWithoutEffectIds };
|
|
5236
4108
|
}
|
|
4109
|
+
var reactNativeChatAdapter = {
|
|
4110
|
+
buildContext: buildAgentContext,
|
|
4111
|
+
getCurrentScreen: chunkV3TUIIT2_js.getCurrentScreen,
|
|
4112
|
+
getCurrentScreenSignature: chunkV3TUIIT2_js.getCurrentScreenSignature,
|
|
4113
|
+
settleTurn
|
|
4114
|
+
};
|
|
5237
4115
|
async function runWithConfirmedDestructiveContext(enabled, fn) {
|
|
5238
4116
|
if (!enabled) return fn();
|
|
5239
4117
|
const originalAlert = reactNative.Alert.alert;
|
|
@@ -5263,321 +4141,120 @@ async function runWithConfirmedDestructiveContext(enabled, fn) {
|
|
|
5263
4141
|
reactNative.Alert.alert = originalAlert;
|
|
5264
4142
|
}
|
|
5265
4143
|
}
|
|
5266
|
-
function
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
if (!action?.id) return;
|
|
5279
|
-
console.log(
|
|
5280
|
-
`[Appilots] useAppilotsActions: received agent:action:start event, action id="${action.id}", type="${action.type}"`
|
|
5281
|
-
);
|
|
5282
|
-
setActions((prev) => {
|
|
5283
|
-
if (prev.some((a) => a.id === action.id)) return prev;
|
|
5284
|
-
return [...prev, action];
|
|
5285
|
-
});
|
|
5286
|
-
}
|
|
5287
|
-
});
|
|
5288
|
-
return unsubscribe;
|
|
5289
|
-
}, [subscribe]);
|
|
5290
|
-
const processedRef = react.useRef(/* @__PURE__ */ new Set());
|
|
5291
|
-
react.useEffect(() => {
|
|
5292
|
-
if (!autoExecute) return;
|
|
5293
|
-
const pending = actions.filter(
|
|
5294
|
-
(a) => a.status === "pending" && !processedRef.current.has(a.id)
|
|
5295
|
-
);
|
|
5296
|
-
console.log(`[Appilots] useAppilotsActions: autoExecute check \u2014 ${pending.length} pending action(s) to process`);
|
|
5297
|
-
const findGatingConfirm = (target) => {
|
|
5298
|
-
return actions.find((a) => {
|
|
5299
|
-
if (a.type !== "confirm") return false;
|
|
5300
|
-
const payload = a.payload;
|
|
5301
|
-
const injectedBy = payload?.injectedBy;
|
|
5302
|
-
return payload?.gatedActionId === target.id && (injectedBy === "permissions" || injectedBy === "sdk-local");
|
|
5303
|
-
});
|
|
5304
|
-
};
|
|
5305
|
-
const ensureLocalConfirmIfNeeded = (target) => {
|
|
5306
|
-
if (target.type !== "ui_interaction") return false;
|
|
5307
|
-
const payload = target.payload;
|
|
5308
|
-
const componentId = typeof payload?.componentId === "string" && payload.componentId || typeof payload?.targetId === "string" && payload.targetId || "";
|
|
5309
|
-
let policy = payloadRequiresConfirmation(target);
|
|
5310
|
-
if (!policy.required && componentId) {
|
|
5311
|
-
const screen = chunkSIBKF7AA_js.getCurrentScreen();
|
|
5312
|
-
const meta = screen ? chunkSIBKF7AA_js.getScreenMetadata(screen) : void 0;
|
|
5313
|
-
const actionsMeta = meta?.actions;
|
|
5314
|
-
if (actionsMeta && Array.isArray(actionsMeta)) {
|
|
5315
|
-
const match = actionsMeta.find((a) => {
|
|
5316
|
-
if (typeof a === "string") return false;
|
|
5317
|
-
return metadataMatchesComponentId(a.id, componentId);
|
|
5318
|
-
});
|
|
5319
|
-
if (match && typeof match !== "string") {
|
|
5320
|
-
policy = actionMetadataRequiresConfirmation(match);
|
|
5321
|
-
}
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
const existingGate = findGatingConfirm(target);
|
|
5325
|
-
if (existingGate) {
|
|
5326
|
-
if (policy.required && confirmNeedsDestructiveUpgrade(existingGate)) {
|
|
5327
|
-
setActions(
|
|
5328
|
-
(prev) => prev.map(
|
|
5329
|
-
(a) => a.id === existingGate.id ? {
|
|
5330
|
-
...a,
|
|
5331
|
-
destructive: true,
|
|
5332
|
-
payload: toDestructiveConfirmPayload(
|
|
5333
|
-
a,
|
|
5334
|
-
policy,
|
|
5335
|
-
componentId || target.id
|
|
5336
|
-
)
|
|
5337
|
-
} : a
|
|
5338
|
-
)
|
|
5339
|
-
);
|
|
5340
|
-
}
|
|
5341
|
-
return false;
|
|
5342
|
-
}
|
|
5343
|
-
if (!policy.required) return false;
|
|
5344
|
-
const confirmId = `confirm-local-${target.id}`;
|
|
5345
|
-
setActions((prev) => {
|
|
5346
|
-
const existingGate2 = prev.some((a) => {
|
|
5347
|
-
if (a.type !== "confirm") return false;
|
|
5348
|
-
const p = a.payload;
|
|
5349
|
-
if (!p || p.gatedActionId !== target.id) return false;
|
|
5350
|
-
return p.injectedBy === "permissions" || p.injectedBy === "sdk-local";
|
|
5351
|
-
});
|
|
5352
|
-
if (existingGate2) {
|
|
5353
|
-
const gateToUpgrade = prev.find(
|
|
5354
|
-
(a) => isConfirmGateFor(a, target) && confirmNeedsDestructiveUpgrade(a)
|
|
5355
|
-
);
|
|
5356
|
-
if (!gateToUpgrade) return prev;
|
|
5357
|
-
return prev.map(
|
|
5358
|
-
(a) => a.id === gateToUpgrade.id ? {
|
|
5359
|
-
...a,
|
|
5360
|
-
destructive: true,
|
|
5361
|
-
payload: toDestructiveConfirmPayload(
|
|
5362
|
-
a,
|
|
5363
|
-
policy,
|
|
5364
|
-
componentId || target.id
|
|
5365
|
-
)
|
|
5366
|
-
} : a
|
|
5367
|
-
);
|
|
5368
|
-
}
|
|
5369
|
-
if (prev.some((a) => a.id === confirmId)) return prev;
|
|
5370
|
-
console.log(
|
|
5371
|
-
`[Appilots] useAppilotsActions: action "${componentId || target.id}" requires confirmation (${policy.source ?? "local-policy"}) \u2014 synthesising local confirm (id="${confirmId}")`
|
|
5372
|
-
);
|
|
5373
|
-
const synth = {
|
|
5374
|
-
id: confirmId,
|
|
5375
|
-
type: "confirm",
|
|
5376
|
-
payload: toDestructiveConfirmPayload(
|
|
5377
|
-
{
|
|
5378
|
-
payload: {
|
|
5379
|
-
gatedActionId: target.id,
|
|
5380
|
-
gatedActionType: target.type,
|
|
5381
|
-
injectedBy: "sdk-local"
|
|
5382
|
-
},
|
|
5383
|
-
messageId: target.messageId
|
|
5384
|
-
},
|
|
5385
|
-
policy,
|
|
5386
|
-
componentId || "esta a\xE7\xE3o"
|
|
5387
|
-
),
|
|
5388
|
-
status: "pending",
|
|
5389
|
-
messageId: target.messageId
|
|
5390
|
-
};
|
|
5391
|
-
emit({
|
|
5392
|
-
type: "agent:action:start",
|
|
5393
|
-
timestamp: Date.now(),
|
|
5394
|
-
data: { action: synth }
|
|
5395
|
-
});
|
|
5396
|
-
const idx = prev.findIndex((a) => a.id === target.id);
|
|
5397
|
-
if (idx < 0) return [...prev, synth];
|
|
5398
|
-
return [...prev.slice(0, idx), synth, ...prev.slice(idx)];
|
|
5399
|
-
});
|
|
5400
|
-
return true;
|
|
5401
|
-
};
|
|
5402
|
-
const executeSequentially = async () => {
|
|
5403
|
-
let lastWasNavigate = false;
|
|
5404
|
-
let preNavScreen = null;
|
|
5405
|
-
let preNavSignature = null;
|
|
5406
|
-
for (const action of pending) {
|
|
5407
|
-
if (action.type === "confirm") {
|
|
5408
|
-
continue;
|
|
5409
|
-
}
|
|
5410
|
-
if (ensureLocalConfirmIfNeeded(action)) {
|
|
5411
|
-
continue;
|
|
5412
|
-
}
|
|
5413
|
-
const gate = findGatingConfirm(action);
|
|
5414
|
-
if (gate) {
|
|
5415
|
-
if (gate.status === "pending" || gate.status === "executing") {
|
|
5416
|
-
console.log(
|
|
5417
|
-
`[Appilots] useAppilotsActions: action id="${action.id}" is gated by confirm id="${gate.id}" (status=${gate.status}); waiting for user decision`
|
|
5418
|
-
);
|
|
5419
|
-
continue;
|
|
5420
|
-
}
|
|
5421
|
-
if (gate.status === "failed") {
|
|
5422
|
-
console.log(
|
|
5423
|
-
`[Appilots] useAppilotsActions: confirm id="${gate.id}" was rejected; skipping gated action id="${action.id}"`
|
|
5424
|
-
);
|
|
5425
|
-
processedRef.current.add(action.id);
|
|
5426
|
-
setActions(
|
|
5427
|
-
(prev) => prev.map(
|
|
5428
|
-
(a) => a.id === action.id ? { ...a, status: "failed", error: "Confirm canceled by user" } : a
|
|
5429
|
-
)
|
|
5430
|
-
);
|
|
5431
|
-
try {
|
|
5432
|
-
await client.completeAction(action.id, false, "Confirm canceled by user");
|
|
5433
|
-
} catch {
|
|
5434
|
-
}
|
|
5435
|
-
emit({
|
|
5436
|
-
type: "agent:action:error",
|
|
5437
|
-
timestamp: Date.now(),
|
|
5438
|
-
data: {
|
|
5439
|
-
actionId: action.id,
|
|
5440
|
-
actionType: action.type,
|
|
5441
|
-
success: false,
|
|
5442
|
-
error: "Confirm canceled by user"
|
|
5443
|
-
}
|
|
5444
|
-
});
|
|
5445
|
-
continue;
|
|
5446
|
-
}
|
|
5447
|
-
}
|
|
5448
|
-
if (lastWasNavigate) {
|
|
5449
|
-
const settle = await chunkSIBKF7AA_js.waitForScreenSettle({
|
|
5450
|
-
expectingChange: true,
|
|
5451
|
-
fromScreen: preNavScreen,
|
|
5452
|
-
fromSignature: preNavSignature,
|
|
5453
|
-
maxMs: 3500
|
|
5454
|
-
});
|
|
5455
|
-
console.log(
|
|
5456
|
-
`[Appilots] useAppilotsActions: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
5457
|
-
);
|
|
5458
|
-
}
|
|
5459
|
-
console.log(`[Appilots] useAppilotsActions: auto-executing action id="${action.id}" type="${action.type}"`);
|
|
5460
|
-
processedRef.current.add(action.id);
|
|
5461
|
-
if (action.type === "navigate") {
|
|
5462
|
-
preNavScreen = chunkSIBKF7AA_js.getCurrentScreen();
|
|
5463
|
-
preNavSignature = chunkSIBKF7AA_js.getCurrentScreenSignature();
|
|
5464
|
-
}
|
|
5465
|
-
const succeeded = await approveActionImpl(action.id);
|
|
5466
|
-
if (!succeeded) {
|
|
5467
|
-
chunkSIBKF7AA_js.appilotsDebugWarn(
|
|
5468
|
-
`useAppilotsActions: stopping current action batch after failed action id="${action.id}"`
|
|
5469
|
-
);
|
|
5470
|
-
break;
|
|
5471
|
-
}
|
|
5472
|
-
lastWasNavigate = action.type === "navigate";
|
|
5473
|
-
}
|
|
5474
|
-
};
|
|
5475
|
-
void executeSequentially();
|
|
5476
|
-
}, [actions, autoExecute]);
|
|
5477
|
-
const approveActionImpl = react.useCallback(
|
|
5478
|
-
async (actionId) => {
|
|
5479
|
-
const action = actionsRef.current.find((a) => a.id === actionId);
|
|
5480
|
-
if (!action) return false;
|
|
5481
|
-
console.log(`[Appilots] useAppilotsActions: action status transition: pending \u2192 executing (id="${actionId}", type="${action.type}")`);
|
|
5482
|
-
setActions(
|
|
5483
|
-
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "executing" } : a)
|
|
4144
|
+
function createReactNativeActionRunner(getOptions) {
|
|
4145
|
+
return {
|
|
4146
|
+
async execute(action, { confirmedDestructive }) {
|
|
4147
|
+
const { permissions, emit, navigationRef } = getOptions();
|
|
4148
|
+
const navRef = navigationRef?.current ? navigationRef : { current: chunkV3TUIIT2_js.getNavigationRef() };
|
|
4149
|
+
return runWithConfirmedDestructiveContext(
|
|
4150
|
+
confirmedDestructive,
|
|
4151
|
+
() => executeAction(action, {
|
|
4152
|
+
navigationRef: navRef,
|
|
4153
|
+
permissions,
|
|
4154
|
+
emit
|
|
4155
|
+
})
|
|
5484
4156
|
);
|
|
5485
|
-
try {
|
|
5486
|
-
const navRef = navRefStable.current?.current ? navRefStable.current : { current: chunkSIBKF7AA_js.getNavigationRef() };
|
|
5487
|
-
const confirmedDestructive = actionHasCompletedConfirmGate(
|
|
5488
|
-
action,
|
|
5489
|
-
actionsRef.current
|
|
5490
|
-
);
|
|
5491
|
-
const result = await runWithConfirmedDestructiveContext(
|
|
5492
|
-
confirmedDestructive,
|
|
5493
|
-
() => executeAction(
|
|
5494
|
-
{ ...action, status: "executing" },
|
|
5495
|
-
{
|
|
5496
|
-
navigationRef: navRef,
|
|
5497
|
-
permissions: config.permissions ?? DEFAULT_PERMISSIONS2,
|
|
5498
|
-
emit
|
|
5499
|
-
}
|
|
5500
|
-
)
|
|
5501
|
-
);
|
|
5502
|
-
if (result.success) {
|
|
5503
|
-
console.log(`[Appilots] useAppilotsActions: action status transition: executing \u2192 completed (id="${actionId}", type="${action.type}")`);
|
|
5504
|
-
setActions(
|
|
5505
|
-
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "completed" } : a)
|
|
5506
|
-
);
|
|
5507
|
-
try {
|
|
5508
|
-
await client.completeAction(actionId, true);
|
|
5509
|
-
} catch {
|
|
5510
|
-
}
|
|
5511
|
-
return true;
|
|
5512
|
-
} else {
|
|
5513
|
-
chunkSIBKF7AA_js.appilotsDebugWarn(
|
|
5514
|
-
`useAppilotsActions: action status transition: executing \u2192 failed (id="${actionId}", type="${action.type}", error="${result.error}")`
|
|
5515
|
-
);
|
|
5516
|
-
setActions(
|
|
5517
|
-
(prev) => prev.map(
|
|
5518
|
-
(a) => a.id === actionId ? { ...a, status: "failed", error: result.error } : a
|
|
5519
|
-
)
|
|
5520
|
-
);
|
|
5521
|
-
try {
|
|
5522
|
-
await client.completeAction(
|
|
5523
|
-
actionId,
|
|
5524
|
-
false,
|
|
5525
|
-
result.error,
|
|
5526
|
-
result.diagnose
|
|
5527
|
-
);
|
|
5528
|
-
} catch {
|
|
5529
|
-
}
|
|
5530
|
-
return false;
|
|
5531
|
-
}
|
|
5532
|
-
} catch (err) {
|
|
5533
|
-
const errorMsg = err instanceof Error ? err.message : "Action failed";
|
|
5534
|
-
chunkSIBKF7AA_js.appilotsDebugWarn(
|
|
5535
|
-
`useAppilotsActions: action status transition: executing \u2192 failed (exception) (id="${actionId}", error="${errorMsg}")`
|
|
5536
|
-
);
|
|
5537
|
-
setActions(
|
|
5538
|
-
(prev) => prev.map(
|
|
5539
|
-
(a) => a.id === actionId ? { ...a, status: "failed", error: errorMsg } : a
|
|
5540
|
-
)
|
|
5541
|
-
);
|
|
5542
|
-
try {
|
|
5543
|
-
await client.completeAction(actionId, false, errorMsg);
|
|
5544
|
-
} catch {
|
|
5545
|
-
}
|
|
5546
|
-
return false;
|
|
5547
|
-
}
|
|
5548
4157
|
},
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
setActions(
|
|
5554
|
-
(prev) => prev.map(
|
|
5555
|
-
(a) => a.id === actionId ? { ...a, status: "failed", error: "Rejected by user" } : a
|
|
5556
|
-
)
|
|
5557
|
-
);
|
|
5558
|
-
const action = actionsRef.current.find((a) => a.id === actionId);
|
|
5559
|
-
emit({
|
|
5560
|
-
type: "agent:action:error",
|
|
5561
|
-
timestamp: Date.now(),
|
|
5562
|
-
data: {
|
|
5563
|
-
actionId,
|
|
5564
|
-
actionType: action?.type,
|
|
5565
|
-
success: false,
|
|
5566
|
-
error: "Rejected by user"
|
|
5567
|
-
}
|
|
5568
|
-
});
|
|
5569
|
-
try {
|
|
5570
|
-
await client.completeAction(actionId, false, "Rejected by user");
|
|
5571
|
-
} catch {
|
|
5572
|
-
}
|
|
4158
|
+
getCurrentScreen: chunkV3TUIIT2_js.getCurrentScreen,
|
|
4159
|
+
getCurrentScreenSignature: chunkV3TUIIT2_js.getCurrentScreenSignature,
|
|
4160
|
+
getScreenActionsMetadata(screen) {
|
|
4161
|
+
return chunkV3TUIIT2_js.getScreenMetadata(screen)?.actions;
|
|
5573
4162
|
},
|
|
4163
|
+
waitForScreenSettle: chunkV3TUIIT2_js.waitForScreenSettle
|
|
4164
|
+
};
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
// src/hooks/useAppilotsChat.ts
|
|
4168
|
+
function useAppilotsChat(options = {}) {
|
|
4169
|
+
const { client, emit, subscribe } = chunkV3TUIIT2_js.useAppilotsContext();
|
|
4170
|
+
const machine = react.useMemo(
|
|
4171
|
+
() => new chunkV3TUIIT2_js.ChatSessionMachine(
|
|
4172
|
+
{ client, adapter: reactNativeChatAdapter, emit, warn: chunkV3TUIIT2_js.appilotsDebugWarn },
|
|
4173
|
+
options
|
|
4174
|
+
),
|
|
4175
|
+
// A new machine per client/provider instance — options are pushed in
|
|
4176
|
+
// below so changing copy never resets the transcript.
|
|
4177
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4178
|
+
[client, emit]
|
|
4179
|
+
);
|
|
4180
|
+
const optionsRef = react.useRef(options);
|
|
4181
|
+
optionsRef.current = options;
|
|
4182
|
+
machine.setOptions(options);
|
|
4183
|
+
const state = react.useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
|
|
4184
|
+
react.useEffect(() => subscribe(machine.handleEvent), [subscribe, machine]);
|
|
4185
|
+
react.useEffect(() => () => machine.dispose(), [machine]);
|
|
4186
|
+
const requestHuman = react.useCallback(
|
|
4187
|
+
(reason) => machine.requestHuman(reason),
|
|
4188
|
+
[machine]
|
|
4189
|
+
);
|
|
4190
|
+
const sendMessage = react.useCallback((content) => machine.sendMessage(content), [machine]);
|
|
4191
|
+
const cancelMessage = react.useCallback(() => machine.cancelMessage(), [machine]);
|
|
4192
|
+
const clearMessages = react.useCallback(() => machine.clearMessages(), [machine]);
|
|
4193
|
+
const clearError = react.useCallback(() => machine.clearError(), [machine]);
|
|
4194
|
+
return {
|
|
4195
|
+
messages: state.messages,
|
|
4196
|
+
isLoading: state.isLoading,
|
|
4197
|
+
loadingStatusKey: state.loadingStatusKey,
|
|
4198
|
+
error: state.error,
|
|
4199
|
+
pendingActions: state.pendingActions,
|
|
4200
|
+
escalation: state.escalation,
|
|
4201
|
+
requestHuman,
|
|
4202
|
+
sendMessage,
|
|
4203
|
+
cancelMessage,
|
|
4204
|
+
clearMessages,
|
|
4205
|
+
clearError
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
var DEFAULT_PERMISSIONS2 = {
|
|
4209
|
+
canNavigate: true,
|
|
4210
|
+
canFillForms: true,
|
|
4211
|
+
canInteractUI: true,
|
|
4212
|
+
canSubmitForms: true
|
|
4213
|
+
};
|
|
4214
|
+
function useAppilotsActions(options = {}) {
|
|
4215
|
+
const { client, subscribe, config, emit } = chunkV3TUIIT2_js.useAppilotsContext();
|
|
4216
|
+
const { navigationRef, autoExecute = false } = options;
|
|
4217
|
+
const runnerOptionsRef = react.useRef({
|
|
4218
|
+
permissions: config.permissions ?? DEFAULT_PERMISSIONS2,
|
|
4219
|
+
emit,
|
|
4220
|
+
navigationRef
|
|
4221
|
+
});
|
|
4222
|
+
runnerOptionsRef.current = {
|
|
4223
|
+
permissions: config.permissions ?? DEFAULT_PERMISSIONS2,
|
|
4224
|
+
emit,
|
|
4225
|
+
navigationRef
|
|
4226
|
+
};
|
|
4227
|
+
const machine = react.useMemo(
|
|
4228
|
+
() => new chunkV3TUIIT2_js.ActionQueueMachine(
|
|
4229
|
+
{
|
|
4230
|
+
client,
|
|
4231
|
+
adapter: createReactNativeActionRunner(() => runnerOptionsRef.current),
|
|
4232
|
+
emit,
|
|
4233
|
+
warn: chunkV3TUIIT2_js.appilotsDebugWarn
|
|
4234
|
+
},
|
|
4235
|
+
{ autoExecute }
|
|
4236
|
+
),
|
|
4237
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5574
4238
|
[client, emit]
|
|
5575
4239
|
);
|
|
4240
|
+
machine.setOptions({ autoExecute });
|
|
4241
|
+
const state = react.useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
|
|
4242
|
+
react.useEffect(() => subscribe(machine.handleEvent), [subscribe, machine]);
|
|
4243
|
+
react.useEffect(() => {
|
|
4244
|
+
if (autoExecute) machine.scheduleDrain();
|
|
4245
|
+
}, [autoExecute, machine, state.actions]);
|
|
4246
|
+
const approveAction = react.useCallback(
|
|
4247
|
+
(actionId) => machine.approveAction(actionId),
|
|
4248
|
+
[machine]
|
|
4249
|
+
);
|
|
4250
|
+
const rejectAction = react.useCallback((actionId) => machine.rejectAction(actionId), [machine]);
|
|
5576
4251
|
return {
|
|
5577
|
-
pendingActions: actions.filter((a) => a.status === "pending"),
|
|
5578
|
-
executingActions: actions.filter((a) => a.status === "executing"),
|
|
5579
|
-
completedActions: actions.filter(
|
|
5580
|
-
|
|
4252
|
+
pendingActions: state.actions.filter((a) => a.status === "pending"),
|
|
4253
|
+
executingActions: state.actions.filter((a) => a.status === "executing"),
|
|
4254
|
+
completedActions: state.actions.filter(
|
|
4255
|
+
(a) => a.status === "completed" || a.status === "failed"
|
|
4256
|
+
),
|
|
4257
|
+
approveAction,
|
|
5581
4258
|
rejectAction
|
|
5582
4259
|
};
|
|
5583
4260
|
}
|
|
@@ -5610,7 +4287,7 @@ function shouldShowSuggestedPrompts(args) {
|
|
|
5610
4287
|
|
|
5611
4288
|
// src/hooks/useSuggestedPrompts.ts
|
|
5612
4289
|
function useSuggestedPrompts(options) {
|
|
5613
|
-
const { client } =
|
|
4290
|
+
const { client } = chunkV3TUIIT2_js.useAppilotsContext();
|
|
5614
4291
|
const { enabled, limit = 4 } = options;
|
|
5615
4292
|
const [prompts, setPrompts] = react.useState([]);
|
|
5616
4293
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
@@ -5620,8 +4297,8 @@ function useSuggestedPrompts(options) {
|
|
|
5620
4297
|
if (!enabled) return;
|
|
5621
4298
|
const requestId = ++requestIdRef.current;
|
|
5622
4299
|
let cancelled = false;
|
|
5623
|
-
const screen =
|
|
5624
|
-
const localDev = screen ? (
|
|
4300
|
+
const screen = chunkV3TUIIT2_js.getCurrentScreen() ?? void 0;
|
|
4301
|
+
const localDev = screen ? (chunkV3TUIIT2_js.getScreenMetadata(screen)?.suggestedPrompts ?? []).map((text) => ({
|
|
5625
4302
|
text,
|
|
5626
4303
|
source: "dev-defined"
|
|
5627
4304
|
})) : [];
|
|
@@ -5646,9 +4323,9 @@ function useSuggestedPrompts(options) {
|
|
|
5646
4323
|
}, [client, enabled, limit, refreshTick]);
|
|
5647
4324
|
react.useEffect(() => {
|
|
5648
4325
|
if (!enabled) return;
|
|
5649
|
-
let lastScreen =
|
|
4326
|
+
let lastScreen = chunkV3TUIIT2_js.getCurrentScreen();
|
|
5650
4327
|
const interval = setInterval(() => {
|
|
5651
|
-
const current =
|
|
4328
|
+
const current = chunkV3TUIIT2_js.getCurrentScreen();
|
|
5652
4329
|
if (current !== lastScreen) {
|
|
5653
4330
|
lastScreen = current;
|
|
5654
4331
|
setRefreshTick((t) => t + 1);
|
|
@@ -5663,14 +4340,14 @@ function useSuggestedPrompts(options) {
|
|
|
5663
4340
|
};
|
|
5664
4341
|
}
|
|
5665
4342
|
function useAppilotsNavigation() {
|
|
5666
|
-
const { emit } =
|
|
4343
|
+
const { emit } = chunkV3TUIIT2_js.useAppilotsContext();
|
|
5667
4344
|
const [currentScreen, setCurrentScreenState] = react.useState(null);
|
|
5668
4345
|
const [navigationHistory, setNavigationHistory] = react.useState([]);
|
|
5669
4346
|
const navigationRef = react.useRef(null);
|
|
5670
4347
|
const setCurrentScreen2 = react.useCallback(
|
|
5671
4348
|
(screenName) => {
|
|
5672
4349
|
setCurrentScreenState(screenName);
|
|
5673
|
-
|
|
4350
|
+
chunkV3TUIIT2_js.setCurrentScreen(screenName);
|
|
5674
4351
|
setNavigationHistory((prev) => [...prev, screenName]);
|
|
5675
4352
|
emit({
|
|
5676
4353
|
type: "navigation:change",
|
|
@@ -5701,7 +4378,7 @@ function useAppilotsNavigation() {
|
|
|
5701
4378
|
|
|
5702
4379
|
// src/hooks/useAppilots.ts
|
|
5703
4380
|
function useAppilots() {
|
|
5704
|
-
const { config, client } =
|
|
4381
|
+
const { config, client } = chunkV3TUIIT2_js.useAppilotsContext();
|
|
5705
4382
|
const chat = useAppilotsChat();
|
|
5706
4383
|
const navigation = useAppilotsNavigation();
|
|
5707
4384
|
const actions = useAppilotsActions();
|
|
@@ -5730,7 +4407,7 @@ function useAppilots() {
|
|
|
5730
4407
|
}
|
|
5731
4408
|
function useAppilotsField(id, options) {
|
|
5732
4409
|
const { value, onChangeText, ref, fieldType, label, screen } = options;
|
|
5733
|
-
const registry =
|
|
4410
|
+
const registry = chunkV3TUIIT2_js.useResolvedRegistry();
|
|
5734
4411
|
const valueRef = react.useRef(value);
|
|
5735
4412
|
const onChangeRef = react.useRef(onChangeText);
|
|
5736
4413
|
valueRef.current = value;
|
|
@@ -5758,7 +4435,7 @@ function useAppilotsField(id, options) {
|
|
|
5758
4435
|
}
|
|
5759
4436
|
function useAppilotsTarget(id, options) {
|
|
5760
4437
|
const { onPress, onLongPress, onScrollTo, label, screen } = options;
|
|
5761
|
-
const registry =
|
|
4438
|
+
const registry = chunkV3TUIIT2_js.useResolvedRegistry();
|
|
5762
4439
|
const onPressRef = react.useRef(onPress);
|
|
5763
4440
|
const onLongPressRef = react.useRef(onLongPress);
|
|
5764
4441
|
const onScrollToRef = react.useRef(onScrollTo);
|
|
@@ -5785,7 +4462,7 @@ function useAppilotsTarget(id, options) {
|
|
|
5785
4462
|
}
|
|
5786
4463
|
function useAppilotsToggle(id, options) {
|
|
5787
4464
|
const { value, onValueChange, label, screen } = options;
|
|
5788
|
-
const registry =
|
|
4465
|
+
const registry = chunkV3TUIIT2_js.useResolvedRegistry();
|
|
5789
4466
|
const valueRef = react.useRef(value);
|
|
5790
4467
|
const onChangeRef = react.useRef(onValueChange);
|
|
5791
4468
|
valueRef.current = value;
|
|
@@ -5808,7 +4485,7 @@ function useAppilotsToggle(id, options) {
|
|
|
5808
4485
|
}
|
|
5809
4486
|
function useAppilotsSlider(id, options) {
|
|
5810
4487
|
const { value, onValueChange, min, max, step, label, screen } = options;
|
|
5811
|
-
const registry =
|
|
4488
|
+
const registry = chunkV3TUIIT2_js.useResolvedRegistry();
|
|
5812
4489
|
const valueRef = react.useRef(value);
|
|
5813
4490
|
const onChangeRef = react.useRef(onValueChange);
|
|
5814
4491
|
valueRef.current = value;
|