@@ -52,7 +52,7 @@ import { normalizeRuntimeTraceConfig } from '../trace/runtime-config.js';
5252import { tracesToMarkdown } from './trace-export.js';
5353import { solveCaptcha, detectCaptcha, injectToken, captchaParamError, captchaTypesMatch, captchaWebsiteUrl } from './captcha-solver.js';
5454import { isCapsolverEnabled, normalizeCapsolverApiKey } from './capsolver-config.js';
55- import { captchaChallengeKey, detectChallengeDialog, detectChallengeDialogInPage } from './captcha-gate.js';
55+ import { captchaChallengeKey, captchaChallengeMatcherOptions, detectChallengeDialog, detectChallengeDialogInPage } from './captcha-gate.js';
5656import { applyCaptchaFrameVisibility } from './captcha-frame-runtime.js';
5757import { getRecordingStateFresh as recorderStateFresh } from '../recorder/host.js';
5858import { Capability, CAPABILITY_LABEL, capabilitiesFor, requiredHosts, frameHostMatches, isNetworkMutation, normalizeHost, PermissionManager, UNTRUSTED_CONTENT_TOOLS } from './permission-gate.js';
@@ -4205,7 +4205,7 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
42054205 func: detectChallengeDialogInPage,
42064206 args: [{
42074207 includeFrameContext: true,
4208- allowGenericFailure: options?.allowGenericFailure === true ,
4208+ ...captchaChallengeMatcherOptions() ,
42094209 }],
42104210 });
42114211 const inspected = (results) => {
@@ -4218,7 +4218,6 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
42184218 if (!label) return [];
42194219 const normalized = detectChallengeDialog(
42204220 `dialog ${JSON.stringify(label.slice(0, 200))}`,
4221- { allowGenericFailure: options?.allowGenericFailure === true },
42224221 );
42234222 if (!normalized?.normalizedLabel) return [];
42244223 return [{
@@ -4323,13 +4322,10 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
43234322 ? toolResult.captchaChallengeFrameId
43244323 : null;
43254324 let observedChallengeFrameUrl = String(toolResult.captchaChallengeFrameUrl || '');
4326- let challenge = detectChallengeDialog(toolResult.pageContent, {
4327- allowGenericFailure: !!activeGate,
4328- });
4325+ let challenge = detectChallengeDialog(toolResult.pageContent);
43294326 if (!challenge && toolResult.pageGate?.surface === 'dialog' && toolResult.pageGate?.label) {
43304327 challenge = detectChallengeDialog(
43314328 `dialog ${JSON.stringify(String(toolResult.pageGate.label).slice(0, 200))}`,
4332- { allowGenericFailure: !!activeGate },
43334329 );
43344330 }
43354331 if (
@@ -4340,12 +4336,10 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
43404336 const recheck = await this._detectChallengeDialogBeforeMutation(tabId, {
43414337 includeStatus: true,
43424338 expectedFrameId: observedChallengeFrameId,
4343- allowGenericFailure: !!activeGate,
43444339 });
43454340 if (recheck.challenge?.label) {
43464341 challenge = detectChallengeDialog(
43474342 `dialog ${JSON.stringify(String(recheck.challenge.label).slice(0, 200))}`,
4348- { allowGenericFailure: !!activeGate },
43494343 );
43504344 observedChallengeFrameId = Number.isInteger(recheck.challenge.frameId)
43514345 ? recheck.challenge.frameId
@@ -4412,20 +4406,67 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
44124406 }
44134407 }
44144408 }
4409+ const requestedPage = toolArgs?.page;
4410+ const requestedMaxDepth = toolArgs?.maxDepth;
4411+ const parsedMaxDepth = Number(requestedMaxDepth);
4412+ const authoritativeRootRead = !toolArgs?.ref_id
4413+ && (
4414+ requestedPage === undefined
4415+ || requestedPage === null
4416+ || requestedPage === ''
4417+ || Number(requestedPage) === 1
4418+ )
4419+ && treeFilter !== 'interactive'
4420+ && (
4421+ requestedMaxDepth === undefined
4422+ || requestedMaxDepth === null
4423+ || requestedMaxDepth === ''
4424+ || (Number.isFinite(parsedMaxDepth) && parsedMaxDepth >= 15)
4425+ )
4426+ && toolResult.truncated !== true
4427+ && toolResult.hasMore !== true
4428+ && toolResult.autoDegraded !== true;
4429+ if (
4430+ !challenge
4431+ && activeGate
4432+ && authoritativeRootRead
4433+ && Number.isInteger(activeGate.challengeFrameId)
4434+ ) {
4435+ const frameInspection = await this._detectChallengeDialogBeforeMutation(tabId, {
4436+ includeStatus: true,
4437+ expectedFrameId: activeGate.challengeFrameId,
4438+ });
4439+ if (frameInspection.challenge?.label) {
4440+ challenge = detectChallengeDialog(
4441+ `dialog ${JSON.stringify(String(frameInspection.challenge.label).slice(0, 200))}`
4442+ );
4443+ } else if (!frameInspection.inspectionComplete) {
4444+ const guardedState = normalizeCaptchaGateState(activeGate);
4445+ this._captchaGateStates.set(tabId, guardedState);
4446+ toolResult.captchaGate = guardedState.publicGate;
4447+ return { gate: guardedState.publicGate, loopCheck: { kind: 'none' } };
4448+ }
4449+ }
4450+ const correlatedCaptchaCandidateIdentity =
4451+ activeGate?.publicGate?.candidateNotCorrelated === true
4452+ ? null
4453+ : activeGate?.captchaCandidateIdentity;
44154454 let postSolveTokenState = null;
44164455 if (
44174456 ['verification_pending', 'manual_required', 'cleared'].includes(activeGate?.status)
4418- && activeGate.captchaCandidateIdentity
4457+ && correlatedCaptchaCandidateIdentity
44194458 ) {
44204459 await inspectCaptchaFrames();
44214460 if (!detectionFailed && detection && typeof detection === 'object') {
44224461 postSolveTokenState = captchaPostSolveTokenState(
44234462 detection,
4424- activeGate.captchaCandidateIdentity ,
4463+ correlatedCaptchaCandidateIdentity ,
44254464 );
44264465 }
44274466 }
4428- const loopCheck = challenge
4467+ const directCaptchaEvidence = !!correlatedCaptchaCandidateIdentity
4468+ || activeGate?.publicGate?.languageNeutralFrameTrigger === true;
4469+ const loopCheck = challenge || (authoritativeRootRead && !directCaptchaEvidence)
44294470 ? this._checkVerificationChallengeLoop(tabId, {
44304471 pageUrl,
44314472 dialogLabel: challenge?.normalizedLabel || '',
@@ -4493,14 +4534,9 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
44934534 return { gate: pendingGate, loopCheck };
44944535 }
44954536 if (!challenge) {
4496- if (
4497- activeGate?.status === 'manual_required'
4498- && !activeGate.captchaCandidateIdentity
4499- && activeGate.publicGate?.languageNeutralFrameTrigger !== true
4500- ) {
4537+ if (activeGate?.status === 'manual_required' && !directCaptchaEvidence) {
45014538 const manualInspection = await this._detectChallengeDialogBeforeMutation(tabId, {
45024539 includeStatus: true,
4503- allowGenericFailure: true,
45044540 ...(Number.isInteger(activeGate.challengeFrameId)
45054541 ? { expectedFrameId: activeGate.challengeFrameId }
45064542 : {}),
@@ -4516,6 +4552,12 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
45164552 return { gate: clearedGate, loopCheck };
45174553 }
45184554 }
4555+ if (activeGate && authoritativeRootRead && activeGate.status !== 'cleared') {
4556+ const guardedState = normalizeCaptchaGateState(activeGate);
4557+ this._captchaGateStates.set(tabId, guardedState);
4558+ toolResult.captchaGate = guardedState.publicGate;
4559+ return { gate: guardedState.publicGate, loopCheck };
4560+ }
45194561 if (activeGate?.status === 'cleared') {
45204562 return { gate: null, loopCheck };
45214563 }
@@ -4609,7 +4651,9 @@ Rules: no prose intro, no conclusion, no "this screenshot shows...", no layout d
46094651 ...(detection?.selected && !selectedCorrelated ? { candidateNotCorrelated: true } : {}),
46104652 ...(languageNeutralFrameTrigger ? { languageNeutralFrameTrigger: true } : {}),
46114653 };
4612- const captchaCandidateIdentity = captchaGateCandidateIdentity(detection?.selected);
4654+ const captchaCandidateIdentity = selectedCorrelated
4655+ ? captchaGateCandidateIdentity(detection?.selected)
4656+ : null;
46134657 this._captchaGateStates.set(tabId, {
46144658 key,
46154659 status: publicGate.status,
0 commit comments