smooth-operator-mcp 3.0.0 → 3.0.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.
- package/.env.example +5 -5
- package/README.md +5 -2
- package/dist/smooth-operator.mjs +52 -149
- package/dist/smooth-operator.mjs.map +2 -2
- package/docs/mcp-server.md +16 -18
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -40,16 +40,16 @@ SMOOTH_OPERATOR_ALLOW_EVAL=true
|
|
|
40
40
|
# these private roots by default.
|
|
41
41
|
# SMOOTH_OPERATOR_ALLOWED_FILE_ROOTS=~/.smooth-operator/files,~/.smooth-operator/downloads
|
|
42
42
|
|
|
43
|
-
#
|
|
44
|
-
# to
|
|
43
|
+
# Compatibility baseline. Browser identity and automation signals remain native;
|
|
44
|
+
# set false to skip the explicit-viewport compatibility script.
|
|
45
45
|
# SMOOTH_OPERATOR_STEALTH_ENABLED=true
|
|
46
46
|
# Stealth profile: balanced (default) | max (accepted compatibility names; same supported patch set)
|
|
47
47
|
# SMOOTH_OPERATOR_STEALTH_PROFILE=balanced
|
|
48
48
|
# Add opt-in GPU launch flags (does not make an identity/coherence guarantee)
|
|
49
49
|
# SMOOTH_OPERATOR_STEALTH_GPU=false
|
|
50
|
-
# Behavioral
|
|
51
|
-
#
|
|
52
|
-
# SMOOTH_OPERATOR_BEHAVIOR_ENABLED=
|
|
50
|
+
# Behavioral timing is off for the fastest deterministic path; set true only
|
|
51
|
+
# when a site or workflow explicitly needs it.
|
|
52
|
+
# SMOOTH_OPERATOR_BEHAVIOR_ENABLED=false
|
|
53
53
|
|
|
54
54
|
# HTTP is opt-in; use a strong token before enabling remote access
|
|
55
55
|
# SMOOTH_OPERATOR_TRANSPORT=http
|
package/README.md
CHANGED
|
@@ -44,9 +44,12 @@ Ask: *“Scrape pricing into a table”*, *“Fill this form with ~/resume.pdf
|
|
|
44
44
|
|
|
45
45
|
## How to use
|
|
46
46
|
|
|
47
|
-
Talk to your harness normally. It can call `browser_navigate` → `browser_snapshot` → `browser_click` and the rest of the MCP surface as needed. For a challenge, `browser_solve_challenge` returns fresh bounded visual/state evidence
|
|
47
|
+
Talk to your harness normally. It can call `browser_navigate` → `browser_snapshot` → `browser_click` and the rest of the MCP surface as needed. For a challenge, `browser_solve_challenge` returns fresh bounded visual/state evidence and an attempt budget; the connected AI keeps using ordinary browser actions and calls it again until the final classification is clear or the budget is exhausted. Human handoff remains available only as an explicit final option.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Browser identity remains native, page JavaScript is available by default, and
|
|
50
|
+
behavioral timing is off for fast deterministic input. Set the explicit
|
|
51
|
+
environment flags to change those choices. See `docs/STEALTH-GUIDE.md` for
|
|
52
|
+
details and responsible use.
|
|
50
53
|
|
|
51
54
|
For the fastest supported operation, keep behavioral timing off with
|
|
52
55
|
`SMOOTH_OPERATOR_BEHAVIOR_ENABLED=false`; the native browser remains bounded and
|
package/dist/smooth-operator.mjs
CHANGED
|
@@ -397,7 +397,7 @@ var SERVER_VERSION;
|
|
|
397
397
|
var init_version = __esm({
|
|
398
398
|
"src/server/version.ts"() {
|
|
399
399
|
"use strict";
|
|
400
|
-
SERVER_VERSION = "3.0.
|
|
400
|
+
SERVER_VERSION = "3.0.1";
|
|
401
401
|
}
|
|
402
402
|
});
|
|
403
403
|
|
|
@@ -1554,7 +1554,7 @@ function writeSummary(ui, harness, choices) {
|
|
|
1554
1554
|
["Blocked sites", choices.blockedDomains.length ? choices.blockedDomains.join(", ") : "none"],
|
|
1555
1555
|
["Page JavaScript", choices.allowEval ? "enabled" : "off"],
|
|
1556
1556
|
["Stealth baseline", "enabled (balanced)"],
|
|
1557
|
-
["Behavioral
|
|
1557
|
+
["Behavioral timing", "off (fast native input)"],
|
|
1558
1558
|
["Data directory", choices.dataDir]
|
|
1559
1559
|
]
|
|
1560
1560
|
]);
|
|
@@ -1629,7 +1629,7 @@ async function persistWizardConfig(rawChoices, homeDir) {
|
|
|
1629
1629
|
enabled: true,
|
|
1630
1630
|
profile: "balanced",
|
|
1631
1631
|
gpu: false,
|
|
1632
|
-
behaviorEnabled:
|
|
1632
|
+
behaviorEnabled: false
|
|
1633
1633
|
};
|
|
1634
1634
|
const config = { ...previous, browser: browserSection, security: securitySection, stealth: stealthSection };
|
|
1635
1635
|
delete config.captchaSolver;
|
|
@@ -2570,7 +2570,7 @@ function loadServerConfig(args = [], environment = env, homeDirectory = homedir(
|
|
|
2570
2570
|
enabled: stealthEnabled,
|
|
2571
2571
|
profile: environment.SMOOTH_OPERATOR_STEALTH_PROFILE ?? nestedStealth.profile ?? "balanced",
|
|
2572
2572
|
gpu: parseBoolean(environment.SMOOTH_OPERATOR_STEALTH_GPU, nestedStealth.gpu ?? false),
|
|
2573
|
-
behaviorEnabled: environment.SMOOTH_OPERATOR_BEHAVIOR_ENABLED === void 0 ? nestedStealth.behaviorEnabled ??
|
|
2573
|
+
behaviorEnabled: environment.SMOOTH_OPERATOR_BEHAVIOR_ENABLED === void 0 ? nestedStealth.behaviorEnabled ?? false : parseBoolean(environment.SMOOTH_OPERATOR_BEHAVIOR_ENABLED, stealthEnabled)
|
|
2574
2574
|
};
|
|
2575
2575
|
const config = {
|
|
2576
2576
|
transport: argValue("--transport") ?? environment.SMOOTH_OPERATOR_TRANSPORT ?? fileConfig.transport ?? "stdio",
|
|
@@ -2820,6 +2820,7 @@ var BrowserActionFieldsSchema = z2.object({
|
|
|
2820
2820
|
verify: z2.boolean().optional(),
|
|
2821
2821
|
durationMs: z2.number().int().min(0).max(3e4).optional(),
|
|
2822
2822
|
pollMs: z2.number().int().min(250).max(1e4).optional(),
|
|
2823
|
+
maxAttempts: z2.number().int().min(1).max(100).optional(),
|
|
2823
2824
|
optionValue: BoundedString(2e3).optional(),
|
|
2824
2825
|
optionValues: z2.array(BoundedString(2e3)).min(1).max(200).optional(),
|
|
2825
2826
|
cookieName: BoundedString(256).optional(),
|
|
@@ -3256,7 +3257,8 @@ var SolveChallengeRequestSchema = z2.object({
|
|
|
3256
3257
|
full: z2.boolean().optional(),
|
|
3257
3258
|
maxDimension: z2.number().int().min(1).max(2e4).optional(),
|
|
3258
3259
|
max_dim: z2.number().int().min(1).max(2e4).optional(),
|
|
3259
|
-
maxChars: z2.number().int().min(1e3).max(MCP_PAGE_TEXT_MAX_CHARS).optional()
|
|
3260
|
+
maxChars: z2.number().int().min(1e3).max(MCP_PAGE_TEXT_MAX_CHARS).optional(),
|
|
3261
|
+
maxAttempts: z2.number().int().min(1).max(100).optional()
|
|
3260
3262
|
}).strict().superRefine((input, context) => {
|
|
3261
3263
|
if (input.includeScreenshot !== void 0 && input.include_screenshot !== void 0) {
|
|
3262
3264
|
context.addIssue({ code: "custom", message: "Provide includeScreenshot or include_screenshot, not both." });
|
|
@@ -3624,7 +3626,7 @@ var MCP_INSTRUCTIONS = [
|
|
|
3624
3626
|
"For open shadow roots, Puppeteer pierce/ selectors may be used explicitly; closed shadow roots remain unavailable.",
|
|
3625
3627
|
"Use browser_batch for short validated sequences, but keep destructive actions separate when user confirmation is needed.",
|
|
3626
3628
|
"Use an efficient observe -> act -> verify loop: observe with browser_snapshot or browser_get_state, perform one bounded browser action, then observe again to verify the resulting state. Keep refs and indexes fresh after navigation, scrolling, or DOM changes; parallelize only independent read-only observations.",
|
|
3627
|
-
"browser_solve_challenge is an internal connected-AI loop.
|
|
3629
|
+
"browser_solve_challenge is an internal connected-AI loop. Each call is one bounded verification cycle and returns fresh visual/state evidence plus attemptsRemaining; the connected AI should keep using normal browser actions and call it again until the final classification explicitly reports the challenge absent or automation_exhausted. Never claim a challenge is solved from a present, unknown, or failed classification. Human handoff is only an explicit final option after exhaustion.",
|
|
3628
3630
|
"The server contains no LLM or agent planner; the MCP client is responsible for reasoning, retries, and task completion."
|
|
3629
3631
|
].join(" ");
|
|
3630
3632
|
function createMcpServer(runtime) {
|
|
@@ -3784,7 +3786,7 @@ function registerBrowserTools(server, runtime) {
|
|
|
3784
3786
|
"browser_solve_challenge",
|
|
3785
3787
|
{
|
|
3786
3788
|
title: "Solve a web challenge",
|
|
3787
|
-
description: "Run the internal connected-AI challenge loop
|
|
3789
|
+
description: "Run one cycle of the internal connected-AI challenge loop. Collect fresh bounded visual/state evidence, use normal browser actions, and call again until the challenge is explicitly absent or the bounded attempt budget is exhausted. No external solver or token injection is used.",
|
|
3788
3790
|
inputSchema: SolveChallengeRequestSchema,
|
|
3789
3791
|
annotations: BROWSER_READ_ONLY
|
|
3790
3792
|
},
|
|
@@ -4732,81 +4734,16 @@ function boundedList(values) {
|
|
|
4732
4734
|
}
|
|
4733
4735
|
|
|
4734
4736
|
// src/server/browser/stealth.ts
|
|
4735
|
-
var STEALTH_BASELINE_ARGS = [
|
|
4736
|
-
"--disable-blink-features=AutomationControlled"
|
|
4737
|
-
// hide navigator.webdriver at the C++ source
|
|
4738
|
-
];
|
|
4737
|
+
var STEALTH_BASELINE_ARGS = [];
|
|
4739
4738
|
function buildStealthInitScript(profile, options = {}) {
|
|
4740
4739
|
const { width, height } = profile.viewport;
|
|
4741
4740
|
const applyViewport = options.applyViewport === true;
|
|
4742
|
-
const
|
|
4743
|
-
//
|
|
4744
|
-
|
|
4745
|
-
return 'function ' + fnName + '() { [native code] }';
|
|
4746
|
-
}
|
|
4747
|
-
function patchToString(target, fnStr) {
|
|
4748
|
-
try {
|
|
4749
|
-
Object.defineProperty(target, 'toString', {
|
|
4750
|
-
configurable: true,
|
|
4751
|
-
writable: true,
|
|
4752
|
-
value: function toString() { return fnStr; }
|
|
4753
|
-
});
|
|
4754
|
-
} catch (e) {}
|
|
4755
|
-
}
|
|
4756
|
-
function stripProxyFromErrors(fn) {
|
|
4757
|
-
try {
|
|
4758
|
-
return new Proxy(fn, {
|
|
4759
|
-
apply: function applyTrap(target, thisArg, args) {
|
|
4760
|
-
try { return Reflect.apply(target, thisArg, args); }
|
|
4761
|
-
catch (e) { throw e; }
|
|
4762
|
-
}
|
|
4763
|
-
});
|
|
4764
|
-
} catch (e) { return fn; }
|
|
4765
|
-
}
|
|
4766
|
-
|
|
4767
|
-
// ---- supported runtime values (only explicit configuration is interpolated) ----
|
|
4741
|
+
const source = `
|
|
4742
|
+
// Only an explicitly configured viewport is applied. Browser identity and
|
|
4743
|
+
// native automation signals remain untouched.
|
|
4768
4744
|
var APPLY_VIEWPORT = ${String(applyViewport)};
|
|
4769
4745
|
var VIEWPORT = { width: ${width}, height: ${height} };`;
|
|
4770
|
-
const
|
|
4771
|
-
// 1. navigator.webdriver \u2014 belt-and-suspenders (the launch flag is primary).
|
|
4772
|
-
try {
|
|
4773
|
-
var navProto = Object.getPrototypeOf(navigator);
|
|
4774
|
-
if (navProto && Object.prototype.hasOwnProperty.call(navProto, 'webdriver')) {
|
|
4775
|
-
delete navProto.webdriver;
|
|
4776
|
-
}
|
|
4777
|
-
} catch (e) {}
|
|
4778
|
-
|
|
4779
|
-
// 2. navigator.permissions.query \u2014 resolve the "impossible combination".
|
|
4780
|
-
try {
|
|
4781
|
-
if (navigator.permissions && typeof navigator.permissions.query === 'function') {
|
|
4782
|
-
var perms = navigator.permissions;
|
|
4783
|
-
var originalQuery = perms.query.bind(perms);
|
|
4784
|
-
perms.query = function (query) {
|
|
4785
|
-
if (query && query.name === 'notifications' &&
|
|
4786
|
-
window.location && window.location.protocol !== 'https:') {
|
|
4787
|
-
try {
|
|
4788
|
-
if (typeof PermissionStatus !== 'undefined') {
|
|
4789
|
-
return Promise.resolve(new PermissionStatus({ state: 'denied' }));
|
|
4790
|
-
}
|
|
4791
|
-
} catch (e) {}
|
|
4792
|
-
}
|
|
4793
|
-
return originalQuery(query);
|
|
4794
|
-
};
|
|
4795
|
-
}
|
|
4796
|
-
} catch (e) {}
|
|
4797
|
-
|
|
4798
|
-
// 3. toString / Proxy trace hiding \u2014 the patched getters hide themselves.
|
|
4799
|
-
try {
|
|
4800
|
-
if (navigator.permissions && typeof navigator.permissions.query === 'function') {
|
|
4801
|
-
var hiddenQuery = stripProxyFromErrors(navigator.permissions.query);
|
|
4802
|
-
patchToString(hiddenQuery, makeNativeString('query'));
|
|
4803
|
-
navigator.permissions.query = hiddenQuery;
|
|
4804
|
-
}
|
|
4805
|
-
} catch (e) {}
|
|
4806
|
-
|
|
4807
|
-
// Coherence: screen dimensions track an explicitly configured launch
|
|
4808
|
-
// viewport (guarded, best-effort). Without that explicit input, leave the
|
|
4809
|
-
// browser's real dimensions untouched.
|
|
4746
|
+
const viewport = `
|
|
4810
4747
|
try {
|
|
4811
4748
|
if (APPLY_VIEWPORT && window && typeof window.innerWidth === 'number') {
|
|
4812
4749
|
Object.defineProperty(window, 'innerWidth', {
|
|
@@ -4818,8 +4755,8 @@ function buildStealthInitScript(profile, options = {}) {
|
|
|
4818
4755
|
}
|
|
4819
4756
|
} catch (e) {}`;
|
|
4820
4757
|
return `(function () {
|
|
4821
|
-
${
|
|
4822
|
-
${
|
|
4758
|
+
${source}
|
|
4759
|
+
${viewport}
|
|
4823
4760
|
})();
|
|
4824
4761
|
`;
|
|
4825
4762
|
}
|
|
@@ -4868,67 +4805,10 @@ function nativeBrowserLaunchArgs(options = {}) {
|
|
|
4868
4805
|
init_discovery();
|
|
4869
4806
|
|
|
4870
4807
|
// src/server/browser/fingerprints.ts
|
|
4871
|
-
var DEFAULT_PROFILE = "balanced";
|
|
4872
|
-
var DEFAULT_VERSION = 124;
|
|
4873
|
-
var DEFAULT_PLATFORM = "Windows";
|
|
4874
|
-
var DEFAULT_LANGUAGE = "en-US";
|
|
4875
|
-
var DEFAULT_TIME_ZONE = "America/New_York";
|
|
4876
4808
|
var DEFAULT_VIEWPORT_WIDTH = 1920;
|
|
4877
4809
|
var DEFAULT_VIEWPORT_HEIGHT = 1080;
|
|
4878
|
-
var DEFAULT_SEED = 0;
|
|
4879
|
-
var PLATFORM_SEGMENTS = {
|
|
4880
|
-
Windows: "Windows NT 10.0; Win64; x64",
|
|
4881
|
-
macOS: "Mac OS X 10_15_7",
|
|
4882
|
-
Linux: "X11; Linux x86_64"
|
|
4883
|
-
};
|
|
4884
|
-
var HARDWARE_CONCURRENCY_SET = [2, 4, 8, 16, 32];
|
|
4885
|
-
var DEVICEMEMORY_SET = [1, 2, 4, 8];
|
|
4886
|
-
var MAX_TOUCH_POINTS_SET = [0, 5, 10];
|
|
4887
4810
|
function buildFingerprintProfile(options = {}) {
|
|
4888
|
-
|
|
4889
|
-
const version = normalizeVersion(options.version);
|
|
4890
|
-
const platform3 = normalizePlatform(options.platform);
|
|
4891
|
-
const viewport = normalizeViewport(options.viewport);
|
|
4892
|
-
const language = options.language ?? DEFAULT_LANGUAGE;
|
|
4893
|
-
const seed = normalizeSeed(options.seed);
|
|
4894
|
-
const versionStr = String(version);
|
|
4895
|
-
const userAgent = `Mozilla/5.0 (${PLATFORM_SEGMENTS[platform3]}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${versionStr}.0.0.0 Safari/537.36`;
|
|
4896
|
-
const brands = [
|
|
4897
|
-
{ brand: "Chromium", version: versionStr },
|
|
4898
|
-
{ brand: "Google Chrome", version: versionStr },
|
|
4899
|
-
{ brand: "Not=A?Brand", version: "8" }
|
|
4900
|
-
];
|
|
4901
|
-
const fullVersionList = {
|
|
4902
|
-
Chromium: versionStr,
|
|
4903
|
-
"Google Chrome": versionStr
|
|
4904
|
-
};
|
|
4905
|
-
const profileWithLanguages = {
|
|
4906
|
-
version,
|
|
4907
|
-
userAgent,
|
|
4908
|
-
platform: platform3,
|
|
4909
|
-
mobile: false,
|
|
4910
|
-
brands,
|
|
4911
|
-
fullVersionList,
|
|
4912
|
-
languages: buildLanguages(language),
|
|
4913
|
-
acceptLanguage: `${language},en;q=0.9`,
|
|
4914
|
-
viewport
|
|
4915
|
-
};
|
|
4916
|
-
if (profile === "max") {
|
|
4917
|
-
return {
|
|
4918
|
-
...profileWithLanguages,
|
|
4919
|
-
hardwareConcurrency: pickFromValidSet(HARDWARE_CONCURRENCY_SET, seed),
|
|
4920
|
-
deviceMemory: pickFromValidSet(DEVICEMEMORY_SET, seed),
|
|
4921
|
-
maxTouchPoints: pickFromValidSet(MAX_TOUCH_POINTS_SET, seed),
|
|
4922
|
-
timeZone: options.timeZone ?? DEFAULT_TIME_ZONE
|
|
4923
|
-
};
|
|
4924
|
-
}
|
|
4925
|
-
return profileWithLanguages;
|
|
4926
|
-
}
|
|
4927
|
-
function normalizeVersion(version) {
|
|
4928
|
-
return typeof version === "number" && Number.isFinite(version) && version > 0 ? Math.floor(version) : DEFAULT_VERSION;
|
|
4929
|
-
}
|
|
4930
|
-
function normalizePlatform(platform3) {
|
|
4931
|
-
return platform3 && platform3 in PLATFORM_SEGMENTS ? platform3 : DEFAULT_PLATFORM;
|
|
4811
|
+
return { viewport: normalizeViewport(options.viewport) };
|
|
4932
4812
|
}
|
|
4933
4813
|
function normalizeViewport(viewport) {
|
|
4934
4814
|
if (viewport && Number.isFinite(viewport.width) && Number.isFinite(viewport.height) && viewport.width > 0 && viewport.height > 0) {
|
|
@@ -4936,16 +4816,6 @@ function normalizeViewport(viewport) {
|
|
|
4936
4816
|
}
|
|
4937
4817
|
return { width: DEFAULT_VIEWPORT_WIDTH, height: DEFAULT_VIEWPORT_HEIGHT };
|
|
4938
4818
|
}
|
|
4939
|
-
function normalizeSeed(seed) {
|
|
4940
|
-
return typeof seed === "number" && Number.isFinite(seed) ? seed : DEFAULT_SEED;
|
|
4941
|
-
}
|
|
4942
|
-
function buildLanguages(language) {
|
|
4943
|
-
const parts = language.split(/[-_]/);
|
|
4944
|
-
return parts.length > 1 ? [language, parts[0].toLowerCase()] : [language];
|
|
4945
|
-
}
|
|
4946
|
-
function pickFromValidSet(set, seed) {
|
|
4947
|
-
return set[Math.floor(Math.abs(seed)) % set.length];
|
|
4948
|
-
}
|
|
4949
4819
|
|
|
4950
4820
|
// src/server/browser/utils.ts
|
|
4951
4821
|
var SENSITIVE_URL_PART = /(access[_-]?token|api[_-]?key|auth|code|credential|jwt|nonce|otp|password|secret|session|sig(?:nature)?|token)/i;
|
|
@@ -5051,6 +4921,8 @@ var MAX_DOM_TRAVERSAL_NODES = 2e4;
|
|
|
5051
4921
|
var MAX_TEXT_SCAN_CHARS = 5e5;
|
|
5052
4922
|
var MAX_MARKUP_EVIDENCE_CHARS = 12e4;
|
|
5053
4923
|
var CHALLENGE_AI_GUIDANCE = "Use normal browser click, input, scroll, or key tools on the visible challenge controls, then call solve_challenge again to verify that the challenge is cleared.";
|
|
4924
|
+
var CHALLENGE_DEFAULT_MAX_ATTEMPTS = 32;
|
|
4925
|
+
var CHALLENGE_MAX_ATTEMPTS = 100;
|
|
5054
4926
|
var MAX_DOWNLOAD_ENTRIES = 100;
|
|
5055
4927
|
var TARGET_GUARD_MAX_REQUEST_IDS = 128;
|
|
5056
4928
|
var CLICK_SETTLE_TIMEOUT_MS = 10;
|
|
@@ -8338,6 +8210,7 @@ var BrowserService = class {
|
|
|
8338
8210
|
this.clearTargetGuardNavigationError(state.page);
|
|
8339
8211
|
state.policyVerifiedUrls?.clear();
|
|
8340
8212
|
state.challengeStatus = void 0;
|
|
8213
|
+
state.challengeAttempts = 0;
|
|
8341
8214
|
} catch (error) {
|
|
8342
8215
|
this.logger.debug("Blocked navigation recovery could not restore a blank page", { pageId: state.id, error: String(error) });
|
|
8343
8216
|
}
|
|
@@ -8570,7 +8443,7 @@ var BrowserService = class {
|
|
|
8570
8443
|
state.stealthInjected = true;
|
|
8571
8444
|
} catch (error) {
|
|
8572
8445
|
throwIfAborted(signal);
|
|
8573
|
-
throw new AppError("STEALTH_INITIALIZATION_FAILED", "The
|
|
8446
|
+
throw new AppError("STEALTH_INITIALIZATION_FAILED", "The browser compatibility script could not be injected.", { retryable: true, cause: error });
|
|
8574
8447
|
}
|
|
8575
8448
|
}
|
|
8576
8449
|
await this.releaseTargetGuardForPage(state.page);
|
|
@@ -8751,6 +8624,7 @@ var BrowserService = class {
|
|
|
8751
8624
|
const mainFrame = state.page.mainFrame();
|
|
8752
8625
|
if (frame === mainFrame) {
|
|
8753
8626
|
state.challengeStatus = void 0;
|
|
8627
|
+
state.challengeAttempts = 0;
|
|
8754
8628
|
}
|
|
8755
8629
|
} catch (error) {
|
|
8756
8630
|
this.logger.debug("Browser frame navigation event was unavailable after page disposal", { pageId: state.id, error: String(error) });
|
|
@@ -10372,12 +10246,15 @@ var BrowserService = class {
|
|
|
10372
10246
|
async solveChallenge(state, action, signal) {
|
|
10373
10247
|
throwIfAborted(signal);
|
|
10374
10248
|
const previousChallengeStatus = state.challengeStatus;
|
|
10249
|
+
const requestedMaxAttempts = action.maxAttempts;
|
|
10250
|
+
const maxAttempts = Number.isFinite(requestedMaxAttempts) ? Math.min(CHALLENGE_MAX_ATTEMPTS, Math.max(1, Math.floor(requestedMaxAttempts))) : CHALLENGE_DEFAULT_MAX_ATTEMPTS;
|
|
10375
10251
|
const detection = await this.detectChallenge(state, signal);
|
|
10376
10252
|
if (isChallengeUnknown(detection)) {
|
|
10377
10253
|
state.challengeStatus = "unknown";
|
|
10378
10254
|
return {
|
|
10379
10255
|
solved: false,
|
|
10380
10256
|
verified: false,
|
|
10257
|
+
challengeState: "unknown",
|
|
10381
10258
|
resolution: "challenge_state_unverified",
|
|
10382
10259
|
verification: "unknown",
|
|
10383
10260
|
workflow: "verification_unavailable",
|
|
@@ -10389,18 +10266,41 @@ var BrowserService = class {
|
|
|
10389
10266
|
}
|
|
10390
10267
|
if (isChallengeAbsent(detection)) {
|
|
10391
10268
|
const cleared = previousChallengeStatus === "present";
|
|
10269
|
+
const attempts2 = state.challengeAttempts ?? 0;
|
|
10392
10270
|
state.challengeStatus = "absent";
|
|
10271
|
+
state.challengeAttempts = 0;
|
|
10393
10272
|
return {
|
|
10394
10273
|
solved: true,
|
|
10395
10274
|
verified: true,
|
|
10275
|
+
challengeState: "clear",
|
|
10396
10276
|
resolution: cleared ? "challenge_cleared" : "no_challenge",
|
|
10397
10277
|
verification: "verified",
|
|
10398
10278
|
workflow: "verified",
|
|
10399
10279
|
pageId: state.id,
|
|
10280
|
+
...attempts2 > 0 ? { attempts: attempts2 } : {},
|
|
10400
10281
|
classification: detection
|
|
10401
10282
|
};
|
|
10402
10283
|
}
|
|
10403
10284
|
state.challengeStatus = "present";
|
|
10285
|
+
const attempts = (state.challengeAttempts ?? 0) + 1;
|
|
10286
|
+
state.challengeAttempts = attempts;
|
|
10287
|
+
if (attempts > maxAttempts) {
|
|
10288
|
+
return {
|
|
10289
|
+
solved: false,
|
|
10290
|
+
verified: true,
|
|
10291
|
+
challengeState: "present",
|
|
10292
|
+
resolution: "automation_exhausted",
|
|
10293
|
+
verification: "challenge_present",
|
|
10294
|
+
workflow: "human_handoff_available",
|
|
10295
|
+
pageId: state.id,
|
|
10296
|
+
attempts: attempts - 1,
|
|
10297
|
+
maxAttempts,
|
|
10298
|
+
attemptsRemaining: 0,
|
|
10299
|
+
classification: detection,
|
|
10300
|
+
nextAction: "Automation attempts are exhausted. Human handoff is available if the site permits it.",
|
|
10301
|
+
guidance: "Automation attempts are exhausted. Human handoff is available if the site permits it."
|
|
10302
|
+
};
|
|
10303
|
+
}
|
|
10404
10304
|
const includeScreenshot = action.includeScreenshot ?? action.include_screenshot ?? true;
|
|
10405
10305
|
const requestedMaxDimension = action.maxDimension ?? action.max_dim;
|
|
10406
10306
|
const maxDimension = Number.isFinite(requestedMaxDimension) ? Math.min(1600, Math.max(100, Math.floor(requestedMaxDimension))) : 1600;
|
|
@@ -10430,6 +10330,9 @@ var BrowserService = class {
|
|
|
10430
10330
|
snapshotId: snapshot.snapshotId,
|
|
10431
10331
|
domRevision: snapshot.domRevision,
|
|
10432
10332
|
viewport: snapshot.viewport,
|
|
10333
|
+
attempts,
|
|
10334
|
+
maxAttempts,
|
|
10335
|
+
attemptsRemaining: Math.max(0, maxAttempts - attempts),
|
|
10433
10336
|
refs: stableRefs,
|
|
10434
10337
|
interactive: stableRefs,
|
|
10435
10338
|
classification: detection,
|
|
@@ -12166,7 +12069,7 @@ var ServerRuntime = class _ServerRuntime {
|
|
|
12166
12069
|
headedBrowser: true,
|
|
12167
12070
|
pageEvaluation: true,
|
|
12168
12071
|
stealth: true,
|
|
12169
|
-
behavioralTiming:
|
|
12072
|
+
behavioralTiming: false
|
|
12170
12073
|
},
|
|
12171
12074
|
features: {
|
|
12172
12075
|
localBrowserTools: "available",
|