@enricai/barnacle 1.12.78 → 1.12.80
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/api/helpers/reply.d.ts.map +1 -1
- package/dist/api/helpers/reply.js +5 -2
- package/dist/api/helpers/reply.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/lib/llm/judge.d.ts.map +1 -1
- package/dist/lib/llm/judge.js +6 -1
- package/dist/lib/llm/judge.js.map +1 -1
- package/dist/lib/llm/judges/verify-step-advanced.d.ts +30 -0
- package/dist/lib/llm/judges/verify-step-advanced.d.ts.map +1 -0
- package/dist/lib/llm/judges/verify-step-advanced.js +62 -0
- package/dist/lib/llm/judges/verify-step-advanced.js.map +1 -0
- package/dist/lib/llm/judges/verify-submit.d.ts +7 -1
- package/dist/lib/llm/judges/verify-submit.d.ts.map +1 -1
- package/dist/lib/llm/judges/verify-submit.js +29 -8
- package/dist/lib/llm/judges/verify-submit.js.map +1 -1
- package/dist/lib/llm/schemas.d.ts +8 -0
- package/dist/lib/llm/schemas.d.ts.map +1 -1
- package/dist/lib/llm/schemas.js +21 -13
- package/dist/lib/llm/schemas.js.map +1 -1
- package/dist/lib/logging.d.ts +7 -0
- package/dist/lib/logging.d.ts.map +1 -1
- package/dist/lib/logging.js +19 -1
- package/dist/lib/logging.js.map +1 -1
- package/dist/lib/pii-redaction.d.ts +104 -0
- package/dist/lib/pii-redaction.d.ts.map +1 -0
- package/dist/lib/pii-redaction.js +397 -0
- package/dist/lib/pii-redaction.js.map +1 -0
- package/dist/lib/random.d.ts +3 -1
- package/dist/lib/random.d.ts.map +1 -1
- package/dist/lib/random.js +7 -2
- package/dist/lib/random.js.map +1 -1
- package/dist/lib/telemetry/beacon-capture.d.ts.map +1 -1
- package/dist/lib/telemetry/beacon-capture.js +2 -0
- package/dist/lib/telemetry/beacon-capture.js.map +1 -1
- package/dist/lib/telemetry/call-capture.d.ts.map +1 -1
- package/dist/lib/telemetry/call-capture.js +4 -1
- package/dist/lib/telemetry/call-capture.js.map +1 -1
- package/dist/lib/telemetry/submission-capture.d.ts +11 -1
- package/dist/lib/telemetry/submission-capture.d.ts.map +1 -1
- package/dist/lib/telemetry/submission-capture.js +32 -3
- package/dist/lib/telemetry/submission-capture.js.map +1 -1
- package/dist/plugins/config-plugin.d.ts +8 -0
- package/dist/plugins/config-plugin.d.ts.map +1 -1
- package/dist/plugins/config-plugin.js +6 -1
- package/dist/plugins/config-plugin.js.map +1 -1
- package/dist/plugins/discover.d.ts.map +1 -1
- package/dist/plugins/discover.js +2 -0
- package/dist/plugins/discover.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +23 -6
- package/dist/plugins/loader.js.map +1 -1
- package/dist/scraper/actionable-visibility-expr.d.ts +12 -0
- package/dist/scraper/actionable-visibility-expr.d.ts.map +1 -0
- package/dist/scraper/actionable-visibility-expr.js +22 -0
- package/dist/scraper/actionable-visibility-expr.js.map +1 -0
- package/dist/scraper/active-page.d.ts +18 -0
- package/dist/scraper/active-page.d.ts.map +1 -0
- package/dist/scraper/active-page.js +53 -0
- package/dist/scraper/active-page.js.map +1 -0
- package/dist/scraper/browser-click-expr.d.ts +1 -1
- package/dist/scraper/browser-click-expr.d.ts.map +1 -1
- package/dist/scraper/browser-click-expr.js +2 -6
- package/dist/scraper/browser-click-expr.js.map +1 -1
- package/dist/scraper/click-settle.d.ts +19 -0
- package/dist/scraper/click-settle.d.ts.map +1 -0
- package/dist/scraper/click-settle.js +65 -0
- package/dist/scraper/click-settle.js.map +1 -0
- package/dist/scraper/deep-locator-candidates.d.ts +20 -1
- package/dist/scraper/deep-locator-candidates.d.ts.map +1 -1
- package/dist/scraper/deep-locator-candidates.js +110 -22
- package/dist/scraper/deep-locator-candidates.js.map +1 -1
- package/dist/scraper/deep-locator-scan.d.ts +12 -0
- package/dist/scraper/deep-locator-scan.d.ts.map +1 -1
- package/dist/scraper/deep-locator-scan.js +42 -13
- package/dist/scraper/deep-locator-scan.js.map +1 -1
- package/dist/scraper/errors.d.ts +10 -0
- package/dist/scraper/errors.d.ts.map +1 -1
- package/dist/scraper/errors.js +19 -1
- package/dist/scraper/errors.js.map +1 -1
- package/dist/scraper/flow-runner.d.ts +355 -6
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +2255 -350
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/navigate.d.ts +3 -3
- package/dist/scraper/navigate.js +4 -4
- package/dist/scraper/navigate.js.map +1 -1
- package/dist/scraper/phantom-click.d.ts +34 -2
- package/dist/scraper/phantom-click.d.ts.map +1 -1
- package/dist/scraper/phantom-click.js +80 -6
- package/dist/scraper/phantom-click.js.map +1 -1
- package/dist/scraper/pool.d.ts.map +1 -1
- package/dist/scraper/pool.js +8 -1
- package/dist/scraper/pool.js.map +1 -1
- package/dist/scraper/quoted-spans.d.ts +35 -0
- package/dist/scraper/quoted-spans.d.ts.map +1 -0
- package/dist/scraper/quoted-spans.js +83 -0
- package/dist/scraper/quoted-spans.js.map +1 -0
- package/dist/scraper/retry.d.ts +1 -1
- package/dist/scraper/retry.d.ts.map +1 -1
- package/dist/scraper/retry.js +5 -1
- package/dist/scraper/retry.js.map +1 -1
- package/dist/scraper/session-browserbase.d.ts +4 -2
- package/dist/scraper/session-browserbase.d.ts.map +1 -1
- package/dist/scraper/session-browserbase.js +63 -20
- package/dist/scraper/session-browserbase.js.map +1 -1
- package/dist/scraper/session-shared.d.ts +5 -0
- package/dist/scraper/session-shared.d.ts.map +1 -1
- package/dist/scraper/session-shared.js.map +1 -1
- package/dist/scraper/session-steel.d.ts.map +1 -1
- package/dist/scraper/session-steel.js +16 -2
- package/dist/scraper/session-steel.js.map +1 -1
- package/dist/scraper/session-teardown.d.ts +31 -1
- package/dist/scraper/session-teardown.d.ts.map +1 -1
- package/dist/scraper/session-teardown.js +60 -4
- package/dist/scraper/session-teardown.js.map +1 -1
- package/dist/scraper/spa-readiness.d.ts +13 -1
- package/dist/scraper/spa-readiness.d.ts.map +1 -1
- package/dist/scraper/spa-readiness.js +31 -1
- package/dist/scraper/spa-readiness.js.map +1 -1
- package/dist/scraper/stagehand-guard.d.ts +8 -3
- package/dist/scraper/stagehand-guard.d.ts.map +1 -1
- package/dist/scraper/stagehand-guard.js +181 -13
- package/dist/scraper/stagehand-guard.js.map +1 -1
- package/dist/scraper/submit-control.d.ts +2 -2
- package/dist/scraper/submit-control.d.ts.map +1 -1
- package/dist/scraper/submit-control.js +5 -21
- package/dist/scraper/submit-control.js.map +1 -1
- package/dist/scraper/target-resolution-diagnostic.d.ts.map +1 -1
- package/dist/scraper/target-resolution-diagnostic.js +6 -11
- package/dist/scraper/target-resolution-diagnostic.js.map +1 -1
- package/dist/scraper/transient-provider-error.d.ts +19 -0
- package/dist/scraper/transient-provider-error.d.ts.map +1 -0
- package/dist/scraper/transient-provider-error.js +59 -0
- package/dist/scraper/transient-provider-error.js.map +1 -0
- package/dist/scraper/trusted-click.d.ts +122 -0
- package/dist/scraper/trusted-click.d.ts.map +1 -0
- package/dist/scraper/trusted-click.js +201 -0
- package/dist/scraper/trusted-click.js.map +1 -0
- package/dist/scraper/upload-affordance-expr.d.ts +33 -0
- package/dist/scraper/upload-affordance-expr.d.ts.map +1 -0
- package/dist/scraper/upload-affordance-expr.js +91 -0
- package/dist/scraper/upload-affordance-expr.js.map +1 -0
- package/dist/scripts/recon-browser.d.ts +30 -2
- package/dist/scripts/recon-browser.d.ts.map +1 -1
- package/dist/scripts/recon-browser.js +133 -27
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/scripts/recon-capture-rebind.d.ts +11 -0
- package/dist/scripts/recon-capture-rebind.d.ts.map +1 -0
- package/dist/scripts/recon-capture-rebind.js +26 -0
- package/dist/scripts/recon-capture-rebind.js.map +1 -0
- package/dist/scripts/recon-generate.d.ts +159 -8
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +998 -182
- package/dist/scripts/recon-generate.js.map +1 -1
- package/dist/scripts/recon-shared.d.ts +7 -0
- package/dist/scripts/recon-shared.d.ts.map +1 -1
- package/dist/scripts/recon-shared.js +19 -0
- package/dist/scripts/recon-shared.js.map +1 -1
- package/dist/site-plugin.d.ts +8 -0
- package/dist/site-plugin.d.ts.map +1 -1
- package/dist/types/pii-redaction.d.ts +39 -0
- package/dist/types/pii-redaction.d.ts.map +1 -0
- package/dist/types/pii-redaction.js +3 -0
- package/dist/types/pii-redaction.js.map +1 -0
- package/package.json +14 -1
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* per act; healing and replans push the upper bound higher on flaky sites).
|
|
49
49
|
*/
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.writeFixtureToTempFile = exports.windowHasTransitionBody = exports.windowHasAdvanceTransition = exports.waitForTransitionBody = exports.verifyFillReadback = exports.shouldWarnMissingAdvancePattern = exports.shouldVetoFallbackAdvance = exports.shouldSkipTechnique = exports.selectRadioGroupOption = exports.selectBodyExcerpt = exports.resetBillingErrorFlagForTests = exports.rephraseWithLLM = exports.renderUnfocusedObserve = exports.renderLeafInvalidFields = exports.probeStepBeforeAttempts = exports.probeLeafInvalidContainers = exports.pollEnumerate = exports.parseSelectStep = exports.parseRadioStep = exports.parseCaptureIndex = exports.pairInvalidWithErrors = exports.normalizeDateValue = exports.narrowInvalidFormControl = exports.logBillingErrorIfPresent = exports.latestCaptureIndex = exports.isWizardExitAction = exports.isUploadAffordanceLabel = exports.isSubmitRevealedInvalid = exports.isDomOnlyAdvanceVerified = exports.isAdvanceStep = exports.isAdvanceStalled = exports.hasBillingErrorBeenLogged = exports.formatValidationRejectedReason = exports.findRecentPageTransition = exports.findRecentBackendError = exports.fillTextDatepickerInput = exports.fillHtml5DateTimeInput = exports.extractSubmitFailureEvidence = exports.extractGaEventEvidence = exports.describeAttemptEffectSignals = exports.chooseRequiredSelectOption = exports.capturesAfterIndex = exports.buildRadioIdXPath = exports.RECON_FLOW_FILE_SCHEMA = void 0;
|
|
51
|
+
exports.writeFixtureToTempFile = exports.windowHasTransitionBody = exports.windowHasAdvanceTransition = exports.waitForTransitionBody = exports.verifyFillReadback = exports.shouldWarnMissingAdvancePattern = exports.shouldVetoFallbackAdvance = exports.shouldSkipTechnique = exports.selectRadioGroupOption = exports.selectBodyExcerpt = exports.resetBillingErrorFlagForTests = exports.rephraseWithLLM = exports.renderUnfocusedObserve = exports.renderLeafInvalidFields = exports.probeStepBeforeAttempts = exports.probeLeafInvalidContainers = exports.pollEnumerate = exports.parseSelectStep = exports.parseRadioStep = exports.parseCaptureIndex = exports.pairInvalidWithErrors = exports.normalizeDateValue = exports.narrowInvalidFormControl = exports.logBillingErrorIfPresent = exports.latestCaptureIndex = exports.isWizardExitAction = exports.isUploadAffordanceLabel = exports.isSubmitRevealedInvalid = exports.isDomOnlyAdvanceVerified = exports.isAdvanceStep = exports.isAdvanceStalled = exports.hasBillingErrorBeenLogged = exports.formatValidationRejectedReason = exports.findRecentPageTransition = exports.findRecentBackendError = exports.fillTextDatepickerInput = exports.fillHtml5DateTimeInput = exports.extractSubmitFailureEvidence = exports.extractGaEventEvidence = exports.describeAttemptEffectSignals = exports.chooseRequiredSelectOption = exports.capturesAfterIndex = exports.buildRadioIdXPath = exports.MAX_TRANSIENT_STEP_RETRIES = exports.RECON_FLOW_FILE_SCHEMA = void 0;
|
|
52
52
|
exports.resolveGotoWaitUntil = resolveGotoWaitUntil;
|
|
53
53
|
exports.detectRejectionInResponseBody = detectRejectionInResponseBody;
|
|
54
54
|
exports.isFlowTruncated = isFlowTruncated;
|
|
@@ -63,7 +63,9 @@ exports.extractSubmitJudgeRequiredFields = extractSubmitJudgeRequiredFields;
|
|
|
63
63
|
exports.applyFailedStepFlagsToResumingBridgeStep = applyFailedStepFlagsToResumingBridgeStep;
|
|
64
64
|
exports.filterReplanDuplicatingNextAuthored = filterReplanDuplicatingNextAuthored;
|
|
65
65
|
exports.isReplanReproposingFailedStep = isReplanReproposingFailedStep;
|
|
66
|
+
exports.decideReplanNoProgress = decideReplanNoProgress;
|
|
66
67
|
exports.hasPageAlreadyAdvancedPastStep = hasPageAlreadyAdvancedPastStep;
|
|
68
|
+
exports.hasSubmitStepDomAdvanced = hasSubmitStepDomAdvanced;
|
|
67
69
|
exports.isReplanRegressingAcrossAuthBoundary = isReplanRegressingAcrossAuthBoundary;
|
|
68
70
|
exports.isSubmitShapedInstructionText = isSubmitShapedInstructionText;
|
|
69
71
|
exports.seedSubmitStepFromOwnInstructionText = seedSubmitStepFromOwnInstructionText;
|
|
@@ -108,8 +110,10 @@ const session_1 = require("../scraper/session");
|
|
|
108
110
|
const session_teardown_1 = require("../scraper/session-teardown");
|
|
109
111
|
const spa_readiness_1 = require("../scraper/spa-readiness");
|
|
110
112
|
const stagehand_guard_1 = require("../scraper/stagehand-guard");
|
|
113
|
+
const transient_provider_error_1 = require("../scraper/transient-provider-error");
|
|
111
114
|
const watchdog_1 = require("../scraper/watchdog");
|
|
112
115
|
const judge_llm_batch_1 = require("./judge-llm-batch");
|
|
116
|
+
const recon_capture_rebind_1 = require("./recon-capture-rebind");
|
|
113
117
|
const recon_shared_1 = require("./recon-shared");
|
|
114
118
|
const resume_fixture_1 = require("../testing/resume-fixture");
|
|
115
119
|
const client_1 = require("../testmail/client");
|
|
@@ -591,6 +595,11 @@ exports.RECON_FLOW_FILE_SCHEMA = v4_1.z.union([
|
|
|
591
595
|
* "<tenant>.<ats>.com"].
|
|
592
596
|
*/
|
|
593
597
|
ownBackendHostnames: v4_1.z.array(v4_1.z.string().min(1)).optional(),
|
|
598
|
+
/**
|
|
599
|
+
* Declares the ATS cannot be replayed as raw HTTP, so the generator emits
|
|
600
|
+
* a browser-flow-only plugin regardless of HTTP analysis.
|
|
601
|
+
*/
|
|
602
|
+
browserFlowOnly: v4_1.z.boolean().optional(),
|
|
594
603
|
/**
|
|
595
604
|
* Optional site-specific class-name prefixes that wrap form/error
|
|
596
605
|
* state. The Haiku invalid-fields judge uses these as additional
|
|
@@ -1171,6 +1180,28 @@ function isReplanReproposingFailedStep(newSteps, failedStep) {
|
|
|
1171
1180
|
const failedSig = stepSignature(failedStep);
|
|
1172
1181
|
return newSteps.every((s) => stepSignature(s.instruction) === failedSig && !hasCompoundBridgeClause(s.instruction));
|
|
1173
1182
|
}
|
|
1183
|
+
/** Max times one step may be re-run after a provider-only failure cascade. */
|
|
1184
|
+
exports.MAX_TRANSIENT_STEP_RETRIES = 2;
|
|
1185
|
+
/**
|
|
1186
|
+
* Cause-aware wrapper over {@link isReplanReproposingFailedStep}: a re-proposal
|
|
1187
|
+
* of only the failed step is a guaranteed re-fail for a real selector/page
|
|
1188
|
+
* problem, but a legitimate bounded re-run when every recorded attempt failed
|
|
1189
|
+
* on a transient provider error. Retries are counted per step signature in
|
|
1190
|
+
* `retryCounts` (mutated on "retry"); once exhausted, or when the dump is
|
|
1191
|
+
* missing/empty/mixed, the original abort stands.
|
|
1192
|
+
*/
|
|
1193
|
+
function decideReplanNoProgress(newSteps, failedStep, attempts, retryCounts) {
|
|
1194
|
+
if (!isReplanReproposingFailedStep(newSteps, failedStep))
|
|
1195
|
+
return "proceed";
|
|
1196
|
+
if (!attempts || !(0, transient_provider_error_1.summarizeAttemptTransience)(attempts).all)
|
|
1197
|
+
return "abort";
|
|
1198
|
+
const sig = stepSignature(failedStep);
|
|
1199
|
+
const used = retryCounts.get(sig) ?? 0;
|
|
1200
|
+
if (used >= exports.MAX_TRANSIENT_STEP_RETRIES)
|
|
1201
|
+
return "abort";
|
|
1202
|
+
retryCounts.set(sig, used + 1);
|
|
1203
|
+
return "retry";
|
|
1204
|
+
}
|
|
1174
1205
|
/**
|
|
1175
1206
|
* Normalize a URL to origin + pathname (query and hash stripped) for
|
|
1176
1207
|
* same-page-vs-navigated comparison. Returns null on unparseable input so
|
|
@@ -1212,6 +1243,18 @@ function hasPageAlreadyAdvancedPastStep(stepStartUrl, currentUrl, stepInstructio
|
|
|
1212
1243
|
return false;
|
|
1213
1244
|
return (0, phantom_click_1.isPlausibleStepDestination)(stepInstruction, currentUrl);
|
|
1214
1245
|
}
|
|
1246
|
+
/**
|
|
1247
|
+
* DOM-advance counterpart of {@link hasPageAlreadyAdvancedPastStep} for
|
|
1248
|
+
* non-final submit-shaped steps, whose SPA wizards swap content without a URL
|
|
1249
|
+
* change. Reuses the shared {@link hasPageAdvanced} judgement over the
|
|
1250
|
+
* step-start and live visible-text signatures; a final step is never credited
|
|
1251
|
+
* here because its completion is owned by the submit-destination judge.
|
|
1252
|
+
*/
|
|
1253
|
+
function hasSubmitStepDomAdvanced(isFinalStep, preVisibleTextSignature, liveVisibleTextSignature) {
|
|
1254
|
+
if (isFinalStep)
|
|
1255
|
+
return false;
|
|
1256
|
+
return (0, phantom_click_1.hasPageAdvanced)({ networkCount: 0, url: "", bodyHtmlLength: 0, visibleTextSignature: preVisibleTextSignature }, { networkCount: 0, url: "", bodyHtmlLength: 0, visibleTextSignature: liveVisibleTextSignature });
|
|
1257
|
+
}
|
|
1215
1258
|
/** Word-boundary phrase patterns identifying an account-creation/registration step. */
|
|
1216
1259
|
const ACCOUNT_CREATION_PATTERNS = [
|
|
1217
1260
|
/\bcreate\s+(an\s+)?account\b/,
|
|
@@ -1234,13 +1277,13 @@ const ACCOUNT_CREATION_PATTERNS = [
|
|
|
1234
1277
|
* in". Scopes the Sign-In match to the step's own action clause (via
|
|
1235
1278
|
* `splitIntoActionClauses`, shared with `isPlausibleStepDestination`) so a
|
|
1236
1279
|
* step that merely mentions sign-in in descriptive context isn't mistaken
|
|
1237
|
-
* for one that is itself about signing in.
|
|
1280
|
+
* for one that is itself about signing in. The completed-steps side is
|
|
1281
|
+
* clause-scoped the same way, so a step that only mentions account creation
|
|
1282
|
+
* in descriptive text (e.g. a wizard step title) isn't mistaken for a
|
|
1283
|
+
* completed account-creation step. Pure.
|
|
1238
1284
|
*/
|
|
1239
1285
|
function isReplanRegressingAcrossAuthBoundary(newSteps, completedSteps) {
|
|
1240
|
-
const hasAccountCreation = completedSteps.some((s) =>
|
|
1241
|
-
const norm = normalizeInstruction(s);
|
|
1242
|
-
return ACCOUNT_CREATION_PATTERNS.some((p) => p.test(norm));
|
|
1243
|
-
});
|
|
1286
|
+
const hasAccountCreation = completedSteps.some((s) => (0, phantom_click_1.splitIntoActionClauses)(s).some((clause) => ACCOUNT_CREATION_PATTERNS.some((p) => p.test(clause))));
|
|
1244
1287
|
if (!hasAccountCreation)
|
|
1245
1288
|
return false;
|
|
1246
1289
|
return newSteps.some((s) => (0, phantom_click_1.splitIntoActionClauses)(s.instruction).some((clause) => phantom_click_1.SIGN_IN_PATTERNS.some((p) => p.test(clause))));
|
|
@@ -1263,10 +1306,18 @@ const SUBMIT_SHAPED_INSTRUCTION_PATTERNS = [
|
|
|
1263
1306
|
* combination that no longer matches what this particular step does).
|
|
1264
1307
|
* Matches on word boundaries against normalized (lowercased, whitespace-
|
|
1265
1308
|
* collapsed) text, mirroring {@link SIGN_IN_PATTERNS}/{@link ACCOUNT_CREATION_PATTERNS},
|
|
1266
|
-
* so a phrase like "commit the change" does not false-positive on "submit".
|
|
1309
|
+
* so a phrase like "commit the change" does not false-positive on "submit".
|
|
1310
|
+
* Negated guardrail clauses ("Do NOT click ... proceeds to checkout") are
|
|
1311
|
+
* blanked first: a step that declares itself not a submit is never seeded as
|
|
1312
|
+
* one. Pure.
|
|
1267
1313
|
*/
|
|
1268
1314
|
function isSubmitShapedInstructionText(instruction) {
|
|
1269
|
-
|
|
1315
|
+
// The loose wording patterns below would seed an explicit `submitStep: true`,
|
|
1316
|
+
// which overrides a declared non-submit intent everywhere downstream; defer
|
|
1317
|
+
// to the same classifier the submit-judge gates read so the two cannot disagree.
|
|
1318
|
+
if ((0, flow_runner_1.classifyStepSubmitIntent)(instruction) === "non-submit")
|
|
1319
|
+
return false;
|
|
1320
|
+
const norm = normalizeInstruction((0, flow_runner_1.stripNegatedSubmitClauses)(instruction));
|
|
1270
1321
|
return SUBMIT_SHAPED_INSTRUCTION_PATTERNS.some((p) => p.test(norm));
|
|
1271
1322
|
}
|
|
1272
1323
|
/**
|
|
@@ -2224,24 +2275,27 @@ async function main() {
|
|
|
2224
2275
|
// rather than a previous attempt's partially-replanned plan.
|
|
2225
2276
|
const plan = [];
|
|
2226
2277
|
const replanEvents = [];
|
|
2278
|
+
const transientStepRetries = new Map();
|
|
2227
2279
|
try {
|
|
2228
2280
|
const stagehand = session.stagehand;
|
|
2229
|
-
|
|
2281
|
+
let page = await stagehand.context.awaitActivePage();
|
|
2230
2282
|
// Phase label is mutated between flow steps so the single CDP listener
|
|
2231
2283
|
// always tags captures with the currently active phase.
|
|
2232
2284
|
let currentPhase = "home";
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2285
|
+
// Live read so SPA history navigation between captures stays accurate.
|
|
2286
|
+
// Fallback to the initial url on parse failure (e.g. about:blank early
|
|
2287
|
+
// in the goto cycle) so we don't accidentally mark a capture as
|
|
2288
|
+
// cross-origin and miss the user-action signal.
|
|
2289
|
+
const currentPageOrigin = () => {
|
|
2238
2290
|
try {
|
|
2239
2291
|
return new URL(page.url()).origin;
|
|
2240
2292
|
}
|
|
2241
2293
|
catch {
|
|
2242
2294
|
return new URL(url).origin;
|
|
2243
2295
|
}
|
|
2244
|
-
}
|
|
2296
|
+
};
|
|
2297
|
+
const capture = (0, recon_capture_rebind_1.createCaptureRebinder)(page, (target) => wireNetworkCapture(target, counter, signalCounter, recentCaptures, recentCaptureMeta, () => currentPhase, currentPageOrigin));
|
|
2298
|
+
const stopCapture = capture.stopAll;
|
|
2245
2299
|
logger.info(`navigating to ${url} (waitUntil: ${GOTO_WAIT_UNTIL})`);
|
|
2246
2300
|
// A navigation wait that never resolves must not discard the run. Ad-heavy
|
|
2247
2301
|
// commercial sites (analytics/session-replay beacons on timers) never reach
|
|
@@ -2316,7 +2370,7 @@ async function main() {
|
|
|
2316
2370
|
const currentOrigin = originOf(readLiveUrl());
|
|
2317
2371
|
if (currentOrigin !== "" && currentOrigin !== lastOrigin) {
|
|
2318
2372
|
logger.info(`origin changed ${lastOrigin || "(none)"} → ${currentOrigin}; re-gating on SPA hydration`);
|
|
2319
|
-
await (0, spa_readiness_1.
|
|
2373
|
+
await (0, spa_readiness_1.regateSpaAfterOriginChange)(page, logger);
|
|
2320
2374
|
lastOrigin = currentOrigin;
|
|
2321
2375
|
}
|
|
2322
2376
|
// Debug: dump the full DOM right before this step's cascade runs. Lets
|
|
@@ -2360,6 +2414,10 @@ async function main() {
|
|
|
2360
2414
|
// in-frame navigation would otherwise be invisible to the top-level
|
|
2361
2415
|
// page.url() this used to read.
|
|
2362
2416
|
const urlAtStepStart = await (0, frame_target_1.readCurrentFrameUrl)(page, frameTarget);
|
|
2417
|
+
const textAtStepStart = step.submitStep
|
|
2418
|
+
? (await (0, flow_runner_1.snapshotPage)(frameTarget, signalCounter).catch(() => undefined))
|
|
2419
|
+
?.visibleTextSignature
|
|
2420
|
+
: undefined;
|
|
2363
2421
|
// A child frame CDP has just attached to may still be sitting on
|
|
2364
2422
|
// about:blank (the moment right after Target.setAutoAttach fires and
|
|
2365
2423
|
// before the OOPIF's own navigation lands). waitForSpaReady above only
|
|
@@ -2385,6 +2443,13 @@ async function main() {
|
|
|
2385
2443
|
: (0, flow_runner_1.executeStepWithHealing)({
|
|
2386
2444
|
stagehand,
|
|
2387
2445
|
page,
|
|
2446
|
+
onPageAdopted: (adopted) => {
|
|
2447
|
+
if (adopted === page)
|
|
2448
|
+
return;
|
|
2449
|
+
page = adopted;
|
|
2450
|
+
capture.adopt(adopted);
|
|
2451
|
+
},
|
|
2452
|
+
abortSignal: session.deathAbortSignal,
|
|
2388
2453
|
frameTarget,
|
|
2389
2454
|
step: step.instruction,
|
|
2390
2455
|
optional: step.optional,
|
|
@@ -2395,7 +2460,10 @@ async function main() {
|
|
|
2395
2460
|
emailStepConfig: step.emailStepConfig,
|
|
2396
2461
|
allocatedInbox,
|
|
2397
2462
|
flowHasSubmitSemantics: (0, flow_runner_1.flowHasSubmitSemantics)({
|
|
2398
|
-
steps: plan.map((s) => ({
|
|
2463
|
+
steps: plan.map((s) => ({
|
|
2464
|
+
submitStep: s.submitStep === true,
|
|
2465
|
+
instruction: s.instruction,
|
|
2466
|
+
})),
|
|
2399
2467
|
submitEndpointPattern,
|
|
2400
2468
|
requireSubmitEndpointMatch,
|
|
2401
2469
|
}),
|
|
@@ -2513,8 +2581,13 @@ async function main() {
|
|
|
2513
2581
|
// matches, so skip the replan dispatcher entirely and resume with
|
|
2514
2582
|
// the remaining tail.
|
|
2515
2583
|
const urlAfterFailure = await (0, frame_target_1.readCurrentFrameUrl)(page, frameTarget);
|
|
2516
|
-
|
|
2517
|
-
hasPageAlreadyAdvancedPastStep(urlAtStepStart, urlAfterFailure, step.instruction)
|
|
2584
|
+
const urlAdvanced = !step.submitStep &&
|
|
2585
|
+
hasPageAlreadyAdvancedPastStep(urlAtStepStart, urlAfterFailure, step.instruction);
|
|
2586
|
+
const domAdvanced = !urlAdvanced &&
|
|
2587
|
+
step.submitStep === true &&
|
|
2588
|
+
hasSubmitStepDomAdvanced(i === plan.length - 1, textAtStepStart, (await (0, flow_runner_1.snapshotPage)(frameTarget, signalCounter).catch(() => undefined))
|
|
2589
|
+
?.visibleTextSignature);
|
|
2590
|
+
if (urlAdvanced || domAdvanced) {
|
|
2518
2591
|
logger.info(`${(0, flow_runner_1.formatStepPrefix)(i, () => plan.length)} verification failed but the page already advanced past this step (${urlAtStepStart} → ${urlAfterFailure}); treating as completed and resuming remaining tail`);
|
|
2519
2592
|
consecutiveStaleSkips = 0;
|
|
2520
2593
|
lastSuccessNetworkCount = signalCounter.n;
|
|
@@ -2543,7 +2616,11 @@ async function main() {
|
|
|
2543
2616
|
// /tmp/recon/graphql/ during a sweep: some tenants hit this exact
|
|
2544
2617
|
// failure — only interruption_check + GA tracking POSTs fired, no
|
|
2545
2618
|
// integrated_apply, yet trailing-grace declared success.
|
|
2546
|
-
|
|
2619
|
+
const stepDeclaredNonSubmit = (0, flow_runner_1.isStepDeclaredNonSubmit)({
|
|
2620
|
+
step: step.instruction,
|
|
2621
|
+
submitStep: step.submitStep ?? false,
|
|
2622
|
+
});
|
|
2623
|
+
if (step.optional && !stepDeclaredNonSubmit && i >= plan.length - flow_runner_1.TRAILING_GRACE_WINDOW) {
|
|
2547
2624
|
// Trailing-grace check: did the submit actually land somewhere in
|
|
2548
2625
|
// the recent capture history? Ask the same Haiku judge — it has
|
|
2549
2626
|
// multi-signal reasoning to distinguish real submit POSTs from
|
|
@@ -2555,6 +2632,11 @@ async function main() {
|
|
|
2555
2632
|
const trailingGraceVerdict = await (0, verify_submit_1.verifySubmitWithLLM)({
|
|
2556
2633
|
client: anthropic,
|
|
2557
2634
|
input: {
|
|
2635
|
+
...(await (0, flow_runner_1.buildSubmitJudgeStepContext)({
|
|
2636
|
+
step: step.instruction,
|
|
2637
|
+
isFinalStep: i === plan.length - 1,
|
|
2638
|
+
target: (0, frame_target_1.mainFrameTarget)(page),
|
|
2639
|
+
})),
|
|
2558
2640
|
// A dead session throws synchronously here too — fall back to ""
|
|
2559
2641
|
// rather than let the trailing-grace check itself crash the run.
|
|
2560
2642
|
pageUrl: (() => {
|
|
@@ -2634,16 +2716,21 @@ async function main() {
|
|
|
2634
2716
|
// failed step's re-emission. originalRemaining is re-appended below.
|
|
2635
2717
|
// The untruncated failure-time DOM re-verifies completed fills so a
|
|
2636
2718
|
// silently-reset field isn't trusted as still-completed.
|
|
2637
|
-
const { bodyHtmlAtFailure, fieldValuesAtFailure } = (() => {
|
|
2719
|
+
const { bodyHtmlAtFailure, fieldValuesAtFailure, attemptsAtFailure } = (() => {
|
|
2638
2720
|
try {
|
|
2639
2721
|
const dump = JSON.parse((0, node_fs_1.readFileSync)(dumpPath, "utf8"));
|
|
2640
2722
|
return {
|
|
2723
|
+
attemptsAtFailure: dump.attempts ?? null,
|
|
2641
2724
|
bodyHtmlAtFailure: dump.bodyOuterHtml ?? null,
|
|
2642
2725
|
fieldValuesAtFailure: dump.fieldValuesAtFailure ?? null,
|
|
2643
2726
|
};
|
|
2644
2727
|
}
|
|
2645
2728
|
catch {
|
|
2646
|
-
return {
|
|
2729
|
+
return {
|
|
2730
|
+
bodyHtmlAtFailure: null,
|
|
2731
|
+
fieldValuesAtFailure: null,
|
|
2732
|
+
attemptsAtFailure: null,
|
|
2733
|
+
};
|
|
2647
2734
|
}
|
|
2648
2735
|
})();
|
|
2649
2736
|
const newSteps = filterCompletedFromReplan(rawNewSteps, completedSteps, step.instruction, bodyHtmlAtFailure, fieldValuesAtFailure);
|
|
@@ -2660,7 +2747,11 @@ async function main() {
|
|
|
2660
2747
|
// cascade on the identical click that just exhausted it. Abort now
|
|
2661
2748
|
// instead of waiting REPLAN_CYCLE_THRESHOLD repeats for the cycle
|
|
2662
2749
|
// detector — that many dead cascades cost minutes of wall-clock.
|
|
2663
|
-
|
|
2750
|
+
const noProgressDecision = decideReplanNoProgress(newSteps, step.instruction, attemptsAtFailure, transientStepRetries);
|
|
2751
|
+
if (noProgressDecision === "retry") {
|
|
2752
|
+
logger.warn(`replan #${replanIndex} re-proposed the failed step after provider-transient errors only; allowing bounded re-run`);
|
|
2753
|
+
}
|
|
2754
|
+
if (noProgressDecision === "abort") {
|
|
2664
2755
|
const noProgressMessage = `replan #${replanIndex} re-proposed only the just-failed step ("${step.instruction.slice(0, 60)}") with no new bridge; resuming would re-fail identically — aborting`;
|
|
2665
2756
|
logger.error(noProgressMessage);
|
|
2666
2757
|
throw new errors_2.StepVerificationError(noProgressMessage, "replan-cycle-detected");
|
|
@@ -2794,8 +2885,7 @@ async function main() {
|
|
|
2794
2885
|
// maxTransportRetries budget re-running the same head of the flow.
|
|
2795
2886
|
const sessionTimeoutHit = session.getSessionTimeoutHit?.();
|
|
2796
2887
|
if (sessionTimeoutHit) {
|
|
2797
|
-
|
|
2798
|
-
process.exit(1);
|
|
2888
|
+
throw new errors_2.SessionLifetimeExceededError(`Browserbase session hit its timeout (${sessionTimeoutHit.configuredTimeoutSeconds}s) after ${completedSteps.length} steps`);
|
|
2799
2889
|
}
|
|
2800
2890
|
const cdpTransportClosedError = session.getCdpTransportClosedError?.();
|
|
2801
2891
|
if (cdpTransportClosedError) {
|
|
@@ -2842,6 +2932,13 @@ async function main() {
|
|
|
2842
2932
|
await snapshotAndPersistCookieJar(page, jarCounter, "run-complete", currentPhase, plan.length - 1);
|
|
2843
2933
|
logger.info(`recon complete — ${counter.n} captures written to ${runDir.root}`);
|
|
2844
2934
|
}
|
|
2935
|
+
catch (err) {
|
|
2936
|
+
// Runs before the finally closes the session so any error that escaped
|
|
2937
|
+
// the step loop (deathSignal rejection, page.url() timeout, uninitialized
|
|
2938
|
+
// Stagehand) is upgraded to the non-retryable lifetime error when the
|
|
2939
|
+
// session died of its own configured lifetime.
|
|
2940
|
+
throw (0, errors_2.isSessionLifetimeExceededError)(err) ? err : (0, session_teardown_1.resolveSessionDeathError)(session, err);
|
|
2941
|
+
}
|
|
2845
2942
|
finally {
|
|
2846
2943
|
// Replay-the-discovered-path: if any replan fired and the user provided
|
|
2847
2944
|
// a flow file, write the improved plan back so the next run starts
|
|
@@ -2897,20 +2994,29 @@ async function main() {
|
|
|
2897
2994
|
// re-thrown unchanged (same instance, same type) once the retry call
|
|
2898
2995
|
// settles. This is plain error propagation, not a second retry loop.
|
|
2899
2996
|
let nonTransportError;
|
|
2997
|
+
let lifetimeError;
|
|
2900
2998
|
try {
|
|
2901
2999
|
await (0, retry_1.withScraperRetry)(async () => {
|
|
2902
3000
|
try {
|
|
2903
3001
|
await runFlowAttempt();
|
|
2904
3002
|
}
|
|
2905
3003
|
catch (err) {
|
|
2906
|
-
if (
|
|
3004
|
+
if ((0, errors_2.isSessionLifetimeExceededError)(err)) {
|
|
3005
|
+
lifetimeError = err;
|
|
3006
|
+
throw err;
|
|
3007
|
+
}
|
|
3008
|
+
if ((0, errors_2.isCdpTransportClosedError)(err))
|
|
2907
3009
|
throw err;
|
|
2908
3010
|
nonTransportError = err;
|
|
2909
3011
|
}
|
|
2910
3012
|
}, { maxAttempts: config_1.config.scraper.maxTransportRetries });
|
|
2911
3013
|
}
|
|
2912
3014
|
catch (err) {
|
|
2913
|
-
if (
|
|
3015
|
+
if (lifetimeError) {
|
|
3016
|
+
logger.error(`${lifetimeError.message} — raise BROWSERBASE_SESSION_TIMEOUT_SECONDS`);
|
|
3017
|
+
process.exit(1);
|
|
3018
|
+
}
|
|
3019
|
+
if ((0, errors_2.isCdpTransportClosedError)(err)) {
|
|
2914
3020
|
logger.error(`Stagehand tore down the CDP transport mid-flow on every attempt (${config_1.config.scraper.maxTransportRetries} of ${config_1.config.scraper.maxTransportRetries}): ${err.message} — refusing to report success`);
|
|
2915
3021
|
process.exit(1);
|
|
2916
3022
|
}
|