@enricai/barnacle 1.9.12 → 1.10.1

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.
@@ -5572,6 +5572,114 @@ async function executeStepWithHealing(params) {
5572
5572
  // observable effect (see classifyPhantomClick). Reroutes attempt 2 to
5573
5573
  // deep-submit-locator instead of observe-act, since re-resolving via the
5574
5574
  // same light-DOM view cannot reach a target the resolver can't see.
5575
+ // Shared deepLocator actionable-candidate walk, invoked by BOTH the
5576
+ // observe-blind attempt-2/4 branch and the phantom-driven `trusted-click-retry`
5577
+ // branch. Walks ranked `candidates`, skipping wizard-exit and not-actionable
5578
+ // ones, actuating the first that sticks (`clickFirstActionableCandidate`), then
5579
+ // synthesizes a `resolvedAction` the standard verifier consumes. Returns
5580
+ // `resolvedAction` on success, or `null` on failure (the caller `continue`s).
5581
+ // `preferTrustedClick` forwards to `clickDeepLocatorCandidate` so the retry
5582
+ // path activates via the trusted CDP click; `labelPrefix` distinguishes the
5583
+ // two callers in the audit record. On failure the running `triedSelectors`
5584
+ // (which already holds prior attempts' resolved xpath) is written to
5585
+ // `record.triedSelectors`, not just this walk's own — so a walk that resolved
5586
+ // only deny-listed candidates still exposes a prior xpath to
5587
+ // `shouldSkipTechnique`'s `anyXpathResolved` gate, keeping `structured-click`
5588
+ // reachable at attempt 3.
5589
+ const runDeepLocatorClickWalk = async (args) => {
5590
+ const { candidates, innerSelector, preferTrustedClick, labelPrefix, record, attempt, pre } = args;
5591
+ const attemptTriedSelectors = [];
5592
+ const denyCandidate = (candidate) => {
5593
+ const denied = isWizardExitAction(candidate.accessibleText, wizardExitButtonLabels);
5594
+ if (denied) {
5595
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: refused wizard-exit control: "${candidate.accessibleText.slice(0, 60)}"`);
5596
+ }
5597
+ return denied;
5598
+ };
5599
+ const actuation = resolveDeepLocatorActuation(step);
5600
+ const isSelectionIntentClick = actuation.kind === "click" && parseSelectStep(step) !== null;
5601
+ const baselineSelectionCount = isSelectionIntentClick
5602
+ ? selectionCountFromSignature(pre.visibleTextSignature)
5603
+ : null;
5604
+ const readSelectionCount = isSelectionIntentClick
5605
+ ? async () => {
5606
+ try {
5607
+ const snap = await snapshotPage(frameTarget ?? (0, frame_target_1.mainFrameTarget)(page), signalCounter, page);
5608
+ return selectionCountFromSignature(snap.visibleTextSignature);
5609
+ }
5610
+ catch {
5611
+ return null;
5612
+ }
5613
+ }
5614
+ : undefined;
5615
+ const cascadeResult = await (0, deep_locator_click_1.clickFirstActionableCandidate)(candidates, async (candidate) => {
5616
+ attemptTriedSelectors.push(candidate.selector);
5617
+ if (actuation.kind === "select") {
5618
+ const verified = await (0, deep_locator_actuate_1.selectDeepLocatorCandidateOption)(page, frameTarget?.frameSelector, innerSelector, candidate.index, actuation.value, { frameTarget });
5619
+ // selectDeepLocatorCandidateOption never throws on an ordinary failed
5620
+ // write (see deep-locator-actuate.ts's writeAndVerify) — it resolves
5621
+ // `false` for both a rejected selectOption() and a read-back mismatch.
5622
+ // clickFirstActionableCandidate infers success from "didn't throw", so
5623
+ // a `false` here must become a throw or the walk would wrongly report
5624
+ // this candidate as actuated.
5625
+ if (!verified)
5626
+ throw new Error("-32000 Node does not have a layout object");
5627
+ }
5628
+ else if (actuation.kind === "fill") {
5629
+ const verified = await (0, deep_locator_actuate_1.fillDeepLocatorCandidate)(page, frameTarget?.frameSelector, innerSelector, candidate.index, actuation.value, { frameTarget });
5630
+ if (!verified)
5631
+ throw new Error("-32000 Node does not have a layout object");
5632
+ }
5633
+ else {
5634
+ await (0, deep_locator_candidates_1.clickDeepLocatorCandidate)(page, frameTarget?.frameSelector, innerSelector, candidate.index, {
5635
+ frameTarget,
5636
+ preferTrustedClick,
5637
+ });
5638
+ }
5639
+ }, { denyCandidate, readSelectionCount, baselineSelectionCount })
5640
+ .then((outcome) => ({ outcome, error: null }))
5641
+ .catch((error) => ({ outcome: null, error }));
5642
+ triedSelectors.push(...attemptTriedSelectors);
5643
+ if (cascadeResult.outcome?.clicked && cascadeResult.outcome.candidate) {
5644
+ const acted = cascadeResult.outcome.candidate;
5645
+ record.triedSelectors = [...attemptTriedSelectors];
5646
+ const verb = actuation.kind === "select" ? "selected" : actuation.kind === "fill" ? "filled" : "clicked";
5647
+ record.instruction = `${labelPrefix}: ${acted.accessibleText || "(no accessible text)"}`;
5648
+ record.actResultSuccess = true;
5649
+ record.actResultDescription = `${labelPrefix} ${verb} "${acted.accessibleText || acted.selector}"`;
5650
+ // Synthesize an action matching the actuation kind so downstream
5651
+ // verification (network/url/dom — STATE_CLASS_METHODS treats
5652
+ // fill/selectOption as DOM-verifiable, not click) treats this exactly like
5653
+ // any other resolved action — same idiom as deep-submit-locator/structured-click.
5654
+ const resolvedAction = actuation.kind === "click"
5655
+ ? { selector: acted.selector, description: record.actResultDescription, method: "click" }
5656
+ : {
5657
+ selector: acted.selector,
5658
+ description: record.actResultDescription,
5659
+ method: actuation.kind === "select" ? "selectOption" : "fill",
5660
+ arguments: [actuation.value],
5661
+ };
5662
+ return { resolvedAction };
5663
+ }
5664
+ // Failure: carry the running triedSelectors (prior xpath + this walk's own)
5665
+ // into the record so anyXpathResolved stays true even when every resolved
5666
+ // candidate was deny-listed (attemptTriedSelectors would be empty then).
5667
+ record.triedSelectors = [...triedSelectors];
5668
+ const counterStalledCount = cascadeResult.outcome?.counterStalledSelectors.length ?? 0;
5669
+ const failureMessage = cascadeResult.error
5670
+ ? `${labelPrefix}: ${actuation.kind} threw ${(0, errors_1.toErrorMessage)(cascadeResult.error)}`
5671
+ : counterStalledCount > 0
5672
+ ? `${labelPrefix}: clicked ${counterStalledCount} candidate(s) but the "N selected" counter never rose (no selection registered)`
5673
+ : attemptTriedSelectors.length > 0
5674
+ ? `${labelPrefix}: no actionable candidate (${attemptTriedSelectors.length} not-actionable)`
5675
+ : `${labelPrefix}: no actionable candidate (every candidate refused by the wizard-exit deny-list)`;
5676
+ record.actResultSuccess = false;
5677
+ record.errorMessage = failureMessage;
5678
+ attempts.push(record);
5679
+ failureReasons.push(failureMessage);
5680
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: ${failureMessage}`);
5681
+ return { resolvedAction: null };
5682
+ };
5575
5683
  let phantomClickAfterAttempt1 = false;
5576
5684
  for (let attempt = 1; attempt <= MAX_STEP_ATTEMPTS; attempt++) {
5577
5685
  // Telemetry-driven technique-skip: when a cascade technique's
@@ -5583,7 +5691,9 @@ async function executeStepWithHealing(params) {
5583
5691
  const wouldBeTechnique = attempt === 2
5584
5692
  ? phantomClickAfterAttempt1 && (isFinalStep || submitStep)
5585
5693
  ? "deep-submit-locator"
5586
- : "observe-act"
5694
+ : phantomClickAfterAttempt1
5695
+ ? "trusted-click-retry"
5696
+ : "observe-act"
5587
5697
  : attempt === 3
5588
5698
  ? "structured-click"
5589
5699
  : attempt === 4
@@ -5842,6 +5952,113 @@ async function executeStepWithHealing(params) {
5842
5952
  }
5843
5953
  }
5844
5954
  }
5955
+ else if (attempt === 2 && phantomClickAfterAttempt1 && !(isFinalStep || submitStep)) {
5956
+ // Trusted-click retry: attempt 1 phantom-clicked a NON-submit control —
5957
+ // Stagehand reported success but pre/post showed zero effect. On a
5958
+ // design-system widget (React synthetic-event delegation, custom
5959
+ // event-bound wrappers) the likely cause is that the attempt-1 click was
5960
+ // an in-page, `isTrusted=false` activation the handler ignores — the
5961
+ // element resolves fine, but only a REAL user gesture registers. So
5962
+ // re-click the target with a TRUSTED gesture. Two arms by page shape:
5963
+ // a top-window (no frame seam) page re-clicks attempt-1's resolved xpath
5964
+ // via a Playwright `.locator().first().click()` on the main frame; an
5965
+ // OOPIF/cross-origin page re-resolves via the frame-seam deepLocator and
5966
+ // clicks through `clickDeepLocatorCandidate` → `deepLocator().nth().click()`
5967
+ // (an `Input.dispatchMouseEvent`). Both deliver `isTrusted=true`. This is
5968
+ // the non-submit sibling of the deep-submit-locator escalation above.
5969
+ record.technique = "trusted-click-retry";
5970
+ // Carry forward any selector prior attempts already resolved (e.g.
5971
+ // attempt-1 act-string's xpath) so a trusted-click-retry that can't
5972
+ // resolve a deepLocator candidate doesn't erase the xpath signal
5973
+ // structured-click's precondition (shouldSkipTechnique) depends on —
5974
+ // this attempt is an ADDITIONAL recovery, not a replacement that
5975
+ // demotes the rest of the ladder.
5976
+ if (!frameTarget?.frame) {
5977
+ // Top-window path: this wizard renders in the top document with no
5978
+ // cross-origin OOPIF, so `deepLocator` (which is built entirely on a
5979
+ // frame seam — deep-locator-candidates.ts's resolveScanFrameTarget
5980
+ // returns null when no frameSelector resolves) yields nothing. The
5981
+ // trusted-click primitive `deepLocator().nth().click()` is therefore
5982
+ // unreachable, but the ACTIVATION we need — a real `isTrusted=true`
5983
+ // gesture the Base Web handler honours — is still deliverable via a
5984
+ // Playwright Locator click on the top frame (the same trusted click
5985
+ // applyRadioSelection uses at Tier A). Re-click attempt-1's resolved
5986
+ // xpath through the main-frame FrameTarget's `.locator()` instead of
5987
+ // bailing; the synthesized click action flows through the standard
5988
+ // verifier exactly like the frame-seam path's result.
5989
+ //
5990
+ // FIRST xpath, not last: attempt-1's act pushes every resolved
5991
+ // action's selector into `triedSelectors` in order but binds the
5992
+ // phantom-classified `resolvedAction` to the FIRST (see the
5993
+ // `if (!resolvedAction)` at the attempt-1 branch). On a multi-action
5994
+ // attempt-1 the last entry is a different control than the one that
5995
+ // was clicked, so the first match is the phantomed target.
5996
+ const topWindowSelector = triedSelectors.find((sel) => xpathBodyForEvaluate(sel) !== null);
5997
+ if (!topWindowSelector) {
5998
+ const failureMessage = "trusted-click-retry: no top-window selector resolved for the phantomed target";
5999
+ record.actResultSuccess = false;
6000
+ record.errorMessage = failureMessage;
6001
+ record.triedSelectors = [...triedSelectors];
6002
+ attempts.push(record);
6003
+ failureReasons.push(failureMessage);
6004
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: ${failureMessage}`);
6005
+ continue;
6006
+ }
6007
+ const topWindowTarget = frameTarget ?? (0, frame_target_1.mainFrameTarget)(page);
6008
+ try {
6009
+ await topWindowTarget.locator(topWindowSelector).first().click();
6010
+ }
6011
+ catch (err) {
6012
+ const failureMessage = `trusted-click-retry: top-window trusted click threw ${(0, errors_1.toErrorMessage)(err)}`;
6013
+ record.actResultSuccess = false;
6014
+ record.errorMessage = failureMessage;
6015
+ record.triedSelectors = [...triedSelectors];
6016
+ attempts.push(record);
6017
+ failureReasons.push(failureMessage);
6018
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: ${failureMessage}`);
6019
+ continue;
6020
+ }
6021
+ record.instruction = `trusted-click-retry (top-window): ${topWindowSelector}`;
6022
+ record.actResultSuccess = true;
6023
+ record.actResultDescription = `trusted-click-retry clicked "${topWindowSelector}" via top-window locator`;
6024
+ record.triedSelectors = [...triedSelectors];
6025
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: trusted-click-retry: top-window trusted locator click on the resolved target`);
6026
+ resolvedAction = {
6027
+ selector: topWindowSelector,
6028
+ description: record.actResultDescription,
6029
+ method: "click",
6030
+ };
6031
+ }
6032
+ else {
6033
+ await reresolveFrameTargetIfLost();
6034
+ const { candidates: retryCandidates, innerSelector: retryInnerSelector } = await resolveDeepLocatorCandidatesWithWidening(page, frameTarget.frameSelector, step, {
6035
+ frameTarget,
6036
+ });
6037
+ const deepLocatorCandidates = retryCandidates.filter((c) => !triedSelectors.includes(c.selector));
6038
+ if (deepLocatorCandidates.length === 0) {
6039
+ const failureMessage = "trusted-click-retry: no deepLocator candidate resolved for the phantomed target";
6040
+ record.actResultSuccess = false;
6041
+ record.errorMessage = failureMessage;
6042
+ record.triedSelectors = [...triedSelectors];
6043
+ attempts.push(record);
6044
+ failureReasons.push(failureMessage);
6045
+ logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: ${failureMessage}`);
6046
+ continue;
6047
+ }
6048
+ const { resolvedAction: retryResolvedAction } = await runDeepLocatorClickWalk({
6049
+ candidates: deepLocatorCandidates,
6050
+ innerSelector: retryInnerSelector,
6051
+ preferTrustedClick: true,
6052
+ labelPrefix: "trusted-click-retry",
6053
+ record,
6054
+ attempt,
6055
+ pre,
6056
+ });
6057
+ if (!retryResolvedAction)
6058
+ continue;
6059
+ resolvedAction = retryResolvedAction;
6060
+ }
6061
+ }
5845
6062
  else if (attempt === 2 || attempt === 4) {
5846
6063
  record.technique = attempt === 2 ? "observe-act" : "observe-act-exclude";
5847
6064
  const observeOptions = attempt === 4 && triedSelectors.length > 0
@@ -5929,128 +6146,26 @@ async function executeStepWithHealing(params) {
5929
6146
  continue;
5930
6147
  }
5931
6148
  }
