@browserbasehq/orca 3.2.1-preview.2 → 3.4.0-preview-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/dist/cjs/lib/inference.js +2 -9
- package/dist/cjs/lib/inference.js.map +1 -1
- package/dist/cjs/lib/prompt.js +3 -1
- package/dist/cjs/lib/prompt.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/cjs/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js +16 -12
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/cjs/lib/v3/agent/utils/validateExperimentalFeatures.js +0 -4
- package/dist/cjs/lib/v3/agent/utils/validateExperimentalFeatures.js.map +1 -1
- package/dist/cjs/lib/v3/api.d.ts +1 -0
- package/dist/cjs/lib/v3/api.js +15 -3
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/a11yScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/a11yScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.d.ts +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.js +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/screenshotScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/screenshotScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.d.ts +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.js +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/selectorRuntime.generated.d.ts +24 -0
- package/dist/cjs/lib/v3/dom/build/selectorRuntime.generated.js +31 -0
- package/dist/cjs/lib/v3/dom/build/selectorRuntime.generated.js.map +1 -0
- package/dist/cjs/lib/v3/handlers/extractHandler.js +3 -1
- package/dist/cjs/lib/v3/handlers/extractHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/observeHandler.js +2 -1
- package/dist/cjs/lib/v3/handlers/observeHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +3 -5
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/index.d.ts +1 -1
- package/dist/cjs/lib/v3/llm/CerebrasClient.js +1 -1
- package/dist/cjs/lib/v3/llm/CerebrasClient.js.map +1 -1
- package/dist/cjs/lib/v3/llm/GroqClient.js +1 -1
- package/dist/cjs/lib/v3/llm/GroqClient.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/agent.d.ts +5 -0
- package/dist/cjs/lib/v3/types/private/agent.js +11 -0
- package/dist/cjs/lib/v3/types/private/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/handlers.d.ts +2 -0
- package/dist/cjs/lib/v3/types/private/handlers.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/snapshot.d.ts +8 -0
- package/dist/cjs/lib/v3/types/private/snapshot.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/agent.d.ts +2 -3
- package/dist/cjs/lib/v3/types/public/agent.js +3 -0
- package/dist/cjs/lib/v3/types/public/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/api.d.ts +7 -1
- package/dist/cjs/lib/v3/types/public/api.js +22 -2
- package/dist/cjs/lib/v3/types/public/api.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/methods.d.ts +2 -0
- package/dist/cjs/lib/v3/types/public/methods.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/a11yTree.js +21 -12
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/a11yTree.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.d.ts +11 -2
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js +268 -21
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/domTree.js +60 -7
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/domTree.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/frameRegistry.js +16 -5
- package/dist/cjs/lib/v3/understudy/frameRegistry.js.map +1 -1
- package/dist/cjs/lib/v3/v3.d.ts +1 -0
- package/dist/cjs/lib/v3/v3.js +18 -14
- 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/cjs/tests/integration/observe-element-id-format.spec.js +130 -0
- package/dist/cjs/tests/integration/observe-element-id-format.spec.js.map +1 -0
- package/dist/cjs/tests/unit/agent-mode-routing.test.js +88 -0
- package/dist/cjs/tests/unit/agent-mode-routing.test.js.map +1 -0
- package/dist/cjs/tests/unit/agent-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/agent-temperature.test.js +191 -0
- package/dist/cjs/tests/unit/agent-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.d.ts +1 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.js +43 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.js.map +1 -0
- package/dist/cjs/tests/unit/api-client-observe-variables.test.js +49 -0
- package/dist/cjs/tests/unit/api-client-observe-variables.test.js.map +1 -1
- package/dist/cjs/tests/unit/api-optional-model-api-key.test.js +60 -0
- package/dist/cjs/tests/unit/api-optional-model-api-key.test.js.map +1 -1
- package/dist/cjs/tests/unit/api-variables-schema.test.js +32 -0
- package/dist/cjs/tests/unit/api-variables-schema.test.js.map +1 -1
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.d.ts +1 -0
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.js +60 -0
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.js.map +1 -0
- package/dist/cjs/tests/unit/inference-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/inference-temperature.test.js +65 -0
- package/dist/cjs/tests/unit/inference-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.js +84 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/prompt-observe-variables.test.js +6 -0
- package/dist/cjs/tests/unit/prompt-observe-variables.test.js.map +1 -1
- package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js +3 -0
- package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
- package/dist/cjs/tests/unit/public-api/public-types.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-a11y-resolvers.test.js +106 -5
- package/dist/cjs/tests/unit/snapshot-a11y-resolvers.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-a11y-tree-utils.test.js +20 -0
- package/dist/cjs/tests/unit/snapshot-a11y-tree-utils.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-capture-orchestration.test.js +119 -9
- package/dist/cjs/tests/unit/snapshot-capture-orchestration.test.js.map +1 -1
- package/dist/cjs/tests/unit/timeout-handlers.test.js +36 -0
- package/dist/cjs/tests/unit/timeout-handlers.test.js.map +1 -1
- package/dist/esm/lib/inference.js +2 -9
- package/dist/esm/lib/inference.js.map +1 -1
- package/dist/esm/lib/prompt.js +3 -1
- package/dist/esm/lib/prompt.js.map +1 -1
- package/dist/esm/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/esm/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js +16 -12
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/esm/lib/v3/agent/utils/validateExperimentalFeatures.js +0 -4
- package/dist/esm/lib/v3/agent/utils/validateExperimentalFeatures.js.map +1 -1
- package/dist/esm/lib/v3/api.d.ts +1 -0
- package/dist/esm/lib/v3/api.js +15 -3
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/a11yScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/a11yScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.d.ts +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.js +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/screenshotScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/screenshotScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.d.ts +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.js +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/selectorRuntime.generated.d.ts +24 -0
- package/dist/esm/lib/v3/dom/build/selectorRuntime.generated.js +28 -0
- package/dist/esm/lib/v3/dom/build/selectorRuntime.generated.js.map +1 -0
- package/dist/esm/lib/v3/handlers/extractHandler.js +3 -1
- package/dist/esm/lib/v3/handlers/extractHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/observeHandler.js +2 -1
- package/dist/esm/lib/v3/handlers/observeHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js +3 -5
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/index.d.ts +1 -1
- package/dist/esm/lib/v3/llm/CerebrasClient.js +1 -1
- package/dist/esm/lib/v3/llm/CerebrasClient.js.map +1 -1
- package/dist/esm/lib/v3/llm/GroqClient.js +1 -1
- package/dist/esm/lib/v3/llm/GroqClient.js.map +1 -1
- package/dist/esm/lib/v3/types/private/agent.d.ts +5 -0
- package/dist/esm/lib/v3/types/private/agent.js +10 -1
- package/dist/esm/lib/v3/types/private/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/private/handlers.d.ts +2 -0
- package/dist/esm/lib/v3/types/private/handlers.js.map +1 -1
- package/dist/esm/lib/v3/types/private/snapshot.d.ts +8 -0
- package/dist/esm/lib/v3/types/private/snapshot.js.map +1 -1
- package/dist/esm/lib/v3/types/public/agent.d.ts +2 -3
- package/dist/esm/lib/v3/types/public/agent.js +3 -0
- package/dist/esm/lib/v3/types/public/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/public/api.d.ts +7 -1
- package/dist/esm/lib/v3/types/public/api.js +22 -2
- package/dist/esm/lib/v3/types/public/api.js.map +1 -1
- package/dist/esm/lib/v3/types/public/methods.d.ts +2 -0
- package/dist/esm/lib/v3/types/public/methods.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/a11yTree.js +21 -12
- package/dist/esm/lib/v3/understudy/a11y/snapshot/a11yTree.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.d.ts +11 -2
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js +267 -22
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/domTree.js +60 -7
- package/dist/esm/lib/v3/understudy/a11y/snapshot/domTree.js.map +1 -1
- package/dist/esm/lib/v3/understudy/frameRegistry.js +16 -5
- package/dist/esm/lib/v3/understudy/frameRegistry.js.map +1 -1
- package/dist/esm/lib/v3/v3.d.ts +1 -0
- package/dist/esm/lib/v3/v3.js +18 -14
- 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/dist/esm/tests/integration/observe-element-id-format.spec.d.ts +1 -0
- package/dist/esm/tests/integration/observe-element-id-format.spec.js +128 -0
- package/dist/esm/tests/integration/observe-element-id-format.spec.js.map +1 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.js +86 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.js.map +1 -0
- package/dist/esm/tests/unit/agent-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-temperature.test.js +189 -0
- package/dist/esm/tests/unit/agent-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.js +41 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.js.map +1 -0
- package/dist/esm/tests/unit/api-client-observe-variables.test.js +49 -0
- package/dist/esm/tests/unit/api-client-observe-variables.test.js.map +1 -1
- package/dist/esm/tests/unit/api-optional-model-api-key.test.js +60 -0
- package/dist/esm/tests/unit/api-optional-model-api-key.test.js.map +1 -1
- package/dist/esm/tests/unit/api-variables-schema.test.js +32 -0
- package/dist/esm/tests/unit/api-variables-schema.test.js.map +1 -1
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.d.ts +1 -0
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.js +58 -0
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.js.map +1 -0
- package/dist/esm/tests/unit/inference-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/inference-temperature.test.js +63 -0
- package/dist/esm/tests/unit/inference-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.js +82 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/prompt-observe-variables.test.js +6 -0
- package/dist/esm/tests/unit/prompt-observe-variables.test.js.map +1 -1
- package/dist/esm/tests/unit/public-api/llm-and-agents.test.js +3 -0
- package/dist/esm/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
- package/dist/esm/tests/unit/public-api/public-types.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-a11y-resolvers.test.js +106 -5
- package/dist/esm/tests/unit/snapshot-a11y-resolvers.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-a11y-tree-utils.test.js +20 -0
- package/dist/esm/tests/unit/snapshot-a11y-tree-utils.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-capture-orchestration.test.js +119 -9
- package/dist/esm/tests/unit/snapshot-capture-orchestration.test.js.map +1 -1
- package/dist/esm/tests/unit/timeout-handlers.test.js +36 -0
- package/dist/esm/tests/unit/timeout-handlers.test.js.map +1 -1
- package/package.json +3 -3
- package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js +0 -56
- package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js.map +0 -1
- package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js +0 -54
- package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js.map +0 -1
- /package/dist/cjs/tests/integration/{agent-captcha-autosolve.spec.d.ts → observe-element-id-format.spec.d.ts} +0 -0
- /package/dist/{esm/tests/integration/agent-captcha-autosolve.spec.d.ts → cjs/tests/unit/agent-mode-routing.test.d.ts} +0 -0
package/dist/esm/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../lib/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * AUTO-GENERATED — DO NOT EDIT BY HAND\n * Run `pnpm run gen-version` to refresh.\n */\nexport const STAGEHAND_VERSION = \"3.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../lib/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * AUTO-GENERATED — DO NOT EDIT BY HAND\n * Run `pnpm run gen-version` to refresh.\n */\nexport const STAGEHAND_VERSION = \"3.3.0\" as const;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { V3 } from "../../lib/v3/v3.js";
|
|
3
|
+
import { getV3TestConfig } from "./v3.config.js";
|
|
4
|
+
import { closeV3, createScriptedAisdkTestLlmClient, findLastEncodedId, promptToText, } from "./testUtils.js";
|
|
5
|
+
const encodedIdPattern = /^\d+-\d+$/;
|
|
6
|
+
const mainFrameEncodedIdPattern = /^0-\d+$/;
|
|
7
|
+
function encodeHtml(html) {
|
|
8
|
+
return `data:text/html,${encodeURIComponent(html)}`;
|
|
9
|
+
}
|
|
10
|
+
const filler = Array.from({ length: 80 }, (_, index) => `<span hidden data-filler="${index}">filler ${index}</span>`).join("");
|
|
11
|
+
const cases = [
|
|
12
|
+
{
|
|
13
|
+
name: "button after hidden filler nodes",
|
|
14
|
+
instruction: "Find the Target Checkout button",
|
|
15
|
+
targetText: "Target Checkout",
|
|
16
|
+
marker: "checkout",
|
|
17
|
+
html: `
|
|
18
|
+
<!doctype html>
|
|
19
|
+
<html>
|
|
20
|
+
<body>
|
|
21
|
+
${filler}
|
|
22
|
+
<main>
|
|
23
|
+
<button onclick="document.body.dataset.clicked = 'checkout'">
|
|
24
|
+
Target Checkout
|
|
25
|
+
</button>
|
|
26
|
+
</main>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
29
|
+
`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "navigation link",
|
|
33
|
+
instruction: "Find the Pricing Plans link",
|
|
34
|
+
targetText: "Pricing Plans",
|
|
35
|
+
marker: "pricing",
|
|
36
|
+
html: `
|
|
37
|
+
<!doctype html>
|
|
38
|
+
<html>
|
|
39
|
+
<body>
|
|
40
|
+
<nav>
|
|
41
|
+
<a href="#pricing" onclick="document.body.dataset.clicked = 'pricing'">
|
|
42
|
+
Pricing Plans
|
|
43
|
+
</a>
|
|
44
|
+
</nav>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
47
|
+
`,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "form input",
|
|
51
|
+
instruction: "Find the Company Email input",
|
|
52
|
+
targetText: "Company Email",
|
|
53
|
+
marker: "email",
|
|
54
|
+
html: `
|
|
55
|
+
<!doctype html>
|
|
56
|
+
<html>
|
|
57
|
+
<body>
|
|
58
|
+
<form>
|
|
59
|
+
<label>
|
|
60
|
+
Company Email
|
|
61
|
+
<input
|
|
62
|
+
type="email"
|
|
63
|
+
onclick="document.body.dataset.clicked = 'email'"
|
|
64
|
+
/>
|
|
65
|
+
</label>
|
|
66
|
+
</form>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
69
|
+
`,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
function observeResponseForTarget(testCase, onElementId) {
|
|
73
|
+
return (options) => {
|
|
74
|
+
const promptText = promptToText(options.prompt);
|
|
75
|
+
expect(promptText).toContain("Always copy the complete ID exactly as shown inside the brackets into elementId");
|
|
76
|
+
expect(promptText).toContain('return elementId "0-18372"');
|
|
77
|
+
expect(promptText).toContain(testCase.targetText);
|
|
78
|
+
const elementId = findLastEncodedId(options);
|
|
79
|
+
expect(elementId).toMatch(encodedIdPattern);
|
|
80
|
+
expect(elementId).toMatch(mainFrameEncodedIdPattern);
|
|
81
|
+
onElementId(elementId, options);
|
|
82
|
+
return {
|
|
83
|
+
elements: [
|
|
84
|
+
{
|
|
85
|
+
elementId,
|
|
86
|
+
description: testCase.targetText,
|
|
87
|
+
method: "click",
|
|
88
|
+
arguments: [],
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
test.describe("observe main frame element IDs", () => {
|
|
95
|
+
for (const testCase of cases) {
|
|
96
|
+
test(`keeps complete 0-ordinal element IDs for ${testCase.name}`, async () => {
|
|
97
|
+
let observedElementId;
|
|
98
|
+
const llmClient = createScriptedAisdkTestLlmClient({
|
|
99
|
+
modelId: "mock/observe-main-frame-element-id-format",
|
|
100
|
+
jsonResponses: {
|
|
101
|
+
Observation: observeResponseForTarget(testCase, (elementId) => {
|
|
102
|
+
observedElementId = elementId;
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
const v3 = new V3(getV3TestConfig({
|
|
107
|
+
llmClient,
|
|
108
|
+
}));
|
|
109
|
+
await v3.init();
|
|
110
|
+
try {
|
|
111
|
+
const page = v3.context.pages()[0];
|
|
112
|
+
await page.goto(encodeHtml(testCase.html));
|
|
113
|
+
const observed = await v3.observe(testCase.instruction);
|
|
114
|
+
expect(observedElementId).toMatch(mainFrameEncodedIdPattern);
|
|
115
|
+
expect(observed).toHaveLength(1);
|
|
116
|
+
expect(observed[0].selector).toMatch(/^xpath=/);
|
|
117
|
+
const actResult = await v3.act(observed[0]);
|
|
118
|
+
expect(actResult.success).toBe(true);
|
|
119
|
+
const clicked = await page.evaluate(() => document.body.dataset.clicked);
|
|
120
|
+
expect(clicked).toBe(testCase.marker);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await closeV3(v3);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=observe-element-id-format.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe-element-id-format.spec.js","sourceRoot":"","sources":["../../../../tests/integration/observe-element-id-format.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"@playwright/test\";\nimport type { LanguageModelV2CallOptions } from \"@ai-sdk/provider\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { getV3TestConfig } from \"./v3.config.js\";\nimport {\n closeV3,\n createScriptedAisdkTestLlmClient,\n findLastEncodedId,\n promptToText,\n} from \"./testUtils.js\";\n\nconst encodedIdPattern = /^\\d+-\\d+$/;\nconst mainFrameEncodedIdPattern = /^0-\\d+$/;\n\nfunction encodeHtml(html: string): string {\n return `data:text/html,${encodeURIComponent(html)}`;\n}\n\ntype MainFrameCase = {\n name: string;\n instruction: string;\n targetText: string;\n marker: string;\n html: string;\n};\n\nconst filler = Array.from(\n { length: 80 },\n (_, index) => `<span hidden data-filler=\"${index}\">filler ${index}</span>`,\n).join(\"\");\n\nconst cases: MainFrameCase[] = [\n {\n name: \"button after hidden filler nodes\",\n instruction: \"Find the Target Checkout button\",\n targetText: \"Target Checkout\",\n marker: \"checkout\",\n html: `\n <!doctype html>\n <html>\n <body>\n ${filler}\n <main>\n <button onclick=\"document.body.dataset.clicked = 'checkout'\">\n Target Checkout\n </button>\n </main>\n </body>\n </html>\n `,\n },\n {\n name: \"navigation link\",\n instruction: \"Find the Pricing Plans link\",\n targetText: \"Pricing Plans\",\n marker: \"pricing\",\n html: `\n <!doctype html>\n <html>\n <body>\n <nav>\n <a href=\"#pricing\" onclick=\"document.body.dataset.clicked = 'pricing'\">\n Pricing Plans\n </a>\n </nav>\n </body>\n </html>\n `,\n },\n {\n name: \"form input\",\n instruction: \"Find the Company Email input\",\n targetText: \"Company Email\",\n marker: \"email\",\n html: `\n <!doctype html>\n <html>\n <body>\n <form>\n <label>\n Company Email\n <input\n type=\"email\"\n onclick=\"document.body.dataset.clicked = 'email'\"\n />\n </label>\n </form>\n </body>\n </html>\n `,\n },\n];\n\nfunction observeResponseForTarget(\n testCase: MainFrameCase,\n onElementId: (elementId: string, options: LanguageModelV2CallOptions) => void,\n) {\n return (options: LanguageModelV2CallOptions) => {\n const promptText = promptToText(options.prompt);\n expect(promptText).toContain(\n \"Always copy the complete ID exactly as shown inside the brackets into elementId\",\n );\n expect(promptText).toContain('return elementId \"0-18372\"');\n\n expect(promptText).toContain(testCase.targetText);\n\n const elementId = findLastEncodedId(options);\n expect(elementId).toMatch(encodedIdPattern);\n expect(elementId).toMatch(mainFrameEncodedIdPattern);\n onElementId(elementId, options);\n\n return {\n elements: [\n {\n elementId,\n description: testCase.targetText,\n method: \"click\",\n arguments: [] as string[],\n },\n ],\n };\n };\n}\n\ntest.describe(\"observe main frame element IDs\", () => {\n for (const testCase of cases) {\n test(`keeps complete 0-ordinal element IDs for ${testCase.name}`, async () => {\n let observedElementId: string | undefined;\n const llmClient = createScriptedAisdkTestLlmClient({\n modelId: \"mock/observe-main-frame-element-id-format\",\n jsonResponses: {\n Observation: observeResponseForTarget(testCase, (elementId) => {\n observedElementId = elementId;\n }),\n },\n });\n\n const v3 = new V3(\n getV3TestConfig({\n llmClient,\n }),\n );\n await v3.init();\n\n try {\n const page = v3.context.pages()[0];\n await page.goto(encodeHtml(testCase.html));\n\n const observed = await v3.observe(testCase.instruction);\n\n expect(observedElementId).toMatch(mainFrameEncodedIdPattern);\n expect(observed).toHaveLength(1);\n expect(observed[0].selector).toMatch(/^xpath=/);\n\n const actResult = await v3.act(observed[0]);\n expect(actResult.success).toBe(true);\n\n const clicked = await page.evaluate(\n () => document.body.dataset.clicked,\n );\n expect(clicked).toBe(testCase.marker);\n } finally {\n await closeV3(v3);\n }\n });\n }\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { extractModelName } from "../../lib/modelUtils.js";
|
|
3
|
+
import { HYBRID_CAPABLE_MODEL_PATTERNS } from "../../lib/v3/types/private/agent.js";
|
|
4
|
+
function resolveDefaultAgentMode(model, fallbackModelName) {
|
|
5
|
+
const modelName = extractModelName(model) ?? fallbackModelName;
|
|
6
|
+
const isHybridCapable = HYBRID_CAPABLE_MODEL_PATTERNS.some((pattern) => modelName.includes(pattern));
|
|
7
|
+
return isHybridCapable ? "hybrid" : "dom";
|
|
8
|
+
}
|
|
9
|
+
function resolveAgentMode(explicitMode, model, fallbackModelName) {
|
|
10
|
+
return explicitMode ?? resolveDefaultAgentMode(model, fallbackModelName);
|
|
11
|
+
}
|
|
12
|
+
describe("agent mode auto-routing", () => {
|
|
13
|
+
describe("explicit mode is never overridden", () => {
|
|
14
|
+
it("respects mode: 'dom' even when model supports hybrid", () => {
|
|
15
|
+
const result = resolveAgentMode("dom", "anthropic/claude-sonnet-4-20250514", "openai/gpt-4o");
|
|
16
|
+
expect(result).toBe("dom");
|
|
17
|
+
});
|
|
18
|
+
it("respects mode: 'hybrid' even when model does not support hybrid", () => {
|
|
19
|
+
const result = resolveAgentMode("hybrid", "openai/gpt-4o-mini", "openai/gpt-4o-mini");
|
|
20
|
+
expect(result).toBe("hybrid");
|
|
21
|
+
});
|
|
22
|
+
it("respects mode: 'cua' regardless of model", () => {
|
|
23
|
+
const result = resolveAgentMode("cua", "anthropic/claude-sonnet-4-20250514", "openai/gpt-4o");
|
|
24
|
+
expect(result).toBe("cua");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("auto-routes to hybrid for supported models when no mode is set", () => {
|
|
28
|
+
it.each([
|
|
29
|
+
["google/gemini-3-flash-preview", "gemini-3"],
|
|
30
|
+
["google/gemini-3-flash", "gemini-3"],
|
|
31
|
+
["anthropic/claude-sonnet-4-20250514", "claude"],
|
|
32
|
+
["anthropic/claude-haiku-4-5-20251001", "claude"],
|
|
33
|
+
["openai/gpt-5.4-turbo", "gpt-5.4"],
|
|
34
|
+
["openai/gpt-5.4", "gpt-5.4"],
|
|
35
|
+
])("model %s (pattern: %s) → hybrid", (modelName) => {
|
|
36
|
+
const result = resolveAgentMode(undefined, modelName, "fallback-model");
|
|
37
|
+
expect(result).toBe("hybrid");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("auto-routes to dom for unsupported models when no mode is set", () => {
|
|
41
|
+
it.each([
|
|
42
|
+
"openai/gpt-4o",
|
|
43
|
+
"openai/gpt-4o-mini",
|
|
44
|
+
"openai/gpt-4.1-mini",
|
|
45
|
+
"google/gemini-2.0-flash",
|
|
46
|
+
"google/gemini-2.5-flash",
|
|
47
|
+
"mistral/mistral-large",
|
|
48
|
+
])("model %s → dom", (modelName) => {
|
|
49
|
+
const result = resolveAgentMode(undefined, modelName, "fallback-model");
|
|
50
|
+
expect(result).toBe("dom");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("falls back to stagehand-level model when no agent model is set", () => {
|
|
54
|
+
it("uses stagehand model for routing when agent model is undefined", () => {
|
|
55
|
+
const result = resolveAgentMode(undefined, undefined, "anthropic/claude-sonnet-4-20250514");
|
|
56
|
+
expect(result).toBe("hybrid");
|
|
57
|
+
});
|
|
58
|
+
it("routes to dom when stagehand model is not hybrid-capable", () => {
|
|
59
|
+
const result = resolveAgentMode(undefined, undefined, "openai/gpt-4o-mini");
|
|
60
|
+
expect(result).toBe("dom");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("handles AgentModelConfig objects", () => {
|
|
64
|
+
it("extracts modelName from config object for routing", () => {
|
|
65
|
+
const result = resolveAgentMode(undefined, { modelName: "anthropic/claude-sonnet-4-20250514" }, "openai/gpt-4o");
|
|
66
|
+
expect(result).toBe("hybrid");
|
|
67
|
+
});
|
|
68
|
+
it("routes to dom when config object model is not hybrid-capable", () => {
|
|
69
|
+
const result = resolveAgentMode(undefined, { modelName: "openai/gpt-4o-mini" }, "openai/gpt-4o");
|
|
70
|
+
expect(result).toBe("dom");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("V3AgentHandler mode fallback", () => {
|
|
75
|
+
it("handler defaults to dom when mode is undefined (safety net)", () => {
|
|
76
|
+
const mode = undefined;
|
|
77
|
+
const resolved = mode ?? "dom";
|
|
78
|
+
expect(resolved).toBe("dom");
|
|
79
|
+
});
|
|
80
|
+
it("handler uses provided mode when set", () => {
|
|
81
|
+
const mode = "hybrid";
|
|
82
|
+
const resolved = mode ?? "dom";
|
|
83
|
+
expect(resolved).toBe("hybrid");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=agent-mode-routing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-mode-routing.test.js","sourceRoot":"","sources":["../../../../tests/unit/agent-mode-routing.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { extractModelName } from \"../../lib/modelUtils.js\";\nimport type { AgentToolMode } from \"../../lib/v3/types/public/agent.js\";\nimport { HYBRID_CAPABLE_MODEL_PATTERNS } from \"../../lib/v3/types/private/agent.js\";\n\nfunction resolveDefaultAgentMode(\n model: string | { modelName: string } | undefined,\n fallbackModelName: string,\n): AgentToolMode {\n const modelName = extractModelName(model) ?? fallbackModelName;\n const isHybridCapable = HYBRID_CAPABLE_MODEL_PATTERNS.some((pattern) =>\n modelName.includes(pattern),\n );\n return isHybridCapable ? \"hybrid\" : \"dom\";\n}\n\nfunction resolveAgentMode(\n explicitMode: AgentToolMode | undefined,\n model: string | { modelName: string } | undefined,\n fallbackModelName: string,\n): AgentToolMode {\n return explicitMode ?? resolveDefaultAgentMode(model, fallbackModelName);\n}\n\ndescribe(\"agent mode auto-routing\", () => {\n describe(\"explicit mode is never overridden\", () => {\n it(\"respects mode: 'dom' even when model supports hybrid\", () => {\n const result = resolveAgentMode(\n \"dom\",\n \"anthropic/claude-sonnet-4-20250514\",\n \"openai/gpt-4o\",\n );\n expect(result).toBe(\"dom\");\n });\n\n it(\"respects mode: 'hybrid' even when model does not support hybrid\", () => {\n const result = resolveAgentMode(\n \"hybrid\",\n \"openai/gpt-4o-mini\",\n \"openai/gpt-4o-mini\",\n );\n expect(result).toBe(\"hybrid\");\n });\n\n it(\"respects mode: 'cua' regardless of model\", () => {\n const result = resolveAgentMode(\n \"cua\",\n \"anthropic/claude-sonnet-4-20250514\",\n \"openai/gpt-4o\",\n );\n expect(result).toBe(\"cua\");\n });\n });\n\n describe(\"auto-routes to hybrid for supported models when no mode is set\", () => {\n it.each([\n [\"google/gemini-3-flash-preview\", \"gemini-3\"],\n [\"google/gemini-3-flash\", \"gemini-3\"],\n [\"anthropic/claude-sonnet-4-20250514\", \"claude\"],\n [\"anthropic/claude-haiku-4-5-20251001\", \"claude\"],\n [\"openai/gpt-5.4-turbo\", \"gpt-5.4\"],\n [\"openai/gpt-5.4\", \"gpt-5.4\"],\n ])(\"model %s (pattern: %s) → hybrid\", (modelName: string) => {\n const result = resolveAgentMode(undefined, modelName, \"fallback-model\");\n expect(result).toBe(\"hybrid\");\n });\n });\n\n describe(\"auto-routes to dom for unsupported models when no mode is set\", () => {\n it.each([\n \"openai/gpt-4o\",\n \"openai/gpt-4o-mini\",\n \"openai/gpt-4.1-mini\",\n \"google/gemini-2.0-flash\",\n \"google/gemini-2.5-flash\",\n \"mistral/mistral-large\",\n ])(\"model %s → dom\", (modelName: string) => {\n const result = resolveAgentMode(undefined, modelName, \"fallback-model\");\n expect(result).toBe(\"dom\");\n });\n });\n\n describe(\"falls back to stagehand-level model when no agent model is set\", () => {\n it(\"uses stagehand model for routing when agent model is undefined\", () => {\n const result = resolveAgentMode(\n undefined,\n undefined,\n \"anthropic/claude-sonnet-4-20250514\",\n );\n expect(result).toBe(\"hybrid\");\n });\n\n it(\"routes to dom when stagehand model is not hybrid-capable\", () => {\n const result = resolveAgentMode(\n undefined,\n undefined,\n \"openai/gpt-4o-mini\",\n );\n expect(result).toBe(\"dom\");\n });\n });\n\n describe(\"handles AgentModelConfig objects\", () => {\n it(\"extracts modelName from config object for routing\", () => {\n const result = resolveAgentMode(\n undefined,\n { modelName: \"anthropic/claude-sonnet-4-20250514\" },\n \"openai/gpt-4o\",\n );\n expect(result).toBe(\"hybrid\");\n });\n\n it(\"routes to dom when config object model is not hybrid-capable\", () => {\n const result = resolveAgentMode(\n undefined,\n { modelName: \"openai/gpt-4o-mini\" },\n \"openai/gpt-4o\",\n );\n expect(result).toBe(\"dom\");\n });\n });\n});\n\ndescribe(\"V3AgentHandler mode fallback\", () => {\n it(\"handler defaults to dom when mode is undefined (safety net)\", () => {\n const mode: AgentToolMode | undefined = undefined;\n const resolved = mode ?? \"dom\";\n expect(resolved).toBe(\"dom\");\n });\n\n it(\"handler uses provided mode when set\", () => {\n const mode: AgentToolMode | undefined = \"hybrid\";\n const resolved = mode ?? \"dom\";\n expect(resolved).toBe(\"hybrid\");\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
vi.mock("ai", async () => {
|
|
3
|
+
const actual = await vi.importActual("ai");
|
|
4
|
+
return {
|
|
5
|
+
...actual,
|
|
6
|
+
wrapLanguageModel: vi.fn(({ model }) => model),
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
import { V3AgentHandler } from "../../lib/v3/handlers/v3AgentHandler.js";
|
|
10
|
+
const usage = {
|
|
11
|
+
inputTokens: 1,
|
|
12
|
+
outputTokens: 1,
|
|
13
|
+
reasoningTokens: 0,
|
|
14
|
+
cachedInputTokens: 0,
|
|
15
|
+
totalTokens: 2,
|
|
16
|
+
};
|
|
17
|
+
const emptyList = () => [];
|
|
18
|
+
function createDoneStep() {
|
|
19
|
+
return {
|
|
20
|
+
content: emptyList(),
|
|
21
|
+
text: "",
|
|
22
|
+
reasoning: emptyList(),
|
|
23
|
+
reasoningText: undefined,
|
|
24
|
+
files: emptyList(),
|
|
25
|
+
sources: emptyList(),
|
|
26
|
+
toolCalls: [
|
|
27
|
+
{
|
|
28
|
+
type: "tool-call",
|
|
29
|
+
toolCallId: "call_done",
|
|
30
|
+
toolName: "done",
|
|
31
|
+
input: {
|
|
32
|
+
reasoning: "Task completed",
|
|
33
|
+
taskComplete: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
staticToolCalls: emptyList(),
|
|
38
|
+
dynamicToolCalls: emptyList(),
|
|
39
|
+
toolResults: [
|
|
40
|
+
{
|
|
41
|
+
type: "tool-result",
|
|
42
|
+
toolCallId: "call_done",
|
|
43
|
+
toolName: "done",
|
|
44
|
+
input: {
|
|
45
|
+
reasoning: "Task completed",
|
|
46
|
+
taskComplete: true,
|
|
47
|
+
},
|
|
48
|
+
output: {
|
|
49
|
+
success: true,
|
|
50
|
+
reasoning: "Task completed",
|
|
51
|
+
taskComplete: true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
staticToolResults: emptyList(),
|
|
56
|
+
dynamicToolResults: emptyList(),
|
|
57
|
+
finishReason: "tool-calls",
|
|
58
|
+
usage,
|
|
59
|
+
warnings: undefined,
|
|
60
|
+
request: {},
|
|
61
|
+
response: {
|
|
62
|
+
id: "response-id",
|
|
63
|
+
modelId: "openai/gpt-5-mini",
|
|
64
|
+
timestamp: new Date(0),
|
|
65
|
+
messages: emptyList(),
|
|
66
|
+
},
|
|
67
|
+
providerMetadata: undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function createGenerateResult(doneStep) {
|
|
71
|
+
return {
|
|
72
|
+
content: emptyList(),
|
|
73
|
+
text: "",
|
|
74
|
+
reasoning: emptyList(),
|
|
75
|
+
reasoningText: undefined,
|
|
76
|
+
files: emptyList(),
|
|
77
|
+
sources: emptyList(),
|
|
78
|
+
toolCalls: doneStep.toolCalls,
|
|
79
|
+
staticToolCalls: emptyList(),
|
|
80
|
+
dynamicToolCalls: emptyList(),
|
|
81
|
+
toolResults: doneStep.toolResults,
|
|
82
|
+
staticToolResults: emptyList(),
|
|
83
|
+
dynamicToolResults: emptyList(),
|
|
84
|
+
finishReason: "tool-calls",
|
|
85
|
+
usage,
|
|
86
|
+
totalUsage: usage,
|
|
87
|
+
warnings: undefined,
|
|
88
|
+
request: {},
|
|
89
|
+
response: {
|
|
90
|
+
id: "response-id",
|
|
91
|
+
modelId: "openai/gpt-5-mini",
|
|
92
|
+
timestamp: new Date(0),
|
|
93
|
+
messages: emptyList(),
|
|
94
|
+
},
|
|
95
|
+
providerMetadata: undefined,
|
|
96
|
+
steps: [doneStep],
|
|
97
|
+
experimental_output: undefined,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createV3() {
|
|
101
|
+
const page = {
|
|
102
|
+
url: () => "https://example.com",
|
|
103
|
+
enableCursorOverlay: vi.fn(async () => { }),
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
context: {
|
|
107
|
+
awaitActivePage: vi.fn(async () => page),
|
|
108
|
+
},
|
|
109
|
+
isCaptchaAutoSolveEnabled: false,
|
|
110
|
+
browserbaseApiKey: undefined,
|
|
111
|
+
logger: vi.fn(),
|
|
112
|
+
recordAgentReplayStep: vi.fn(),
|
|
113
|
+
updateMetrics: vi.fn(),
|
|
114
|
+
act: vi.fn(),
|
|
115
|
+
extract: vi.fn(),
|
|
116
|
+
observe: vi.fn(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function createLlmClient() {
|
|
120
|
+
const model = {
|
|
121
|
+
modelId: "openai/gpt-5-mini",
|
|
122
|
+
provider: "openai",
|
|
123
|
+
specificationVersion: "v2",
|
|
124
|
+
};
|
|
125
|
+
const generateText = vi.fn(async (options) => {
|
|
126
|
+
const doneStep = createDoneStep();
|
|
127
|
+
await options.onStepFinish?.(doneStep);
|
|
128
|
+
return createGenerateResult(doneStep);
|
|
129
|
+
});
|
|
130
|
+
const streamText = vi.fn((options) => {
|
|
131
|
+
void (async () => {
|
|
132
|
+
const doneStep = createDoneStep();
|
|
133
|
+
await options.onStepFinish?.(doneStep);
|
|
134
|
+
options.onFinish?.(createGenerateResult(doneStep));
|
|
135
|
+
})();
|
|
136
|
+
return {
|
|
137
|
+
textStream: (async function* () { })(),
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
client: {
|
|
142
|
+
getLanguageModel: vi.fn(() => model),
|
|
143
|
+
generateText,
|
|
144
|
+
streamText,
|
|
145
|
+
},
|
|
146
|
+
generateText,
|
|
147
|
+
streamText,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
describe("v3 agent temperature options", () => {
|
|
151
|
+
let logger;
|
|
152
|
+
beforeEach(() => {
|
|
153
|
+
logger = vi.fn();
|
|
154
|
+
});
|
|
155
|
+
it("does not pass a temperature setting to non-streaming agent generation", async () => {
|
|
156
|
+
const { client, generateText } = createLlmClient();
|
|
157
|
+
const handler = new V3AgentHandler(createV3(), logger, client);
|
|
158
|
+
await handler.execute({
|
|
159
|
+
instruction: "finish",
|
|
160
|
+
maxSteps: 1,
|
|
161
|
+
excludeTools: ["search"],
|
|
162
|
+
});
|
|
163
|
+
expect(generateText).toHaveBeenCalledTimes(1);
|
|
164
|
+
const options = generateText.mock.calls[0][0];
|
|
165
|
+
expect(options).not.toHaveProperty("temperature");
|
|
166
|
+
expect(options.providerOptions).toEqual({
|
|
167
|
+
google: { mediaResolution: "MEDIA_RESOLUTION_HIGH" },
|
|
168
|
+
openai: { store: false },
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
it("does not pass a temperature setting to streaming agent generation", async () => {
|
|
172
|
+
const { client, streamText } = createLlmClient();
|
|
173
|
+
const handler = new V3AgentHandler(createV3(), logger, client);
|
|
174
|
+
const streamResult = await handler.stream({
|
|
175
|
+
instruction: "finish",
|
|
176
|
+
maxSteps: 1,
|
|
177
|
+
excludeTools: ["search"],
|
|
178
|
+
});
|
|
179
|
+
await streamResult.result;
|
|
180
|
+
expect(streamText).toHaveBeenCalledTimes(1);
|
|
181
|
+
const options = streamText.mock.calls[0][0];
|
|
182
|
+
expect(options).not.toHaveProperty("temperature");
|
|
183
|
+
expect(options.providerOptions).toEqual({
|
|
184
|
+
google: { mediaResolution: "MEDIA_RESOLUTION_HIGH" },
|
|
185
|
+
openai: { store: false },
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=agent-temperature.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-temperature.test.js","sourceRoot":"","sources":["../../../../tests/unit/agent-temperature.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { beforeEach, describe, expect, it, vi } from \"vitest\";\nimport type { LanguageModelV2 } from \"@ai-sdk/provider\";\nimport type { LLMClient } from \"../../lib/v3/llm/LLMClient.js\";\nimport type { LogLine } from \"../../lib/v3/types/public/logs.js\";\nimport type { V3 } from \"../../lib/v3/v3.js\";\n\nvi.mock(\"ai\", async () => {\n const actual = await vi.importActual<typeof import(\"ai\")>(\"ai\");\n return {\n ...actual,\n wrapLanguageModel: vi.fn(({ model }) => model),\n };\n});\n\nimport { V3AgentHandler } from \"../../lib/v3/handlers/v3AgentHandler.js\";\n\ntype AgentLlmOptions = {\n onStepFinish?: (step: unknown) => Promise<void> | void;\n onFinish?: (event: unknown) => void;\n providerOptions?: Record<string, unknown>;\n temperature?: number;\n};\n\nconst usage = {\n inputTokens: 1,\n outputTokens: 1,\n reasoningTokens: 0,\n cachedInputTokens: 0,\n totalTokens: 2,\n};\n\nconst emptyList = () => [] as unknown[];\n\nfunction createDoneStep() {\n return {\n content: emptyList(),\n text: \"\",\n reasoning: emptyList(),\n reasoningText: undefined as string | undefined,\n files: emptyList(),\n sources: emptyList(),\n toolCalls: [\n {\n type: \"tool-call\",\n toolCallId: \"call_done\",\n toolName: \"done\",\n input: {\n reasoning: \"Task completed\",\n taskComplete: true,\n },\n },\n ],\n staticToolCalls: emptyList(),\n dynamicToolCalls: emptyList(),\n toolResults: [\n {\n type: \"tool-result\",\n toolCallId: \"call_done\",\n toolName: \"done\",\n input: {\n reasoning: \"Task completed\",\n taskComplete: true,\n },\n output: {\n success: true,\n reasoning: \"Task completed\",\n taskComplete: true,\n },\n },\n ],\n staticToolResults: emptyList(),\n dynamicToolResults: emptyList(),\n finishReason: \"tool-calls\",\n usage,\n warnings: undefined as unknown,\n request: {},\n response: {\n id: \"response-id\",\n modelId: \"openai/gpt-5-mini\",\n timestamp: new Date(0),\n messages: emptyList(),\n },\n providerMetadata: undefined as unknown,\n };\n}\n\nfunction createGenerateResult(doneStep: ReturnType<typeof createDoneStep>) {\n return {\n content: emptyList(),\n text: \"\",\n reasoning: emptyList(),\n reasoningText: undefined as string | undefined,\n files: emptyList(),\n sources: emptyList(),\n toolCalls: doneStep.toolCalls,\n staticToolCalls: emptyList(),\n dynamicToolCalls: emptyList(),\n toolResults: doneStep.toolResults,\n staticToolResults: emptyList(),\n dynamicToolResults: emptyList(),\n finishReason: \"tool-calls\",\n usage,\n totalUsage: usage,\n warnings: undefined as unknown,\n request: {},\n response: {\n id: \"response-id\",\n modelId: \"openai/gpt-5-mini\",\n timestamp: new Date(0),\n messages: emptyList(),\n },\n providerMetadata: undefined as unknown,\n steps: [doneStep],\n experimental_output: undefined as unknown,\n };\n}\n\nfunction createV3() {\n const page = {\n url: () => \"https://example.com\",\n enableCursorOverlay: vi.fn(async () => {}),\n };\n\n return {\n context: {\n awaitActivePage: vi.fn(async () => page),\n },\n isCaptchaAutoSolveEnabled: false,\n browserbaseApiKey: undefined,\n logger: vi.fn(),\n recordAgentReplayStep: vi.fn(),\n updateMetrics: vi.fn(),\n act: vi.fn(),\n extract: vi.fn(),\n observe: vi.fn(),\n } as unknown as V3;\n}\n\nfunction createLlmClient() {\n const model = {\n modelId: \"openai/gpt-5-mini\",\n provider: \"openai\",\n specificationVersion: \"v2\",\n } as unknown as LanguageModelV2;\n\n const generateText = vi.fn(async (options: AgentLlmOptions) => {\n const doneStep = createDoneStep();\n await options.onStepFinish?.(doneStep);\n return createGenerateResult(doneStep);\n });\n\n const streamText = vi.fn((options: AgentLlmOptions) => {\n void (async () => {\n const doneStep = createDoneStep();\n await options.onStepFinish?.(doneStep);\n options.onFinish?.(createGenerateResult(doneStep));\n })();\n\n return {\n textStream: (async function* () {})(),\n };\n });\n\n return {\n client: {\n getLanguageModel: vi.fn(() => model),\n generateText,\n streamText,\n } as unknown as LLMClient,\n generateText,\n streamText,\n };\n}\n\ndescribe(\"v3 agent temperature options\", () => {\n let logger: (line: LogLine) => void;\n\n beforeEach(() => {\n logger = vi.fn();\n });\n\n it(\"does not pass a temperature setting to non-streaming agent generation\", async () => {\n const { client, generateText } = createLlmClient();\n const handler = new V3AgentHandler(createV3(), logger, client);\n\n await handler.execute({\n instruction: \"finish\",\n maxSteps: 1,\n excludeTools: [\"search\"],\n });\n\n expect(generateText).toHaveBeenCalledTimes(1);\n const options = generateText.mock.calls[0][0] as AgentLlmOptions;\n expect(options).not.toHaveProperty(\"temperature\");\n expect(options.providerOptions).toEqual({\n google: { mediaResolution: \"MEDIA_RESOLUTION_HIGH\" },\n openai: { store: false },\n });\n });\n\n it(\"does not pass a temperature setting to streaming agent generation\", async () => {\n const { client, streamText } = createLlmClient();\n const handler = new V3AgentHandler(createV3(), logger, client);\n\n const streamResult = await handler.stream({\n instruction: \"finish\",\n maxSteps: 1,\n excludeTools: [\"search\"],\n });\n await streamResult.result;\n\n expect(streamText).toHaveBeenCalledTimes(1);\n const options = streamText.mock.calls[0][0] as AgentLlmOptions;\n expect(options).not.toHaveProperty(\"temperature\");\n expect(options.providerOptions).toEqual({\n google: { mediaResolution: \"MEDIA_RESOLUTION_HIGH\" },\n openai: { store: false },\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { validateExperimentalFeatures } from "../../lib/v3/agent/utils/validateExperimentalFeatures.js";
|
|
3
|
+
import { StagehandInvalidArgumentError } from "../../lib/v3/types/public/sdkErrors.js";
|
|
4
|
+
describe("agent variable experimental validation", () => {
|
|
5
|
+
it("allows variables without experimental mode", () => {
|
|
6
|
+
expect(() => validateExperimentalFeatures({
|
|
7
|
+
isExperimental: false,
|
|
8
|
+
agentConfig: { mode: "dom" },
|
|
9
|
+
executeOptions: {
|
|
10
|
+
instruction: "fill %username%",
|
|
11
|
+
variables: { username: "john@example.com" },
|
|
12
|
+
},
|
|
13
|
+
})).not.toThrow();
|
|
14
|
+
});
|
|
15
|
+
it("allows rich variables without experimental mode", () => {
|
|
16
|
+
expect(() => validateExperimentalFeatures({
|
|
17
|
+
isExperimental: false,
|
|
18
|
+
agentConfig: { mode: "dom" },
|
|
19
|
+
executeOptions: {
|
|
20
|
+
instruction: "fill %username%",
|
|
21
|
+
variables: {
|
|
22
|
+
username: {
|
|
23
|
+
value: "john@example.com",
|
|
24
|
+
description: "The login email",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
})).not.toThrow();
|
|
29
|
+
});
|
|
30
|
+
it("continues to reject variables in CUA mode", () => {
|
|
31
|
+
expect(() => validateExperimentalFeatures({
|
|
32
|
+
isExperimental: true,
|
|
33
|
+
agentConfig: { mode: "cua" },
|
|
34
|
+
executeOptions: {
|
|
35
|
+
instruction: "fill %username%",
|
|
36
|
+
variables: { username: "john@example.com" },
|
|
37
|
+
},
|
|
38
|
+
})).toThrow(StagehandInvalidArgumentError);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=agent-variables-validation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-variables-validation.test.js","sourceRoot":"","sources":["../../../../tests/unit/agent-variables-validation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,0DAA0D,CAAC;AACxG,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AAEvF,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,EAAE,CACV,4BAA4B,CAAC;YAC3B,cAAc,EAAE,KAAK;YACrB,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YAC5B,cAAc,EAAE;gBACd,WAAW,EAAE,iBAAiB;gBAC9B,SAAS,EAAE,EAAE,QAAQ,EAAE,kBAAkB,EAAE;aAC5C;SACF,CAAC,CACH,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,EAAE,CACV,4BAA4B,CAAC;YAC3B,cAAc,EAAE,KAAK;YACrB,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YAC5B,cAAc,EAAE;gBACd,WAAW,EAAE,iBAAiB;gBAC9B,SAAS,EAAE;oBACT,QAAQ,EAAE;wBACR,KAAK,EAAE,kBAAkB;wBACzB,WAAW,EAAE,iBAAiB;qBAC/B;iBACF;aACF;SACF,CAAC,CACH,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CACV,4BAA4B,CAAC;YAC3B,cAAc,EAAE,IAAI;YACpB,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YAC5B,cAAc,EAAE;gBACd,WAAW,EAAE,iBAAiB;gBAC9B,SAAS,EAAE,EAAE,QAAQ,EAAE,kBAAkB,EAAE;aAC5C;SACF,CAAC,CACH,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { validateExperimentalFeatures } from \"../../lib/v3/agent/utils/validateExperimentalFeatures.js\";\nimport { StagehandInvalidArgumentError } from \"../../lib/v3/types/public/sdkErrors.js\";\n\ndescribe(\"agent variable experimental validation\", () => {\n it(\"allows variables without experimental mode\", () => {\n expect(() =>\n validateExperimentalFeatures({\n isExperimental: false,\n agentConfig: { mode: \"dom\" },\n executeOptions: {\n instruction: \"fill %username%\",\n variables: { username: \"john@example.com\" },\n },\n }),\n ).not.toThrow();\n });\n\n it(\"allows rich variables without experimental mode\", () => {\n expect(() =>\n validateExperimentalFeatures({\n isExperimental: false,\n agentConfig: { mode: \"dom\" },\n executeOptions: {\n instruction: \"fill %username%\",\n variables: {\n username: {\n value: \"john@example.com\",\n description: \"The login email\",\n },\n },\n },\n }),\n ).not.toThrow();\n });\n\n it(\"continues to reject variables in CUA mode\", () => {\n expect(() =>\n validateExperimentalFeatures({\n isExperimental: true,\n agentConfig: { mode: \"cua\" },\n executeOptions: {\n instruction: \"fill %username%\",\n variables: { username: \"john@example.com\" },\n },\n }),\n ).toThrow(StagehandInvalidArgumentError);\n });\n});\n"]}
|
|
@@ -60,6 +60,7 @@ describe("StagehandAPIClient variable serialization", () => {
|
|
|
60
60
|
},
|
|
61
61
|
password: "secret",
|
|
62
62
|
},
|
|
63
|
+
ignoreSelectors: [".cookie-banner"],
|
|
63
64
|
},
|
|
64
65
|
});
|
|
65
66
|
expect(executeMock).toHaveBeenCalledWith({
|
|
@@ -74,11 +75,59 @@ describe("StagehandAPIClient variable serialization", () => {
|
|
|
74
75
|
},
|
|
75
76
|
password: "secret",
|
|
76
77
|
},
|
|
78
|
+
ignoreSelectors: [".cookie-banner"],
|
|
77
79
|
},
|
|
78
80
|
frameId: undefined,
|
|
79
81
|
},
|
|
80
82
|
serverCache: undefined,
|
|
81
83
|
});
|
|
82
84
|
});
|
|
85
|
+
it("preserves rich variables when sending the agentExecute request", async () => {
|
|
86
|
+
const client = new StagehandAPIClient({
|
|
87
|
+
apiKey: "bb-test",
|
|
88
|
+
logger: vi.fn(),
|
|
89
|
+
});
|
|
90
|
+
const executeMock = vi.fn().mockResolvedValue({
|
|
91
|
+
success: true,
|
|
92
|
+
message: "ok",
|
|
93
|
+
actions: [],
|
|
94
|
+
completed: true,
|
|
95
|
+
});
|
|
96
|
+
client.execute = executeMock;
|
|
97
|
+
await client.agentExecute({ mode: "dom" }, {
|
|
98
|
+
instruction: "fill the form with %username% and %password%",
|
|
99
|
+
variables: {
|
|
100
|
+
username: "john@example.com",
|
|
101
|
+
password: {
|
|
102
|
+
value: "secret",
|
|
103
|
+
description: "The login password",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
expect(executeMock).toHaveBeenCalledWith({
|
|
108
|
+
method: "agentExecute",
|
|
109
|
+
args: {
|
|
110
|
+
agentConfig: {
|
|
111
|
+
systemPrompt: undefined,
|
|
112
|
+
mode: "dom",
|
|
113
|
+
cua: undefined,
|
|
114
|
+
model: undefined,
|
|
115
|
+
executionModel: undefined,
|
|
116
|
+
},
|
|
117
|
+
executeOptions: {
|
|
118
|
+
instruction: "fill the form with %username% and %password%",
|
|
119
|
+
variables: {
|
|
120
|
+
username: "john@example.com",
|
|
121
|
+
password: {
|
|
122
|
+
value: "secret",
|
|
123
|
+
description: "The login password",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
frameId: undefined,
|
|
128
|
+
shouldCache: undefined,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
});
|
|
83
132
|
});
|
|
84
133
|
//# sourceMappingURL=api-client-observe-variables.test.js.map
|