@browserbasehq/orca 3.5.0-preview.1 → 3.5.1-preview.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/README.md +7 -3
- package/dist/cjs/lib/utils.d.ts +2 -0
- package/dist/cjs/lib/utils.js +20 -0
- package/dist/cjs/lib/utils.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AgentProvider.js +1 -0
- package/dist/cjs/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +1 -0
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/api.js +28 -33
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.d.ts +24 -24
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js +24 -24
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/locatorScripts/xpathResolver.js +79 -10
- package/dist/cjs/lib/v3/dom/locatorScripts/xpathResolver.js.map +1 -1
- package/dist/cjs/lib/v3/index.d.ts +2 -1
- package/dist/cjs/lib/v3/llm/LLMProvider.js +18 -2
- package/dist/cjs/lib/v3/llm/LLMProvider.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/agent.d.ts +2 -2
- package/dist/cjs/lib/v3/types/public/agent.js +1 -0
- package/dist/cjs/lib/v3/types/public/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/api.d.ts +538 -27
- package/dist/cjs/lib/v3/types/public/api.js +83 -7
- package/dist/cjs/lib/v3/types/public/api.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/clipboard.d.ts +15 -0
- package/dist/cjs/lib/v3/types/public/clipboard.js +3 -0
- package/dist/cjs/lib/v3/types/public/clipboard.js.map +1 -0
- package/dist/cjs/lib/v3/types/public/index.d.ts +1 -0
- package/dist/cjs/lib/v3/types/public/index.js +1 -0
- package/dist/cjs/lib/v3/types/public/index.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/model.d.ts +18 -3
- package/dist/cjs/lib/v3/types/public/model.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/page.d.ts +29 -0
- package/dist/cjs/lib/v3/types/public/page.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/sdkErrors.d.ts +3 -0
- package/dist/cjs/lib/v3/types/public/sdkErrors.js +8 -2
- package/dist/cjs/lib/v3/types/public/sdkErrors.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/clipboard.d.ts +24 -0
- package/dist/cjs/lib/v3/understudy/clipboard.js +166 -0
- package/dist/cjs/lib/v3/understudy/clipboard.js.map +1 -0
- package/dist/cjs/lib/v3/understudy/context.d.ts +3 -0
- package/dist/cjs/lib/v3/understudy/context.js +15 -0
- package/dist/cjs/lib/v3/understudy/context.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/page.d.ts +23 -1
- package/dist/cjs/lib/v3/understudy/page.js +283 -0
- package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
- package/dist/cjs/lib/v3/v3.js +15 -6
- package/dist/cjs/lib/v3/v3.js.map +1 -1
- package/dist/cjs/lib/version.d.ts +1 -1
- package/dist/cjs/lib/version.js +1 -1
- package/dist/cjs/lib/version.js.map +1 -1
- package/dist/esm/lib/utils.d.ts +2 -0
- package/dist/esm/lib/utils.js +18 -0
- package/dist/esm/lib/utils.js.map +1 -1
- package/dist/esm/lib/v3/agent/AgentProvider.js +1 -0
- package/dist/esm/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +1 -0
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/api.js +29 -34
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.d.ts +24 -24
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js +24 -24
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/locatorScripts/xpathResolver.js +79 -10
- package/dist/esm/lib/v3/dom/locatorScripts/xpathResolver.js.map +1 -1
- package/dist/esm/lib/v3/index.d.ts +2 -1
- package/dist/esm/lib/v3/llm/LLMProvider.js +18 -2
- package/dist/esm/lib/v3/llm/LLMProvider.js.map +1 -1
- package/dist/esm/lib/v3/types/public/agent.d.ts +2 -2
- package/dist/esm/lib/v3/types/public/agent.js +1 -0
- package/dist/esm/lib/v3/types/public/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/public/api.d.ts +538 -27
- package/dist/esm/lib/v3/types/public/api.js +81 -5
- package/dist/esm/lib/v3/types/public/api.js.map +1 -1
- package/dist/esm/lib/v3/types/public/clipboard.d.ts +15 -0
- package/dist/esm/lib/v3/types/public/clipboard.js +2 -0
- package/dist/esm/lib/v3/types/public/clipboard.js.map +1 -0
- package/dist/esm/lib/v3/types/public/index.d.ts +1 -0
- package/dist/esm/lib/v3/types/public/index.js +1 -0
- package/dist/esm/lib/v3/types/public/index.js.map +1 -1
- package/dist/esm/lib/v3/types/public/model.d.ts +18 -3
- package/dist/esm/lib/v3/types/public/model.js.map +1 -1
- package/dist/esm/lib/v3/types/public/page.d.ts +29 -0
- package/dist/esm/lib/v3/types/public/page.js.map +1 -1
- package/dist/esm/lib/v3/types/public/sdkErrors.d.ts +3 -0
- package/dist/esm/lib/v3/types/public/sdkErrors.js +5 -0
- package/dist/esm/lib/v3/types/public/sdkErrors.js.map +1 -1
- package/dist/esm/lib/v3/understudy/clipboard.d.ts +24 -0
- package/dist/esm/lib/v3/understudy/clipboard.js +163 -0
- package/dist/esm/lib/v3/understudy/clipboard.js.map +1 -0
- package/dist/esm/lib/v3/understudy/context.d.ts +3 -0
- package/dist/esm/lib/v3/understudy/context.js +15 -0
- package/dist/esm/lib/v3/understudy/context.js.map +1 -1
- package/dist/esm/lib/v3/understudy/page.d.ts +23 -1
- package/dist/esm/lib/v3/understudy/page.js +284 -1
- package/dist/esm/lib/v3/understudy/page.js.map +1 -1
- package/dist/esm/lib/v3/v3.js +16 -7
- package/dist/esm/lib/v3/v3.js.map +1 -1
- package/dist/esm/lib/version.d.ts +1 -1
- package/dist/esm/lib/version.js +1 -1
- package/dist/esm/lib/version.js.map +1 -1
- package/package.json +13 -7
|
@@ -47,7 +47,7 @@ import { NavigationResponseTracker } from "./navigationResponseTracker.js";
|
|
|
47
47
|
import { Response, isSerializableResponse } from "./response.js";
|
|
48
48
|
import { ConsoleMessage } from "./consoleMessage.js";
|
|
49
49
|
import { StagehandSetExtraHTTPHeadersError, StagehandSnapshotError, } from "../types/public/index.js";
|
|
50
|
-
import { StagehandInvalidArgumentError, StagehandEvalError, } from "../types/public/sdkErrors.js";
|
|
50
|
+
import { StagehandInvalidArgumentError, StagehandEvalError, StagehandUnsupportedBrowserFeatureError, } from "../types/public/sdkErrors.js";
|
|
51
51
|
import { normalizeInitScriptSource } from "./initScripts.js";
|
|
52
52
|
import { buildLocatorInvocation } from "./locatorInvocation.js";
|
|
53
53
|
import { applyMaskOverlays, applyStyleToFrames, collectFramesForScreenshot, computeScreenshotScale, disableAnimations, hideCaret, normalizeScreenshotClip, runScreenshotCleanups, setTransparentBackground, } from "./screenshotUtils.js";
|
|
@@ -70,8 +70,49 @@ const LIFECYCLE_NAME = {
|
|
|
70
70
|
domcontentloaded: "DOMContentLoaded",
|
|
71
71
|
networkidle: "networkIdle",
|
|
72
72
|
};
|
|
73
|
+
const WEB_MCP_SUPPORT_MESSAGE = "Make sure you are using Chrome/Chromium newer than version 149 and that it is launched with --enable-features=WebMCPTesting,DevToolsWebMCPSupport.";
|
|
74
|
+
function createDeferred() {
|
|
75
|
+
let resolve;
|
|
76
|
+
let reject;
|
|
77
|
+
const promise = new Promise((res, rej) => {
|
|
78
|
+
resolve = res;
|
|
79
|
+
reject = rej;
|
|
80
|
+
});
|
|
81
|
+
return { promise, resolve, reject };
|
|
82
|
+
}
|
|
83
|
+
function stripWebMCPToolDebugFields(tool) {
|
|
84
|
+
const { name, description, inputSchema, annotations, frameId } = tool;
|
|
85
|
+
const publicAnnotations = annotations === undefined ? undefined : { ...annotations };
|
|
86
|
+
return {
|
|
87
|
+
name,
|
|
88
|
+
...(description !== undefined && { description }),
|
|
89
|
+
...(inputSchema !== undefined && { inputSchema }),
|
|
90
|
+
...(publicAnnotations !== undefined && { annotations: publicAnnotations }),
|
|
91
|
+
frameId,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function webMCPResultFromEvent(invocationId, event) {
|
|
95
|
+
return {
|
|
96
|
+
invocationId,
|
|
97
|
+
status: event.status ?? "Error",
|
|
98
|
+
...(event.output !== undefined && { output: event.output }),
|
|
99
|
+
...(event.errorText !== undefined && { errorText: event.errorText }),
|
|
100
|
+
...(event.exception !== undefined && { exception: event.exception }),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function isWebMCPUnsupportedBrowserError(error) {
|
|
104
|
+
if (error instanceof StagehandUnsupportedBrowserFeatureError)
|
|
105
|
+
return true;
|
|
106
|
+
const code = error?.code;
|
|
107
|
+
if (code === -32601)
|
|
108
|
+
return true;
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return /method not found/i.test(message);
|
|
111
|
+
}
|
|
73
112
|
let Page = (() => {
|
|
74
113
|
let _instanceExtraInitializers = [];
|
|
114
|
+
let _listWebMCPTools_decorators;
|
|
115
|
+
let _invokeWebMCPTool_decorators;
|
|
75
116
|
let _close_decorators;
|
|
76
117
|
let _goto_decorators;
|
|
77
118
|
let _reload_decorators;
|
|
@@ -91,6 +132,8 @@ let Page = (() => {
|
|
|
91
132
|
return class Page {
|
|
92
133
|
static {
|
|
93
134
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
135
|
+
_listWebMCPTools_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageListWebMCPTools" })];
|
|
136
|
+
_invokeWebMCPTool_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageInvokeWebMCPTool" })];
|
|
94
137
|
_close_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageClose" })];
|
|
95
138
|
_goto_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageGoto" })];
|
|
96
139
|
_reload_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageReload" })];
|
|
@@ -117,6 +160,8 @@ let Page = (() => {
|
|
|
117
160
|
_type_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageType" })];
|
|
118
161
|
_keyPress_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageKeyPress" })];
|
|
119
162
|
_snapshot_decorators = [FlowLogger.wrapWithLogging({ eventType: "PageSnapshot" })];
|
|
163
|
+
__esDecorate(this, null, _listWebMCPTools_decorators, { kind: "method", name: "listWebMCPTools", static: false, private: false, access: { has: obj => "listWebMCPTools" in obj, get: obj => obj.listWebMCPTools }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
164
|
+
__esDecorate(this, null, _invokeWebMCPTool_decorators, { kind: "method", name: "invokeWebMCPTool", static: false, private: false, access: { has: obj => "invokeWebMCPTool" in obj, get: obj => obj.invokeWebMCPTool }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
120
165
|
__esDecorate(this, null, _close_decorators, { kind: "method", name: "close", static: false, private: false, access: { has: obj => "close" in obj, get: obj => obj.close }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
121
166
|
__esDecorate(this, null, _goto_decorators, { kind: "method", name: "goto", static: false, private: false, access: { has: obj => "goto" in obj, get: obj => obj.goto }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
122
167
|
__esDecorate(this, null, _reload_decorators, { kind: "method", name: "reload", static: false, private: false, access: { has: obj => "reload" in obj, get: obj => obj.reload }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -161,6 +206,10 @@ let Page = (() => {
|
|
|
161
206
|
apiClient = null;
|
|
162
207
|
consoleListeners = new Set();
|
|
163
208
|
consoleHandlers = new Map();
|
|
209
|
+
webMCPEnablePromise = null;
|
|
210
|
+
pendingWebMCPInvocations = new Map();
|
|
211
|
+
bufferedWebMCPResults = new Map();
|
|
212
|
+
pendingWebMCPInvokeToolResponses = 0;
|
|
164
213
|
/** Document-start scripts installed across every session this page owns. */
|
|
165
214
|
initScripts = [];
|
|
166
215
|
extraHTTPHeaders;
|
|
@@ -514,6 +563,154 @@ let Page = (() => {
|
|
|
514
563
|
}
|
|
515
564
|
return this;
|
|
516
565
|
}
|
|
566
|
+
onWebMCPToolResponded = (event) => {
|
|
567
|
+
if (!event.invocationId)
|
|
568
|
+
return;
|
|
569
|
+
const result = webMCPResultFromEvent(event.invocationId, event);
|
|
570
|
+
const pending = this.pendingWebMCPInvocations.get(event.invocationId);
|
|
571
|
+
if (!pending) {
|
|
572
|
+
/*
|
|
573
|
+
* WebMCP.invokeTool returns the invocationId, while WebMCP.toolResponded
|
|
574
|
+
* is a separate event. Very fast tools can emit the event before the
|
|
575
|
+
* invokeTool command response reaches us, so there is not yet a pending
|
|
576
|
+
* invocation entry to resolve. Buffer only during that command-response
|
|
577
|
+
* window; otherwise unmatched events are stale or unexpected.
|
|
578
|
+
*/
|
|
579
|
+
if (this.pendingWebMCPInvokeToolResponses > 0) {
|
|
580
|
+
this.bufferedWebMCPResults.set(event.invocationId, result);
|
|
581
|
+
}
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
clearTimeout(pending.timer);
|
|
585
|
+
this.pendingWebMCPInvocations.delete(event.invocationId);
|
|
586
|
+
pending.deferred.resolve(result);
|
|
587
|
+
};
|
|
588
|
+
async ensureWebMCPEnabled() {
|
|
589
|
+
if (this.webMCPEnablePromise) {
|
|
590
|
+
return this.webMCPEnablePromise;
|
|
591
|
+
}
|
|
592
|
+
this.mainSession.on("WebMCP.toolResponded", this.onWebMCPToolResponded);
|
|
593
|
+
this.webMCPEnablePromise = this.mainSession
|
|
594
|
+
.send("WebMCP.enable")
|
|
595
|
+
.then(() => undefined)
|
|
596
|
+
.catch((error) => {
|
|
597
|
+
this.teardownWebMCP();
|
|
598
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
599
|
+
if (isWebMCPUnsupportedBrowserError(error)) {
|
|
600
|
+
throw new StagehandUnsupportedBrowserFeatureError("WebMCP", `Unable to enable WebMCP. ${WEB_MCP_SUPPORT_MESSAGE} CDP error: ${message}`, error);
|
|
601
|
+
}
|
|
602
|
+
throw error;
|
|
603
|
+
});
|
|
604
|
+
return this.webMCPEnablePromise;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* `WebMCP.enable` is the browser's source-of-truth snapshot trigger: CDP emits
|
|
608
|
+
* `WebMCP.toolsAdded` for all tools currently registered on the page. Keep
|
|
609
|
+
* this scoped to the list call so tools from old documents are not cached here.
|
|
610
|
+
*/
|
|
611
|
+
async collectWebMCPToolsSnapshot(timeoutMs) {
|
|
612
|
+
const quietWindowMs = Math.min(100, Math.max(0, timeoutMs));
|
|
613
|
+
const tools = new Map();
|
|
614
|
+
let toolsVersion = 0;
|
|
615
|
+
let toolsLastUpdatedAt = null;
|
|
616
|
+
const toolKey = (tool) => `${tool.frameId}:${tool.name}`;
|
|
617
|
+
const onToolsAdded = (event) => {
|
|
618
|
+
if (!Array.isArray(event.tools))
|
|
619
|
+
return;
|
|
620
|
+
let changed = false;
|
|
621
|
+
for (const tool of event.tools) {
|
|
622
|
+
const normalized = stripWebMCPToolDebugFields(tool);
|
|
623
|
+
tools.set(toolKey(normalized), normalized);
|
|
624
|
+
changed = true;
|
|
625
|
+
}
|
|
626
|
+
if (!changed)
|
|
627
|
+
return;
|
|
628
|
+
toolsVersion += 1;
|
|
629
|
+
toolsLastUpdatedAt = Date.now();
|
|
630
|
+
schedule?.();
|
|
631
|
+
};
|
|
632
|
+
const onToolsRemoved = (event) => {
|
|
633
|
+
if (!Array.isArray(event.tools))
|
|
634
|
+
return;
|
|
635
|
+
let changed = false;
|
|
636
|
+
for (const tool of event.tools) {
|
|
637
|
+
changed = tools.delete(toolKey(tool)) || changed;
|
|
638
|
+
}
|
|
639
|
+
if (!changed)
|
|
640
|
+
return;
|
|
641
|
+
toolsVersion += 1;
|
|
642
|
+
toolsLastUpdatedAt = Date.now();
|
|
643
|
+
schedule?.();
|
|
644
|
+
};
|
|
645
|
+
const deadline = Date.now() + timeoutMs;
|
|
646
|
+
let schedule = null;
|
|
647
|
+
this.mainSession.on("WebMCP.toolsAdded", onToolsAdded);
|
|
648
|
+
this.mainSession.on("WebMCP.toolsRemoved", onToolsRemoved);
|
|
649
|
+
try {
|
|
650
|
+
await this.mainSession.send("WebMCP.enable");
|
|
651
|
+
if (quietWindowMs === 0)
|
|
652
|
+
return [...tools.values()];
|
|
653
|
+
await new Promise((resolve) => {
|
|
654
|
+
const startVersion = toolsVersion;
|
|
655
|
+
let quietTimer = null;
|
|
656
|
+
let timeoutTimer = null;
|
|
657
|
+
let done = false;
|
|
658
|
+
const cleanup = () => {
|
|
659
|
+
if (done)
|
|
660
|
+
return;
|
|
661
|
+
done = true;
|
|
662
|
+
if (quietTimer)
|
|
663
|
+
clearTimeout(quietTimer);
|
|
664
|
+
if (timeoutTimer)
|
|
665
|
+
clearTimeout(timeoutTimer);
|
|
666
|
+
resolve();
|
|
667
|
+
};
|
|
668
|
+
schedule = () => {
|
|
669
|
+
if (done)
|
|
670
|
+
return;
|
|
671
|
+
if (quietTimer) {
|
|
672
|
+
clearTimeout(quietTimer);
|
|
673
|
+
quietTimer = null;
|
|
674
|
+
}
|
|
675
|
+
const now = Date.now();
|
|
676
|
+
if (now >= deadline) {
|
|
677
|
+
cleanup();
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const sawToolsSinceStart = toolsVersion > startVersion;
|
|
681
|
+
const lastUpdate = toolsLastUpdatedAt;
|
|
682
|
+
const quietRemaining = sawToolsSinceStart && lastUpdate !== null
|
|
683
|
+
? Math.max(0, quietWindowMs - (now - lastUpdate))
|
|
684
|
+
: quietWindowMs;
|
|
685
|
+
quietTimer = setTimeout(cleanup, Math.min(quietRemaining, deadline - now));
|
|
686
|
+
};
|
|
687
|
+
timeoutTimer = setTimeout(cleanup, timeoutMs);
|
|
688
|
+
schedule();
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
finally {
|
|
692
|
+
this.mainSession.off("WebMCP.toolsAdded", onToolsAdded);
|
|
693
|
+
this.mainSession.off("WebMCP.toolsRemoved", onToolsRemoved);
|
|
694
|
+
}
|
|
695
|
+
return [...tools.values()];
|
|
696
|
+
}
|
|
697
|
+
cleanupWebMCPInvocation(invocationId) {
|
|
698
|
+
const pending = this.pendingWebMCPInvocations.get(invocationId);
|
|
699
|
+
if (!pending)
|
|
700
|
+
return;
|
|
701
|
+
clearTimeout(pending.timer);
|
|
702
|
+
this.pendingWebMCPInvocations.delete(invocationId);
|
|
703
|
+
}
|
|
704
|
+
teardownWebMCP() {
|
|
705
|
+
this.mainSession.off("WebMCP.toolResponded", this.onWebMCPToolResponded);
|
|
706
|
+
for (const [invocationId, pending] of this.pendingWebMCPInvocations) {
|
|
707
|
+
clearTimeout(pending.timer);
|
|
708
|
+
pending.deferred.reject(new StagehandInvalidArgumentError(`WebMCP invocation "${invocationId}" was disposed before it completed.`));
|
|
709
|
+
}
|
|
710
|
+
this.pendingWebMCPInvocations.clear();
|
|
711
|
+
this.bufferedWebMCPResults.clear();
|
|
712
|
+
this.webMCPEnablePromise = null;
|
|
713
|
+
}
|
|
517
714
|
// ---------------- MAIN APIs ----------------
|
|
518
715
|
targetId() {
|
|
519
716
|
return this._targetId;
|
|
@@ -540,6 +737,90 @@ let Page = (() => {
|
|
|
540
737
|
sendCDP(method, params) {
|
|
541
738
|
return this.mainSession.send(method, params);
|
|
542
739
|
}
|
|
740
|
+
/**
|
|
741
|
+
* List WebMCP tools registered by the current page.
|
|
742
|
+
* CDP emits WebMCP.toolsAdded for all currently registered tools each time
|
|
743
|
+
* WebMCP.enable is called, so this method treats the browser as the source of
|
|
744
|
+
* truth and collects that per-call snapshot instead of keeping a tool cache.
|
|
745
|
+
*/
|
|
746
|
+
async listWebMCPTools(options) {
|
|
747
|
+
const timeoutMs = options?.timeoutMs ?? 1_000;
|
|
748
|
+
try {
|
|
749
|
+
return await this.collectWebMCPToolsSnapshot(timeoutMs);
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
if (error instanceof StagehandUnsupportedBrowserFeatureError)
|
|
753
|
+
throw error;
|
|
754
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
755
|
+
if (isWebMCPUnsupportedBrowserError(error)) {
|
|
756
|
+
throw new StagehandUnsupportedBrowserFeatureError("WebMCP", `Unable to list WebMCP tools. ${WEB_MCP_SUPPORT_MESSAGE} CDP error: ${message}`, error);
|
|
757
|
+
}
|
|
758
|
+
throw error;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async invokeWebMCPTool(toolName, input, options) {
|
|
762
|
+
const timeoutMs = options?.timeoutMs ?? 30_000;
|
|
763
|
+
let frameId = options?.frameId;
|
|
764
|
+
if (!frameId) {
|
|
765
|
+
const matchingTools = (await this.listWebMCPTools()).filter((tool) => tool.name === toolName);
|
|
766
|
+
if (matchingTools.length === 0) {
|
|
767
|
+
throw new StagehandInvalidArgumentError(`Unable to invoke WebMCP tool "${toolName}" because it was not found on the current page.`);
|
|
768
|
+
}
|
|
769
|
+
if (matchingTools.length > 1) {
|
|
770
|
+
throw new StagehandInvalidArgumentError(`Unable to invoke WebMCP tool "${toolName}" because multiple frames registered a tool with that name. Pass options.frameId to disambiguate.`);
|
|
771
|
+
}
|
|
772
|
+
frameId = matchingTools[0].frameId;
|
|
773
|
+
}
|
|
774
|
+
const deferred = createDeferred();
|
|
775
|
+
let invocationId;
|
|
776
|
+
await this.ensureWebMCPEnabled();
|
|
777
|
+
try {
|
|
778
|
+
this.pendingWebMCPInvokeToolResponses += 1;
|
|
779
|
+
const response = await this.mainSession
|
|
780
|
+
.send("WebMCP.invokeTool", {
|
|
781
|
+
frameId,
|
|
782
|
+
toolName,
|
|
783
|
+
input,
|
|
784
|
+
})
|
|
785
|
+
.finally(() => {
|
|
786
|
+
this.pendingWebMCPInvokeToolResponses -= 1;
|
|
787
|
+
});
|
|
788
|
+
invocationId = response.invocationId;
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
792
|
+
if (isWebMCPUnsupportedBrowserError(error)) {
|
|
793
|
+
throw new StagehandUnsupportedBrowserFeatureError("WebMCP", `Unable to invoke WebMCP tool "${toolName}". ${WEB_MCP_SUPPORT_MESSAGE} CDP error: ${message}`, error);
|
|
794
|
+
}
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
const bufferedResult = this.bufferedWebMCPResults.get(invocationId);
|
|
798
|
+
if (bufferedResult) {
|
|
799
|
+
this.bufferedWebMCPResults.delete(invocationId);
|
|
800
|
+
deferred.resolve(bufferedResult);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
const timer = setTimeout(() => {
|
|
804
|
+
this.cleanupWebMCPInvocation(invocationId);
|
|
805
|
+
deferred.reject(new StagehandInvalidArgumentError(`Timed out waiting for WebMCP tool "${toolName}" invocation result after ${timeoutMs}ms.`));
|
|
806
|
+
}, timeoutMs);
|
|
807
|
+
this.pendingWebMCPInvocations.set(invocationId, {
|
|
808
|
+
deferred,
|
|
809
|
+
timer,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
invocationId,
|
|
814
|
+
toolName,
|
|
815
|
+
frameId,
|
|
816
|
+
result: deferred.promise,
|
|
817
|
+
cancel: async () => {
|
|
818
|
+
await this.mainSession.send("WebMCP.cancelInvocation", {
|
|
819
|
+
invocationId,
|
|
820
|
+
});
|
|
821
|
+
},
|
|
822
|
+
};
|
|
823
|
+
}
|
|
543
824
|
/** Seed the cached URL before navigation events converge. */
|
|
544
825
|
seedCurrentUrl(url) {
|
|
545
826
|
if (!url)
|
|
@@ -576,6 +857,7 @@ let Page = (() => {
|
|
|
576
857
|
const targets = await this.conn.getTargets();
|
|
577
858
|
if (!targets.some((t) => t.targetId === this._targetId)) {
|
|
578
859
|
this.networkManager.dispose();
|
|
860
|
+
this.teardownWebMCP();
|
|
579
861
|
return;
|
|
580
862
|
}
|
|
581
863
|
}
|
|
@@ -585,6 +867,7 @@ let Page = (() => {
|
|
|
585
867
|
await new Promise((r) => setTimeout(r, 25));
|
|
586
868
|
}
|
|
587
869
|
this.networkManager.dispose();
|
|
870
|
+
this.teardownWebMCP();
|
|
588
871
|
this.removeAllConsoleTaps();
|
|
589
872
|
this.consoleListeners.clear();
|
|
590
873
|
}
|