5932
- // Actionable-candidate walk: a top pick that rejects with the CDP
5933
- // `-32000 Node does not have a layout object` error (an unrendered
5934
- // node) or is refused by the wizard-exit deny-list costs only that
5935
- // one candidate, not the whole attempt the next ranked candidate
5936
- // is tried instead. `attemptTriedSelectors` parallels the walk's own
5937
- // click attempts as they happen (not just on a successful return)
5938
- // so a click that throws a REAL error (e.g. a wedged
5939
- // `WatchdogTimeoutError`) still feeds attempt 4's exclusion filter.
5940
- const attemptTriedSelectors = [];
5941
- const denyCandidate = (candidate) => {
5942
- const denied = isWizardExitAction(candidate.accessibleText, wizardExitButtonLabels);
5943
- if (denied) {
5944
- logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: refused wizard-exit control: "${candidate.accessibleText.slice(0, 60)}"`);
5945
- }
5946
- return denied;
5947
- };
5948
- // Intent discrimination: observe()'s Stagehand-resolved action
5949
- // carries its own method + fill/select arguments (see attempt 1's
5950
- // `target.method === "fill"` handling above); the deepLocator
5951
- // fallback has no such resolved action, so the step prose is the
5952
- // only place the fill/select value can come from. Derived once per
5953
- // attempt (not per candidate) — every candidate in this walk is a
5954
- // guess at the SAME step's target, so they all actuate the same way.
5955
- const actuation = resolveDeepLocatorActuation(step);
5956
- // Next-best-candidate recovery for a markerless multi-select phantom:
5957
- // a click-to-toggle option (not a native <select> write, which
5958
- // already read-back-verifies) whose "N selected" counter doesn't rise
5959
- // means the click landed but didn't register — so the walk should try
5960
- // the next real option before this attempt is scored a failure and a
5961
- // replan is spent. Only wired for a selection-intent CLICK step; the
5962
- // reader re-reads the same counter `pre` captured (via
5963
- // `selectionCountFromSignature`) so no extra pre-snapshot is paid, and
5964
- // returns `null` on any frame hiccup so a read failure never converts
5965
- // to a walk-stopping throw. Non-selection steps pass `undefined` and
5966
- // the walk behaves exactly as before.
5967
- const isSelectionIntentClick = actuation.kind === "click" && parseSelectStep(step) !== null;
5968
- const baselineSelectionCount = isSelectionIntentClick
5969
- ? selectionCountFromSignature(pre.visibleTextSignature)
5970
- : null;
5971
- const readSelectionCount = isSelectionIntentClick
5972
- ? async () => {
5973
- try {
5974
- const snap = await snapshotPage(frameTarget ?? (0, frame_target_1.mainFrameTarget)(page), signalCounter, page);
5975
- return selectionCountFromSignature(snap.visibleTextSignature);
5976
- }
5977
- catch {
5978
- return null;
5979
- }
5980
- }
5981
- : undefined;
5982
- const cascadeResult = await (0, deep_locator_click_1.clickFirstActionableCandidate)(deepLocatorCandidates, async (candidate) => {
5983
- attemptTriedSelectors.push(candidate.selector);
5984
- if (actuation.kind === "select") {
5985
- const verified = await (0, deep_locator_actuate_1.selectDeepLocatorCandidateOption)(page, frameTarget?.frameSelector, deepLocatorInnerSelector, candidate.index, actuation.value, { frameTarget });
5986
- // selectDeepLocatorCandidateOption never throws on an ordinary
5987
- // failed write (see deep-locator-actuate.ts's writeAndVerify) —
5988
- // it resolves `false` for both a rejected selectOption() and a
5989
- // read-back mismatch. clickFirstActionableCandidate infers
5990
- // success from "didn't throw", so a `false` here must become a
5991
- // throw or the walk would wrongly report this candidate as
5992
- // actuated.
5993
- if (!verified)
5994
- throw new Error("-32000 Node does not have a layout object");
5995
- }
5996
- else if (actuation.kind === "fill") {
5997
- const verified = await (0, deep_locator_actuate_1.fillDeepLocatorCandidate)(page, frameTarget?.frameSelector, deepLocatorInnerSelector, candidate.index, actuation.value, { frameTarget });
5998
- if (!verified)
5999
- throw new Error("-32000 Node does not have a layout object");
6000
- }
6001
- else {
6002
- await (0, deep_locator_candidates_1.clickDeepLocatorCandidate)(page, frameTarget?.frameSelector, deepLocatorInnerSelector, candidate.index, { frameTarget });
6003
- }
6004
- }, { denyCandidate, readSelectionCount, baselineSelectionCount })
6005
- .then((outcome) => ({ outcome, error: null }))
6006
- .catch((error) => ({ outcome: null, error }));
6007
- triedSelectors.push(...attemptTriedSelectors);
6008
- record.triedSelectors = [...attemptTriedSelectors];
6009
- if (cascadeResult.outcome?.clicked && cascadeResult.outcome.candidate) {
6010
- const acted = cascadeResult.outcome.candidate;
6011
- const verb = actuation.kind === "select"
6012
- ? "selected"
6013
- : actuation.kind === "fill"
6014
- ? "filled"
6015
- : "clicked";
6016
- record.instruction = `deepLocator: ${acted.accessibleText || "(no accessible text)"}`;
6017
- record.actResultSuccess = true;
6018
- record.actResultDescription = `deepLocator ${verb} "${acted.accessibleText || acted.selector}"`;
6019
- // Synthesize an action matching the actuation kind so downstream
6020
- // verification (network/url/dom — STATE_CLASS_METHODS treats
6021
- // fill/selectOption as DOM-verifiable, not click) treats this
6022
- // exactly like any other resolved action — same idiom as
6023
- // deep-submit-locator/structured-click.
6024
- resolvedAction =
6025
- actuation.kind === "click"
6026
- ? {
6027
- selector: acted.selector,
6028
- description: record.actResultDescription,
6029
- method: "click",
6030
- }
6031
- : {
6032
- selector: acted.selector,
6033
- description: record.actResultDescription,
6034
- method: actuation.kind === "select" ? "selectOption" : "fill",
6035
- arguments: [actuation.value],
6036
- };
6037
- }
6038
- else {
6039
- const counterStalledCount = cascadeResult.outcome?.counterStalledSelectors.length ?? 0;
6040
- const failureMessage = cascadeResult.error
6041
- ? `deepLocator: ${actuation.kind} threw ${(0, errors_1.toErrorMessage)(cascadeResult.error)}`
6042
- : counterStalledCount > 0
6043
- ? `deepLocator: clicked ${counterStalledCount} candidate(s) but the "N selected" counter never rose (no selection registered)`
6044
- : attemptTriedSelectors.length > 0
6045
- ? `deepLocator: no actionable candidate (${attemptTriedSelectors.length} not-actionable)`
6046
- : "deepLocator: no actionable candidate (every candidate refused by the wizard-exit deny-list)";
6047
- record.actResultSuccess = false;
6048
- record.errorMessage = failureMessage;
6049
- attempts.push(record);
6050
- failureReasons.push(failureMessage);
6051
- logger.info(`${formatStepPrefix(stepIndex, totalSteps)} attempt ${attempt}: ${failureMessage}`);
6149
+ // Actionable-candidate walk (shared with the trusted-click-retry
6150
+ // branch): a top pick that rejects with the CDP `-32000 Node does not
6151
+ // have a layout object` error (an unrendered node) or is refused by
6152
+ // the wizard-exit deny-list costs only that one candidate, not the
6153
+ // whole attempt — the next ranked candidate is tried instead. The
6154
+ // observe-act path activates via the synthetic fast click
6155
+ // (preferTrustedClick: false); the trusted CDP click is the
6156
+ // phantom-retry escalation.
6157
+ const { resolvedAction: walkResolvedAction } = await runDeepLocatorClickWalk({
6158
+ candidates: deepLocatorCandidates,
6159
+ innerSelector: deepLocatorInnerSelector,
6160
+ preferTrustedClick: false,
6161
+ labelPrefix: "deepLocator",
6162
+ record,
6163
+ attempt,
6164
+ pre,
6165
+ });
6166
+ if (!walkResolvedAction)
6052
6167
  continue;
6053
- }
6168
+ resolvedAction = walkResolvedAction;
6054
6169
  }
6055
6170
  else if (candidates.length === 0) {
6056
6171
  record.errorMessage = "observe returned no candidates";
@@ -7149,7 +7264,7 @@ async function executeStepWithHealing(params) {
7149
7264
  const suppressedCount = getSuppressedAisdkElementIdErrorCount?.();
7150
7265
  const escalationTarget = isFinalStep || submitStep
7151
7266
  ? "escalating attempt 2 to deep-submit-locator"
7152
- : "non-submit step — leaving the normal structured-click/observe-act-exclude ladder intact";
7267
+ : "non-submit step — escalating attempt 2 to trusted-click-retry (trusted CDP click on the resolved target)";
7153
7268
  logger.warn(`${formatStepPrefix(stepIndex, totalSteps)} phantom click detected on attempt 1 (${record.technique}): reported success with no network/url/dom change${suppressedCount !== undefined ? `; ${suppressedCount} AISDK elementId errors suppressed this session (corroborating, not causal)` : ""} — ${escalationTarget}`);
7154
7269
  }
7155
7270
  const postAttemptInvalidCount = await countNgInvalidContainers(frameTarget ?? (0, frame_target_1.mainFrameTarget)(page));