@browserbasehq/stagehand 1.1.2 → 1.3.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/dist/index.d.ts +17 -14
- package/dist/index.js +1202 -326
- package/package.json +2 -3
- package/dist/dom/build/debug.js +0 -115
- package/dist/dom/build/global.d.js +0 -2
- package/dist/dom/build/index.js +0 -614
- package/dist/dom/build/process.js +0 -482
- package/dist/dom/build/utils.js +0 -19
- package/dist/dom/build/xpathUtils.js +0 -482
package/dist/index.js
CHANGED
|
@@ -90,21 +90,23 @@ var import_sdk2 = require("@browserbasehq/sdk");
|
|
|
90
90
|
// lib/prompt.ts
|
|
91
91
|
var actSystemPrompt = `
|
|
92
92
|
# Instructions
|
|
93
|
-
You are a browser automation assistant. Your job is to accomplish the user's goal across multiple model calls.
|
|
93
|
+
You are a browser automation assistant. Your job is to accomplish the user's goal across multiple model calls by running playwright commands.
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
## Input
|
|
96
|
+
You will receive:
|
|
96
97
|
1. the user's overall goal
|
|
97
98
|
2. the steps that you've taken so far
|
|
98
99
|
3. a list of active DOM elements in this chunk to consider to get closer to the goal.
|
|
99
100
|
4. Optionally, a list of variable names that the user has provided that you may use to accomplish the goal. To use the variables, you must use the special <|VARIABLE_NAME|> syntax.
|
|
100
101
|
|
|
101
|
-
You have 2 tools that you can call: doAction, and skipSection. Do action only performs Playwright actions. Do not perform any other actions.
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
## Your Goal / Specification
|
|
104
|
+
You have 2 tools that you can call: doAction, and skipSection. Do action only performs Playwright actions. Do exactly what the user's goal is. Do not perform any other actions or exceed the scope of the goal.
|
|
105
|
+
If the user's goal will be accomplished after running the playwright action, set completed to true. Better to have completed set to true if your are not sure.
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
Note: If there is a popup on the page for cookies or advertising that has nothing to do with the goal, try to close it first before proceeding. As this can block the goal from being completed.
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
Again, if the user's goal will be accomplished after running the playwright action, set completed to true.
|
|
108
110
|
`;
|
|
109
111
|
var verifyActCompletionSystemPrompt = `
|
|
110
112
|
You are a browser automation assistant. The job has given you a goal and a list of steps that have been taken so far. Your job is to determine if the user's goal has been completed based on the provided information.
|
|
@@ -169,7 +171,7 @@ ${steps}
|
|
|
169
171
|
# Current Active Dom Elements
|
|
170
172
|
${domElements}
|
|
171
173
|
`;
|
|
172
|
-
if (variables) {
|
|
174
|
+
if (variables && Object.keys(variables).length > 0) {
|
|
173
175
|
actUserPrompt += `
|
|
174
176
|
# Variables
|
|
175
177
|
${Object.entries(variables).map(([key, value]) => `<|${key.toUpperCase()}|>`).join("\n")}
|
|
@@ -600,6 +602,18 @@ var OpenAIClient = class {
|
|
|
600
602
|
}
|
|
601
603
|
createChatCompletion(options) {
|
|
602
604
|
return __async(this, null, function* () {
|
|
605
|
+
const _a = options, { image: _ } = _a, optionsWithoutImage = __objRest(_a, ["image"]);
|
|
606
|
+
this.logger({
|
|
607
|
+
category: "openai",
|
|
608
|
+
message: "creating chat completion",
|
|
609
|
+
level: 1,
|
|
610
|
+
auxiliary: {
|
|
611
|
+
options: {
|
|
612
|
+
value: JSON.stringify(optionsWithoutImage),
|
|
613
|
+
type: "object"
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
});
|
|
603
617
|
const cacheOptions = {
|
|
604
618
|
model: options.model,
|
|
605
619
|
messages: options.messages,
|
|
@@ -615,15 +629,31 @@ var OpenAIClient = class {
|
|
|
615
629
|
if (cachedResponse) {
|
|
616
630
|
this.logger({
|
|
617
631
|
category: "llm_cache",
|
|
618
|
-
message:
|
|
619
|
-
level: 1
|
|
632
|
+
message: "LLM cache hit - returning cached response",
|
|
633
|
+
level: 1,
|
|
634
|
+
auxiliary: {
|
|
635
|
+
requestId: {
|
|
636
|
+
value: this.requestId,
|
|
637
|
+
type: "string"
|
|
638
|
+
},
|
|
639
|
+
cachedResponse: {
|
|
640
|
+
value: JSON.stringify(cachedResponse),
|
|
641
|
+
type: "object"
|
|
642
|
+
}
|
|
643
|
+
}
|
|
620
644
|
});
|
|
621
645
|
return cachedResponse;
|
|
622
646
|
} else {
|
|
623
647
|
this.logger({
|
|
624
648
|
category: "llm_cache",
|
|
625
|
-
message:
|
|
626
|
-
level: 1
|
|
649
|
+
message: "LLM cache miss - no cached response found",
|
|
650
|
+
level: 1,
|
|
651
|
+
auxiliary: {
|
|
652
|
+
requestId: {
|
|
653
|
+
value: this.requestId,
|
|
654
|
+
type: "string"
|
|
655
|
+
}
|
|
656
|
+
}
|
|
627
657
|
});
|
|
628
658
|
}
|
|
629
659
|
}
|
|
@@ -642,7 +672,7 @@ var OpenAIClient = class {
|
|
|
642
672
|
};
|
|
643
673
|
options.messages = [...options.messages, screenshotMessage];
|
|
644
674
|
}
|
|
645
|
-
const
|
|
675
|
+
const _b = options, { image, response_model } = _b, openAiOptions = __objRest(_b, ["image", "response_model"]);
|
|
646
676
|
let responseFormat = void 0;
|
|
647
677
|
if (options.response_model) {
|
|
648
678
|
responseFormat = (0, import_zod2.zodResponseFormat)(
|
|
@@ -653,6 +683,21 @@ var OpenAIClient = class {
|
|
|
653
683
|
const response = yield this.client.chat.completions.create(__spreadProps(__spreadValues({}, openAiOptions), {
|
|
654
684
|
response_format: responseFormat
|
|
655
685
|
}));
|
|
686
|
+
this.logger({
|
|
687
|
+
category: "openai",
|
|
688
|
+
message: "response",
|
|
689
|
+
level: 1,
|
|
690
|
+
auxiliary: {
|
|
691
|
+
response: {
|
|
692
|
+
value: JSON.stringify(response),
|
|
693
|
+
type: "object"
|
|
694
|
+
},
|
|
695
|
+
requestId: {
|
|
696
|
+
value: this.requestId,
|
|
697
|
+
type: "string"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
});
|
|
656
701
|
if (response_model) {
|
|
657
702
|
const extractedData = response.choices[0].message.content;
|
|
658
703
|
const parsedData = JSON.parse(extractedData);
|
|
@@ -666,6 +711,25 @@ var OpenAIClient = class {
|
|
|
666
711
|
return __spreadValues({}, parsedData);
|
|
667
712
|
}
|
|
668
713
|
if (this.enableCaching) {
|
|
714
|
+
this.logger({
|
|
715
|
+
category: "llm_cache",
|
|
716
|
+
message: "caching response",
|
|
717
|
+
level: 1,
|
|
718
|
+
auxiliary: {
|
|
719
|
+
requestId: {
|
|
720
|
+
value: this.requestId,
|
|
721
|
+
type: "string"
|
|
722
|
+
},
|
|
723
|
+
cacheOptions: {
|
|
724
|
+
value: JSON.stringify(cacheOptions),
|
|
725
|
+
type: "object"
|
|
726
|
+
},
|
|
727
|
+
response: {
|
|
728
|
+
value: JSON.stringify(response),
|
|
729
|
+
type: "object"
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
});
|
|
669
733
|
this.cache.set(cacheOptions, response, this.requestId);
|
|
670
734
|
}
|
|
671
735
|
return response;
|
|
@@ -688,7 +752,19 @@ var AnthropicClient = class {
|
|
|
688
752
|
}
|
|
689
753
|
createChatCompletion(options) {
|
|
690
754
|
return __async(this, null, function* () {
|
|
691
|
-
var
|
|
755
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
756
|
+
const _a = options, { image: _ } = _a, optionsWithoutImage = __objRest(_a, ["image"]);
|
|
757
|
+
this.logger({
|
|
758
|
+
category: "anthropic",
|
|
759
|
+
message: "creating chat completion",
|
|
760
|
+
level: 1,
|
|
761
|
+
auxiliary: {
|
|
762
|
+
options: {
|
|
763
|
+
value: JSON.stringify(optionsWithoutImage),
|
|
764
|
+
type: "object"
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
692
768
|
const cacheOptions = {
|
|
693
769
|
model: options.model,
|
|
694
770
|
messages: options.messages,
|
|
@@ -703,15 +779,39 @@ var AnthropicClient = class {
|
|
|
703
779
|
if (cachedResponse) {
|
|
704
780
|
this.logger({
|
|
705
781
|
category: "llm_cache",
|
|
706
|
-
message:
|
|
707
|
-
level: 1
|
|
782
|
+
message: "LLM cache hit - returning cached response",
|
|
783
|
+
level: 1,
|
|
784
|
+
auxiliary: {
|
|
785
|
+
cachedResponse: {
|
|
786
|
+
value: JSON.stringify(cachedResponse),
|
|
787
|
+
type: "object"
|
|
788
|
+
},
|
|
789
|
+
requestId: {
|
|
790
|
+
value: this.requestId,
|
|
791
|
+
type: "string"
|
|
792
|
+
},
|
|
793
|
+
cacheOptions: {
|
|
794
|
+
value: JSON.stringify(cacheOptions),
|
|
795
|
+
type: "object"
|
|
796
|
+
}
|
|
797
|
+
}
|
|
708
798
|
});
|
|
709
799
|
return cachedResponse;
|
|
710
800
|
} else {
|
|
711
801
|
this.logger({
|
|
712
802
|
category: "llm_cache",
|
|
713
|
-
message:
|
|
714
|
-
level: 1
|
|
803
|
+
message: "LLM cache miss - no cached response found",
|
|
804
|
+
level: 1,
|
|
805
|
+
auxiliary: {
|
|
806
|
+
cacheOptions: {
|
|
807
|
+
value: JSON.stringify(cacheOptions),
|
|
808
|
+
type: "object"
|
|
809
|
+
},
|
|
810
|
+
requestId: {
|
|
811
|
+
value: this.requestId,
|
|
812
|
+
type: "string"
|
|
813
|
+
}
|
|
814
|
+
}
|
|
715
815
|
});
|
|
716
816
|
}
|
|
717
817
|
}
|
|
@@ -736,7 +836,7 @@ var AnthropicClient = class {
|
|
|
736
836
|
};
|
|
737
837
|
options.messages = [...options.messages, screenshotMessage];
|
|
738
838
|
}
|
|
739
|
-
let anthropicTools = (
|
|
839
|
+
let anthropicTools = (_b = options.tools) == null ? void 0 : _b.map((tool) => {
|
|
740
840
|
if (tool.type === "function") {
|
|
741
841
|
return {
|
|
742
842
|
name: tool.function.name,
|
|
@@ -753,8 +853,8 @@ var AnthropicClient = class {
|
|
|
753
853
|
let toolDefinition;
|
|
754
854
|
if (options.response_model) {
|
|
755
855
|
const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(options.response_model.schema);
|
|
756
|
-
const schemaProperties = ((
|
|
757
|
-
const schemaRequired = ((
|
|
856
|
+
const schemaProperties = ((_d = (_c = jsonSchema.definitions) == null ? void 0 : _c.MySchema) == null ? void 0 : _d.properties) || jsonSchema.properties;
|
|
857
|
+
const schemaRequired = ((_f = (_e = jsonSchema.definitions) == null ? void 0 : _e.MySchema) == null ? void 0 : _f.required) || jsonSchema.required;
|
|
758
858
|
toolDefinition = {
|
|
759
859
|
name: "print_extracted_data",
|
|
760
860
|
description: "Prints the extracted data based on the provided schema.",
|
|
@@ -780,6 +880,21 @@ var AnthropicClient = class {
|
|
|
780
880
|
system: systemMessage == null ? void 0 : systemMessage.content,
|
|
781
881
|
temperature: options.temperature
|
|
782
882
|
});
|
|
883
|
+
this.logger({
|
|
884
|
+
category: "anthropic",
|
|
885
|
+
message: "response",
|
|
886
|
+
level: 1,
|
|
887
|
+
auxiliary: {
|
|
888
|
+
response: {
|
|
889
|
+
value: JSON.stringify(response),
|
|
890
|
+
type: "object"
|
|
891
|
+
},
|
|
892
|
+
requestId: {
|
|
893
|
+
value: this.requestId,
|
|
894
|
+
type: "string"
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
});
|
|
783
898
|
const transformedResponse = {
|
|
784
899
|
id: response.id,
|
|
785
900
|
object: "chat.completion",
|
|
@@ -790,7 +905,7 @@ var AnthropicClient = class {
|
|
|
790
905
|
index: 0,
|
|
791
906
|
message: {
|
|
792
907
|
role: "assistant",
|
|
793
|
-
content: ((
|
|
908
|
+
content: ((_g = response.content.find((c) => c.type === "text")) == null ? void 0 : _g.text) || null,
|
|
794
909
|
tool_calls: response.content.filter((c) => c.type === "tool_use").map((toolUse) => ({
|
|
795
910
|
id: toolUse.id,
|
|
796
911
|
type: "function",
|
|
@@ -810,8 +925,19 @@ var AnthropicClient = class {
|
|
|
810
925
|
}
|
|
811
926
|
};
|
|
812
927
|
this.logger({
|
|
813
|
-
category: "
|
|
814
|
-
message: "
|
|
928
|
+
category: "anthropic",
|
|
929
|
+
message: "transformed response",
|
|
930
|
+
level: 1,
|
|
931
|
+
auxiliary: {
|
|
932
|
+
transformedResponse: {
|
|
933
|
+
value: JSON.stringify(transformedResponse),
|
|
934
|
+
type: "object"
|
|
935
|
+
},
|
|
936
|
+
requestId: {
|
|
937
|
+
value: this.requestId,
|
|
938
|
+
type: "string"
|
|
939
|
+
}
|
|
940
|
+
}
|
|
815
941
|
});
|
|
816
942
|
if (options.response_model) {
|
|
817
943
|
const toolUse = response.content.find((c) => c.type === "tool_use");
|
|
@@ -824,9 +950,20 @@ var AnthropicClient = class {
|
|
|
824
950
|
} else {
|
|
825
951
|
if (!options.retries || options.retries < 5) {
|
|
826
952
|
return this.createChatCompletion(__spreadProps(__spreadValues({}, options), {
|
|
827
|
-
retries: ((
|
|
953
|
+
retries: ((_h = options.retries) != null ? _h : 0) + 1
|
|
828
954
|
}));
|
|
829
955
|
}
|
|
956
|
+
this.logger({
|
|
957
|
+
category: "anthropic",
|
|
958
|
+
message: "error creating chat completion",
|
|
959
|
+
level: 1,
|
|
960
|
+
auxiliary: {
|
|
961
|
+
requestId: {
|
|
962
|
+
value: this.requestId,
|
|
963
|
+
type: "string"
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
});
|
|
830
967
|
throw new Error(
|
|
831
968
|
"Create Chat Completion Failed: No tool use with input in response"
|
|
832
969
|
);
|
|
@@ -834,6 +971,25 @@ var AnthropicClient = class {
|
|
|
834
971
|
}
|
|
835
972
|
if (this.enableCaching) {
|
|
836
973
|
this.cache.set(cacheOptions, transformedResponse, this.requestId);
|
|
974
|
+
this.logger({
|
|
975
|
+
category: "anthropic",
|
|
976
|
+
message: "cached response",
|
|
977
|
+
level: 1,
|
|
978
|
+
auxiliary: {
|
|
979
|
+
requestId: {
|
|
980
|
+
value: this.requestId,
|
|
981
|
+
type: "string"
|
|
982
|
+
},
|
|
983
|
+
transformedResponse: {
|
|
984
|
+
value: JSON.stringify(transformedResponse),
|
|
985
|
+
type: "object"
|
|
986
|
+
},
|
|
987
|
+
cacheOptions: {
|
|
988
|
+
value: JSON.stringify(cacheOptions),
|
|
989
|
+
type: "object"
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
});
|
|
837
993
|
}
|
|
838
994
|
return transformedResponse;
|
|
839
995
|
});
|
|
@@ -872,8 +1028,18 @@ var BaseCache = class {
|
|
|
872
1028
|
process.on("uncaughtException", (err) => {
|
|
873
1029
|
this.logger({
|
|
874
1030
|
category: "base_cache",
|
|
875
|
-
message:
|
|
876
|
-
level: 2
|
|
1031
|
+
message: "uncaught exception",
|
|
1032
|
+
level: 2,
|
|
1033
|
+
auxiliary: {
|
|
1034
|
+
error: {
|
|
1035
|
+
value: err.message,
|
|
1036
|
+
type: "string"
|
|
1037
|
+
},
|
|
1038
|
+
trace: {
|
|
1039
|
+
value: err.stack,
|
|
1040
|
+
type: "string"
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
877
1043
|
});
|
|
878
1044
|
if (this.lockAcquired) {
|
|
879
1045
|
releaseLockAndExit();
|
|
@@ -885,8 +1051,14 @@ var BaseCache = class {
|
|
|
885
1051
|
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
886
1052
|
this.logger({
|
|
887
1053
|
category: "base_cache",
|
|
888
|
-
message:
|
|
889
|
-
level: 1
|
|
1054
|
+
message: "created cache directory",
|
|
1055
|
+
level: 1,
|
|
1056
|
+
auxiliary: {
|
|
1057
|
+
cacheDir: {
|
|
1058
|
+
value: this.cacheDir,
|
|
1059
|
+
type: "string"
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
890
1062
|
});
|
|
891
1063
|
}
|
|
892
1064
|
}
|
|
@@ -957,8 +1129,18 @@ var BaseCache = class {
|
|
|
957
1129
|
} catch (error) {
|
|
958
1130
|
this.logger({
|
|
959
1131
|
category: "base_cache",
|
|
960
|
-
message:
|
|
961
|
-
level: 2
|
|
1132
|
+
message: "error releasing lock",
|
|
1133
|
+
level: 2,
|
|
1134
|
+
auxiliary: {
|
|
1135
|
+
error: {
|
|
1136
|
+
value: error.message,
|
|
1137
|
+
type: "string"
|
|
1138
|
+
},
|
|
1139
|
+
trace: {
|
|
1140
|
+
value: error.stack,
|
|
1141
|
+
type: "string"
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
962
1144
|
});
|
|
963
1145
|
}
|
|
964
1146
|
}
|
|
@@ -970,7 +1152,7 @@ var BaseCache = class {
|
|
|
970
1152
|
if (!(yield this.acquireLock())) {
|
|
971
1153
|
this.logger({
|
|
972
1154
|
category: "llm_cache",
|
|
973
|
-
message: "
|
|
1155
|
+
message: "failed to acquire lock for cleanup",
|
|
974
1156
|
level: 2
|
|
975
1157
|
});
|
|
976
1158
|
return;
|
|
@@ -989,15 +1171,31 @@ var BaseCache = class {
|
|
|
989
1171
|
this.writeCache(cache);
|
|
990
1172
|
this.logger({
|
|
991
1173
|
category: "llm_cache",
|
|
992
|
-
message:
|
|
993
|
-
level: 1
|
|
1174
|
+
message: "cleaned up stale cache entries",
|
|
1175
|
+
level: 1,
|
|
1176
|
+
auxiliary: {
|
|
1177
|
+
entriesRemoved: {
|
|
1178
|
+
value: entriesRemoved.toString(),
|
|
1179
|
+
type: "integer"
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
994
1182
|
});
|
|
995
1183
|
}
|
|
996
1184
|
} catch (error) {
|
|
997
1185
|
this.logger({
|
|
998
1186
|
category: "llm_cache",
|
|
999
|
-
message:
|
|
1000
|
-
level: 2
|
|
1187
|
+
message: "error during cache cleanup",
|
|
1188
|
+
level: 2,
|
|
1189
|
+
auxiliary: {
|
|
1190
|
+
error: {
|
|
1191
|
+
value: error.message,
|
|
1192
|
+
type: "string"
|
|
1193
|
+
},
|
|
1194
|
+
trace: {
|
|
1195
|
+
value: error.stack,
|
|
1196
|
+
type: "string"
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1001
1199
|
});
|
|
1002
1200
|
} finally {
|
|
1003
1201
|
this.releaseLock();
|
|
@@ -1012,8 +1210,18 @@ var BaseCache = class {
|
|
|
1012
1210
|
} catch (error) {
|
|
1013
1211
|
this.logger({
|
|
1014
1212
|
category: "base_cache",
|
|
1015
|
-
message:
|
|
1016
|
-
level: 1
|
|
1213
|
+
message: "error reading cache file. resetting cache.",
|
|
1214
|
+
level: 1,
|
|
1215
|
+
auxiliary: {
|
|
1216
|
+
error: {
|
|
1217
|
+
value: error.message,
|
|
1218
|
+
type: "string"
|
|
1219
|
+
},
|
|
1220
|
+
trace: {
|
|
1221
|
+
value: error.stack,
|
|
1222
|
+
type: "string"
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1017
1225
|
});
|
|
1018
1226
|
this.resetCache();
|
|
1019
1227
|
return {};
|
|
@@ -1032,8 +1240,18 @@ var BaseCache = class {
|
|
|
1032
1240
|
} catch (error) {
|
|
1033
1241
|
this.logger({
|
|
1034
1242
|
category: "base_cache",
|
|
1035
|
-
message:
|
|
1036
|
-
level: 2
|
|
1243
|
+
message: "error writing cache file",
|
|
1244
|
+
level: 2,
|
|
1245
|
+
auxiliary: {
|
|
1246
|
+
error: {
|
|
1247
|
+
value: error.message,
|
|
1248
|
+
type: "string"
|
|
1249
|
+
},
|
|
1250
|
+
trace: {
|
|
1251
|
+
value: error.stack,
|
|
1252
|
+
type: "string"
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1037
1255
|
});
|
|
1038
1256
|
} finally {
|
|
1039
1257
|
this.releaseLock();
|
|
@@ -1066,8 +1284,18 @@ var BaseCache = class {
|
|
|
1066
1284
|
} catch (error) {
|
|
1067
1285
|
this.logger({
|
|
1068
1286
|
category: "base_cache",
|
|
1069
|
-
message:
|
|
1070
|
-
level: 1
|
|
1287
|
+
message: "error getting cache. resetting cache.",
|
|
1288
|
+
level: 1,
|
|
1289
|
+
auxiliary: {
|
|
1290
|
+
error: {
|
|
1291
|
+
value: error.message,
|
|
1292
|
+
type: "string"
|
|
1293
|
+
},
|
|
1294
|
+
trace: {
|
|
1295
|
+
value: error.stack,
|
|
1296
|
+
type: "string"
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1071
1299
|
});
|
|
1072
1300
|
this.resetCache();
|
|
1073
1301
|
return null;
|
|
@@ -1105,8 +1333,18 @@ var BaseCache = class {
|
|
|
1105
1333
|
} catch (error) {
|
|
1106
1334
|
this.logger({
|
|
1107
1335
|
category: "base_cache",
|
|
1108
|
-
message:
|
|
1109
|
-
level: 1
|
|
1336
|
+
message: "error setting cache. resetting cache.",
|
|
1337
|
+
level: 1,
|
|
1338
|
+
auxiliary: {
|
|
1339
|
+
error: {
|
|
1340
|
+
value: error.message,
|
|
1341
|
+
type: "string"
|
|
1342
|
+
},
|
|
1343
|
+
trace: {
|
|
1344
|
+
value: error.stack,
|
|
1345
|
+
type: "string"
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1110
1348
|
});
|
|
1111
1349
|
this.resetCache();
|
|
1112
1350
|
} finally {
|
|
@@ -1143,8 +1381,18 @@ var BaseCache = class {
|
|
|
1143
1381
|
} catch (error) {
|
|
1144
1382
|
this.logger({
|
|
1145
1383
|
category: "base_cache",
|
|
1146
|
-
message:
|
|
1147
|
-
level: 2
|
|
1384
|
+
message: "error removing cache entry",
|
|
1385
|
+
level: 2,
|
|
1386
|
+
auxiliary: {
|
|
1387
|
+
error: {
|
|
1388
|
+
value: error.message,
|
|
1389
|
+
type: "string"
|
|
1390
|
+
},
|
|
1391
|
+
trace: {
|
|
1392
|
+
value: error.stack,
|
|
1393
|
+
type: "string"
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1148
1396
|
});
|
|
1149
1397
|
} finally {
|
|
1150
1398
|
this.releaseLock();
|
|
@@ -1191,16 +1439,36 @@ var BaseCache = class {
|
|
|
1191
1439
|
} else {
|
|
1192
1440
|
this.logger({
|
|
1193
1441
|
category: "base_cache",
|
|
1194
|
-
message:
|
|
1195
|
-
level: 1
|
|
1442
|
+
message: "no cache entries found for requestId",
|
|
1443
|
+
level: 1,
|
|
1444
|
+
auxiliary: {
|
|
1445
|
+
requestId: {
|
|
1446
|
+
value: requestId,
|
|
1447
|
+
type: "string"
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1196
1450
|
});
|
|
1197
1451
|
}
|
|
1198
1452
|
delete this.requestIdToUsedHashes[requestId];
|
|
1199
1453
|
} catch (error) {
|
|
1200
1454
|
this.logger({
|
|
1201
1455
|
category: "base_cache",
|
|
1202
|
-
message:
|
|
1203
|
-
level: 2
|
|
1456
|
+
message: "error deleting cache for requestId",
|
|
1457
|
+
level: 2,
|
|
1458
|
+
auxiliary: {
|
|
1459
|
+
error: {
|
|
1460
|
+
value: error.message,
|
|
1461
|
+
type: "string"
|
|
1462
|
+
},
|
|
1463
|
+
trace: {
|
|
1464
|
+
value: error.stack,
|
|
1465
|
+
type: "string"
|
|
1466
|
+
},
|
|
1467
|
+
requestId: {
|
|
1468
|
+
value: requestId,
|
|
1469
|
+
type: "string"
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1204
1472
|
});
|
|
1205
1473
|
} finally {
|
|
1206
1474
|
this.releaseLock();
|
|
@@ -1217,8 +1485,18 @@ var BaseCache = class {
|
|
|
1217
1485
|
} catch (error) {
|
|
1218
1486
|
this.logger({
|
|
1219
1487
|
category: "base_cache",
|
|
1220
|
-
message:
|
|
1221
|
-
level: 2
|
|
1488
|
+
message: "error resetting cache",
|
|
1489
|
+
level: 2,
|
|
1490
|
+
auxiliary: {
|
|
1491
|
+
error: {
|
|
1492
|
+
value: error.message,
|
|
1493
|
+
type: "string"
|
|
1494
|
+
},
|
|
1495
|
+
trace: {
|
|
1496
|
+
value: error.stack,
|
|
1497
|
+
type: "string"
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1222
1500
|
});
|
|
1223
1501
|
} finally {
|
|
1224
1502
|
this.releaseLock();
|
|
@@ -1274,12 +1552,22 @@ var LLMProvider = class {
|
|
|
1274
1552
|
};
|
|
1275
1553
|
this.logger = logger;
|
|
1276
1554
|
this.enableCaching = enableCaching;
|
|
1277
|
-
this.cache = new LLMCache(logger);
|
|
1555
|
+
this.cache = enableCaching ? new LLMCache(logger) : void 0;
|
|
1278
1556
|
}
|
|
1279
1557
|
cleanRequestCache(requestId) {
|
|
1558
|
+
if (!this.enableCaching) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1280
1561
|
this.logger({
|
|
1281
1562
|
category: "llm_cache",
|
|
1282
|
-
message:
|
|
1563
|
+
message: "cleaning up cache",
|
|
1564
|
+
level: 1,
|
|
1565
|
+
auxiliary: {
|
|
1566
|
+
requestId: {
|
|
1567
|
+
value: requestId,
|
|
1568
|
+
type: "string"
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1283
1571
|
});
|
|
1284
1572
|
this.cache.deleteCacheForRequestId(requestId);
|
|
1285
1573
|
}
|
|
@@ -1309,31 +1597,45 @@ var LLMProvider = class {
|
|
|
1309
1597
|
}
|
|
1310
1598
|
};
|
|
1311
1599
|
|
|
1312
|
-
// lib/index.ts
|
|
1313
|
-
var import_path2 = __toESM(require("path"));
|
|
1314
|
-
|
|
1315
1600
|
// lib/vision.ts
|
|
1316
1601
|
var import_fs = __toESM(require("fs"));
|
|
1317
1602
|
var import_path = __toESM(require("path"));
|
|
1318
1603
|
var import_sharp = __toESM(require("sharp"));
|
|
1319
1604
|
var import_child_process = require("child_process");
|
|
1320
|
-
|
|
1321
|
-
|
|
1605
|
+
|
|
1606
|
+
// lib/utils.ts
|
|
1607
|
+
var import_crypto = __toESM(require("crypto"));
|
|
1608
|
+
function generateId(operation) {
|
|
1609
|
+
return import_crypto.default.createHash("sha256").update(operation).digest("hex");
|
|
1610
|
+
}
|
|
1611
|
+
function logLineToString(logLine) {
|
|
1612
|
+
var _a;
|
|
1613
|
+
const timestamp = logLine.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1614
|
+
if ((_a = logLine.auxiliary) == null ? void 0 : _a.error) {
|
|
1615
|
+
return `${timestamp}::[stagehand:${logLine.category}] ${logLine.message}
|
|
1616
|
+
${logLine.auxiliary.error.value}
|
|
1617
|
+
${logLine.auxiliary.trace.value}`;
|
|
1618
|
+
}
|
|
1619
|
+
return `${timestamp}::[stagehand:${logLine.category}] ${logLine.message} ${logLine.auxiliary ? JSON.stringify(logLine.auxiliary) : ""}`;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// lib/vision.ts
|
|
1623
|
+
var ScreenshotService = class {
|
|
1624
|
+
constructor(page, selectorMap, verbose, externalLogger, isDebugEnabled = false) {
|
|
1322
1625
|
this.annotationBoxes = [];
|
|
1323
1626
|
this.numberPositions = [];
|
|
1324
1627
|
this.page = page;
|
|
1325
1628
|
this.selectorMap = selectorMap;
|
|
1326
1629
|
this.isDebugEnabled = isDebugEnabled;
|
|
1327
1630
|
this.verbose = verbose;
|
|
1631
|
+
this.externalLogger = externalLogger;
|
|
1328
1632
|
}
|
|
1329
|
-
log({
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
const categoryString = category ? `:${category}` : "";
|
|
1336
|
-
console.log(`[stagehand${categoryString}] ${message}`);
|
|
1633
|
+
log(logLine) {
|
|
1634
|
+
if (this.verbose >= logLine.level) {
|
|
1635
|
+
console.log(logLineToString(logLine));
|
|
1636
|
+
}
|
|
1637
|
+
if (this.externalLogger) {
|
|
1638
|
+
this.externalLogger(logLine);
|
|
1337
1639
|
}
|
|
1338
1640
|
}
|
|
1339
1641
|
getScreenshot(fullpage = true, quality) {
|
|
@@ -1350,21 +1652,40 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1350
1652
|
}
|
|
1351
1653
|
getScreenshotPixelCount(screenshot) {
|
|
1352
1654
|
return __async(this, null, function* () {
|
|
1655
|
+
var _a, _b, _c, _d;
|
|
1353
1656
|
const image = (0, import_sharp.default)(screenshot);
|
|
1354
1657
|
const metadata = yield image.metadata();
|
|
1355
1658
|
if (!metadata.width || !metadata.height) {
|
|
1356
1659
|
this.log({
|
|
1357
|
-
category: "
|
|
1660
|
+
category: "screenshotService",
|
|
1358
1661
|
message: "Unable to determine image dimensions.",
|
|
1359
|
-
level: 0
|
|
1662
|
+
level: 0,
|
|
1663
|
+
auxiliary: {
|
|
1664
|
+
width: {
|
|
1665
|
+
value: (_b = (_a = metadata.width) == null ? void 0 : _a.toString()) != null ? _b : "undefined",
|
|
1666
|
+
type: "string"
|
|
1667
|
+
// might be undefined
|
|
1668
|
+
},
|
|
1669
|
+
height: {
|
|
1670
|
+
value: (_d = (_c = metadata.height) == null ? void 0 : _c.toString()) != null ? _d : "undefined",
|
|
1671
|
+
type: "string"
|
|
1672
|
+
// might be undefined
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1360
1675
|
});
|
|
1361
1676
|
throw new Error("Unable to determine image dimensions.");
|
|
1362
1677
|
}
|
|
1363
1678
|
const pixelCount = metadata.width * metadata.height;
|
|
1364
1679
|
this.log({
|
|
1365
|
-
category: "
|
|
1366
|
-
message:
|
|
1367
|
-
level: 1
|
|
1680
|
+
category: "screenshotService",
|
|
1681
|
+
message: "got screenshot pixel count",
|
|
1682
|
+
level: 1,
|
|
1683
|
+
auxiliary: {
|
|
1684
|
+
pixelCount: {
|
|
1685
|
+
value: pixelCount.toString(),
|
|
1686
|
+
type: "integer"
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1368
1689
|
});
|
|
1369
1690
|
return pixelCount;
|
|
1370
1691
|
});
|
|
@@ -1376,6 +1697,17 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1376
1697
|
const screenshot = yield this.getScreenshot(fullpage);
|
|
1377
1698
|
const image = (0, import_sharp.default)(screenshot);
|
|
1378
1699
|
const { width, height } = yield image.metadata();
|
|
1700
|
+
this.log({
|
|
1701
|
+
category: "screenshotService",
|
|
1702
|
+
message: "annotating screenshot",
|
|
1703
|
+
level: 2,
|
|
1704
|
+
auxiliary: {
|
|
1705
|
+
selectorMap: {
|
|
1706
|
+
value: JSON.stringify(this.selectorMap),
|
|
1707
|
+
type: "object"
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1379
1711
|
const svgAnnotations = yield Promise.all(
|
|
1380
1712
|
Object.entries(this.selectorMap).map(
|
|
1381
1713
|
(_0) => __async(this, [_0], function* ([id, selectors]) {
|
|
@@ -1396,7 +1728,7 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1396
1728
|
`;
|
|
1397
1729
|
const annotatedScreenshot = yield image.composite([{ input: Buffer.from(svg), top: 0, left: 0 }]).toBuffer();
|
|
1398
1730
|
if (this.isDebugEnabled) {
|
|
1399
|
-
yield
|
|
1731
|
+
yield this.saveAndOpenScreenshot(annotatedScreenshot);
|
|
1400
1732
|
}
|
|
1401
1733
|
return annotatedScreenshot;
|
|
1402
1734
|
});
|
|
@@ -1446,9 +1778,23 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1446
1778
|
`;
|
|
1447
1779
|
} catch (error) {
|
|
1448
1780
|
this.log({
|
|
1449
|
-
category: "
|
|
1450
|
-
message:
|
|
1451
|
-
level:
|
|
1781
|
+
category: "screenshotService",
|
|
1782
|
+
message: "warning: failed to create annotation for element",
|
|
1783
|
+
level: 1,
|
|
1784
|
+
auxiliary: {
|
|
1785
|
+
element_id: {
|
|
1786
|
+
value: id,
|
|
1787
|
+
type: "string"
|
|
1788
|
+
},
|
|
1789
|
+
error: {
|
|
1790
|
+
value: error.message,
|
|
1791
|
+
type: "string"
|
|
1792
|
+
},
|
|
1793
|
+
trace: {
|
|
1794
|
+
value: error.stack,
|
|
1795
|
+
type: "string"
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1452
1798
|
});
|
|
1453
1799
|
return "";
|
|
1454
1800
|
}
|
|
@@ -1478,7 +1824,7 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1478
1824
|
) < circleRadius * 2
|
|
1479
1825
|
);
|
|
1480
1826
|
}
|
|
1481
|
-
|
|
1827
|
+
saveAndOpenScreenshot(screenshot) {
|
|
1482
1828
|
return __async(this, null, function* () {
|
|
1483
1829
|
const screenshotDir = import_path.default.join(process.cwd(), "screenshots");
|
|
1484
1830
|
if (!import_fs.default.existsSync(screenshotDir)) {
|
|
@@ -1487,7 +1833,17 @@ var ScreenshotService = class _ScreenshotService {
|
|
|
1487
1833
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1488
1834
|
const filename = import_path.default.join(screenshotDir, `screenshot-${timestamp}.png`);
|
|
1489
1835
|
import_fs.default.writeFileSync(filename, screenshot);
|
|
1490
|
-
|
|
1836
|
+
this.log({
|
|
1837
|
+
category: "screenshotService",
|
|
1838
|
+
message: "screenshot saved",
|
|
1839
|
+
level: 1,
|
|
1840
|
+
auxiliary: {
|
|
1841
|
+
filename: {
|
|
1842
|
+
value: filename,
|
|
1843
|
+
type: "string"
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1491
1847
|
if (process.platform === "win32") {
|
|
1492
1848
|
(0, import_child_process.exec)(`start ${filename}`);
|
|
1493
1849
|
} else if (process.platform === "darwin") {
|
|
@@ -1532,8 +1888,26 @@ var ActionCache = class _ActionCache extends BaseCache {
|
|
|
1532
1888
|
}) {
|
|
1533
1889
|
this.logger({
|
|
1534
1890
|
category: "action_cache",
|
|
1535
|
-
message:
|
|
1536
|
-
level: 1
|
|
1891
|
+
message: "adding action step to cache",
|
|
1892
|
+
level: 1,
|
|
1893
|
+
auxiliary: {
|
|
1894
|
+
action: {
|
|
1895
|
+
value: action,
|
|
1896
|
+
type: "string"
|
|
1897
|
+
},
|
|
1898
|
+
requestId: {
|
|
1899
|
+
value: requestId,
|
|
1900
|
+
type: "string"
|
|
1901
|
+
},
|
|
1902
|
+
url: {
|
|
1903
|
+
value: url,
|
|
1904
|
+
type: "string"
|
|
1905
|
+
},
|
|
1906
|
+
previousSelectors: {
|
|
1907
|
+
value: JSON.stringify(previousSelectors),
|
|
1908
|
+
type: "object"
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1537
1911
|
});
|
|
1538
1912
|
yield this.set(
|
|
1539
1913
|
{ url, action, previousSelectors },
|
|
@@ -1585,8 +1959,14 @@ var ActionCache = class _ActionCache extends BaseCache {
|
|
|
1585
1959
|
yield __superGet(_ActionCache.prototype, this, "deleteCacheForRequestId").call(this, requestId);
|
|
1586
1960
|
this.logger({
|
|
1587
1961
|
category: "action_cache",
|
|
1588
|
-
message:
|
|
1589
|
-
level: 1
|
|
1962
|
+
message: "cleared action for ID",
|
|
1963
|
+
level: 1,
|
|
1964
|
+
auxiliary: {
|
|
1965
|
+
requestId: {
|
|
1966
|
+
value: requestId,
|
|
1967
|
+
type: "string"
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1590
1970
|
});
|
|
1591
1971
|
});
|
|
1592
1972
|
}
|
|
@@ -1605,12 +1985,6 @@ var ActionCache = class _ActionCache extends BaseCache {
|
|
|
1605
1985
|
}
|
|
1606
1986
|
};
|
|
1607
1987
|
|
|
1608
|
-
// lib/utils.ts
|
|
1609
|
-
var import_crypto = __toESM(require("crypto"));
|
|
1610
|
-
function generateId(operation) {
|
|
1611
|
-
return import_crypto.default.createHash("sha256").update(operation).digest("hex");
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
1988
|
// lib/handlers/actHandler.ts
|
|
1615
1989
|
var StagehandActHandler = class {
|
|
1616
1990
|
constructor({
|
|
@@ -1630,7 +2004,7 @@ var StagehandActHandler = class {
|
|
|
1630
2004
|
this.enableCaching = enableCaching;
|
|
1631
2005
|
this.logger = logger;
|
|
1632
2006
|
this.waitForSettledDom = waitForSettledDom;
|
|
1633
|
-
this.actionCache = new ActionCache(this.logger);
|
|
2007
|
+
this.actionCache = enableCaching ? new ActionCache(this.logger) : void 0;
|
|
1634
2008
|
this.defaultModelName = defaultModelName;
|
|
1635
2009
|
this.startDomDebug = startDomDebug;
|
|
1636
2010
|
this.cleanupDomDebug = cleanupDomDebug;
|
|
@@ -1661,8 +2035,14 @@ var StagehandActHandler = class {
|
|
|
1661
2035
|
if (completed) {
|
|
1662
2036
|
this.stagehand.log({
|
|
1663
2037
|
category: "action",
|
|
1664
|
-
message:
|
|
1665
|
-
level: 1
|
|
2038
|
+
message: "action marked as completed, verifying if this is true...",
|
|
2039
|
+
level: 1,
|
|
2040
|
+
auxiliary: {
|
|
2041
|
+
action: {
|
|
2042
|
+
value: action,
|
|
2043
|
+
type: "string"
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
1666
2046
|
});
|
|
1667
2047
|
let domElements = void 0;
|
|
1668
2048
|
let fullpageScreenshot = void 0;
|
|
@@ -1671,20 +2051,31 @@ var StagehandActHandler = class {
|
|
|
1671
2051
|
const screenshotService = new ScreenshotService(
|
|
1672
2052
|
this.stagehand.page,
|
|
1673
2053
|
selectorMap,
|
|
1674
|
-
this.verbose
|
|
2054
|
+
this.verbose,
|
|
2055
|
+
this.logger
|
|
1675
2056
|
);
|
|
1676
2057
|
fullpageScreenshot = yield screenshotService.getScreenshot(true, 15);
|
|
1677
2058
|
} catch (e) {
|
|
1678
2059
|
this.stagehand.log({
|
|
1679
2060
|
category: "action",
|
|
1680
|
-
message:
|
|
1681
|
-
|
|
1682
|
-
|
|
2061
|
+
message: "error getting full page screenshot. trying again...",
|
|
2062
|
+
level: 1,
|
|
2063
|
+
auxiliary: {
|
|
2064
|
+
error: {
|
|
2065
|
+
value: e.message,
|
|
2066
|
+
type: "string"
|
|
2067
|
+
},
|
|
2068
|
+
trace: {
|
|
2069
|
+
value: e.stack,
|
|
2070
|
+
type: "string"
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
1683
2073
|
});
|
|
1684
2074
|
const screenshotService = new ScreenshotService(
|
|
1685
2075
|
this.stagehand.page,
|
|
1686
2076
|
selectorMap,
|
|
1687
|
-
this.verbose
|
|
2077
|
+
this.verbose,
|
|
2078
|
+
this.logger
|
|
1688
2079
|
);
|
|
1689
2080
|
fullpageScreenshot = yield screenshotService.getScreenshot(true, 15);
|
|
1690
2081
|
}
|
|
@@ -1707,8 +2098,18 @@ var StagehandActHandler = class {
|
|
|
1707
2098
|
});
|
|
1708
2099
|
this.stagehand.log({
|
|
1709
2100
|
category: "action",
|
|
1710
|
-
message:
|
|
1711
|
-
level: 1
|
|
2101
|
+
message: "action completion verification result",
|
|
2102
|
+
level: 1,
|
|
2103
|
+
auxiliary: {
|
|
2104
|
+
action: {
|
|
2105
|
+
value: action,
|
|
2106
|
+
type: "string"
|
|
2107
|
+
},
|
|
2108
|
+
result: {
|
|
2109
|
+
value: actionCompleted.toString(),
|
|
2110
|
+
type: "boolean"
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
1712
2113
|
});
|
|
1713
2114
|
}
|
|
1714
2115
|
return actionCompleted;
|
|
@@ -1716,13 +2117,20 @@ var StagehandActHandler = class {
|
|
|
1716
2117
|
}
|
|
1717
2118
|
_performPlaywrightMethod(method, args, xpath, domSettleTimeoutMs) {
|
|
1718
2119
|
return __async(this, null, function* () {
|
|
2120
|
+
var _a, _b;
|
|
1719
2121
|
const locator = this.stagehand.page.locator(`xpath=${xpath}`).first();
|
|
1720
2122
|
const initialUrl = this.stagehand.page.url();
|
|
1721
2123
|
if (method === "scrollIntoView") {
|
|
1722
2124
|
this.stagehand.log({
|
|
1723
2125
|
category: "action",
|
|
1724
|
-
message:
|
|
1725
|
-
level: 2
|
|
2126
|
+
message: "scrolling element into view",
|
|
2127
|
+
level: 2,
|
|
2128
|
+
auxiliary: {
|
|
2129
|
+
xpath: {
|
|
2130
|
+
value: xpath,
|
|
2131
|
+
type: "string"
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
1726
2134
|
});
|
|
1727
2135
|
try {
|
|
1728
2136
|
yield locator.evaluate((element) => {
|
|
@@ -1730,17 +2138,43 @@ var StagehandActHandler = class {
|
|
|
1730
2138
|
}).catch((e) => {
|
|
1731
2139
|
this.stagehand.log({
|
|
1732
2140
|
category: "action",
|
|
1733
|
-
message:
|
|
1734
|
-
|
|
1735
|
-
|
|
2141
|
+
message: "error scrolling element into view",
|
|
2142
|
+
level: 1,
|
|
2143
|
+
auxiliary: {
|
|
2144
|
+
error: {
|
|
2145
|
+
value: e.message,
|
|
2146
|
+
type: "string"
|
|
2147
|
+
},
|
|
2148
|
+
trace: {
|
|
2149
|
+
value: e.stack,
|
|
2150
|
+
type: "string"
|
|
2151
|
+
},
|
|
2152
|
+
xpath: {
|
|
2153
|
+
value: xpath,
|
|
2154
|
+
type: "string"
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
1736
2157
|
});
|
|
1737
2158
|
});
|
|
1738
2159
|
} catch (e) {
|
|
1739
2160
|
this.stagehand.log({
|
|
1740
2161
|
category: "action",
|
|
1741
|
-
message:
|
|
1742
|
-
|
|
1743
|
-
|
|
2162
|
+
message: "error scrolling element into view",
|
|
2163
|
+
level: 1,
|
|
2164
|
+
auxiliary: {
|
|
2165
|
+
error: {
|
|
2166
|
+
value: e.message,
|
|
2167
|
+
type: "string"
|
|
2168
|
+
},
|
|
2169
|
+
trace: {
|
|
2170
|
+
value: e.stack,
|
|
2171
|
+
type: "string"
|
|
2172
|
+
},
|
|
2173
|
+
xpath: {
|
|
2174
|
+
value: xpath,
|
|
2175
|
+
type: "string"
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
1744
2178
|
});
|
|
1745
2179
|
throw new PlaywrightCommandException(e.message);
|
|
1746
2180
|
}
|
|
@@ -1757,9 +2191,22 @@ Trace: ${e.stack}`,
|
|
|
1757
2191
|
} catch (e) {
|
|
1758
2192
|
this.logger({
|
|
1759
2193
|
category: "action",
|
|
1760
|
-
message:
|
|
1761
|
-
|
|
1762
|
-
|
|
2194
|
+
message: "error filling element",
|
|
2195
|
+
level: 1,
|
|
2196
|
+
auxiliary: {
|
|
2197
|
+
error: {
|
|
2198
|
+
value: e.message,
|
|
2199
|
+
type: "string"
|
|
2200
|
+
},
|
|
2201
|
+
trace: {
|
|
2202
|
+
value: e.stack,
|
|
2203
|
+
type: "string"
|
|
2204
|
+
},
|
|
2205
|
+
xpath: {
|
|
2206
|
+
value: xpath,
|
|
2207
|
+
type: "string"
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
1763
2210
|
});
|
|
1764
2211
|
throw new PlaywrightCommandException(e.message);
|
|
1765
2212
|
}
|
|
@@ -1770,36 +2217,80 @@ Trace: ${e.stack}`,
|
|
|
1770
2217
|
} catch (e) {
|
|
1771
2218
|
this.logger({
|
|
1772
2219
|
category: "action",
|
|
1773
|
-
message:
|
|
1774
|
-
|
|
1775
|
-
|
|
2220
|
+
message: "error pressing key",
|
|
2221
|
+
level: 1,
|
|
2222
|
+
auxiliary: {
|
|
2223
|
+
error: {
|
|
2224
|
+
value: e.message,
|
|
2225
|
+
type: "string"
|
|
2226
|
+
},
|
|
2227
|
+
trace: {
|
|
2228
|
+
value: e.stack,
|
|
2229
|
+
type: "string"
|
|
2230
|
+
},
|
|
2231
|
+
key: {
|
|
2232
|
+
value: (_b = (_a = args[0]) == null ? void 0 : _a.toString()) != null ? _b : "unknown",
|
|
2233
|
+
type: "string"
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
1776
2236
|
});
|
|
1777
2237
|
throw new PlaywrightCommandException(e.message);
|
|
1778
2238
|
}
|
|
1779
2239
|
} else if (typeof locator[method] === "function") {
|
|
1780
2240
|
this.logger({
|
|
1781
2241
|
category: "action",
|
|
1782
|
-
message:
|
|
1783
|
-
level: 2
|
|
2242
|
+
message: "page URL before action",
|
|
2243
|
+
level: 2,
|
|
2244
|
+
auxiliary: {
|
|
2245
|
+
url: {
|
|
2246
|
+
value: this.stagehand.page.url(),
|
|
2247
|
+
type: "string"
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
1784
2250
|
});
|
|
1785
2251
|
try {
|
|
1786
2252
|
yield locator[method](...args);
|
|
1787
2253
|
} catch (e) {
|
|
1788
2254
|
this.logger({
|
|
1789
2255
|
category: "action",
|
|
1790
|
-
message:
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2256
|
+
message: "error performing method",
|
|
2257
|
+
level: 1,
|
|
2258
|
+
auxiliary: {
|
|
2259
|
+
error: {
|
|
2260
|
+
value: e.message,
|
|
2261
|
+
type: "string"
|
|
2262
|
+
},
|
|
2263
|
+
trace: {
|
|
2264
|
+
value: e.stack,
|
|
2265
|
+
type: "string"
|
|
2266
|
+
},
|
|
2267
|
+
xpath: {
|
|
2268
|
+
value: xpath,
|
|
2269
|
+
type: "string"
|
|
2270
|
+
},
|
|
2271
|
+
method: {
|
|
2272
|
+
value: method,
|
|
2273
|
+
type: "string"
|
|
2274
|
+
},
|
|
2275
|
+
args: {
|
|
2276
|
+
value: JSON.stringify(args),
|
|
2277
|
+
type: "object"
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
1795
2280
|
});
|
|
1796
2281
|
throw new PlaywrightCommandException(e.message);
|
|
1797
2282
|
}
|
|
1798
2283
|
if (method === "click") {
|
|
1799
2284
|
this.logger({
|
|
1800
2285
|
category: "action",
|
|
1801
|
-
message:
|
|
1802
|
-
level: 1
|
|
2286
|
+
message: "clicking element, checking for page navigation",
|
|
2287
|
+
level: 1,
|
|
2288
|
+
auxiliary: {
|
|
2289
|
+
xpath: {
|
|
2290
|
+
value: xpath,
|
|
2291
|
+
type: "string"
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
1803
2294
|
});
|
|
1804
2295
|
const newOpenedTab = yield Promise.race([
|
|
1805
2296
|
new Promise((resolve) => {
|
|
@@ -1809,14 +2300,26 @@ Trace: ${e.stack}`,
|
|
|
1809
2300
|
]);
|
|
1810
2301
|
this.logger({
|
|
1811
2302
|
category: "action",
|
|
1812
|
-
message:
|
|
1813
|
-
level: 1
|
|
2303
|
+
message: "clicked element",
|
|
2304
|
+
level: 1,
|
|
2305
|
+
auxiliary: {
|
|
2306
|
+
newOpenedTab: {
|
|
2307
|
+
value: newOpenedTab ? "opened a new tab" : "no new tabs opened",
|
|
2308
|
+
type: "string"
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
1814
2311
|
});
|
|
1815
2312
|
if (newOpenedTab) {
|
|
1816
2313
|
this.logger({
|
|
1817
2314
|
category: "action",
|
|
1818
|
-
message:
|
|
1819
|
-
level: 1
|
|
2315
|
+
message: "new page detected (new tab) with URL",
|
|
2316
|
+
level: 1,
|
|
2317
|
+
auxiliary: {
|
|
2318
|
+
url: {
|
|
2319
|
+
value: newOpenedTab.url(),
|
|
2320
|
+
type: "string"
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
1820
2323
|
});
|
|
1821
2324
|
yield newOpenedTab.close();
|
|
1822
2325
|
yield this.stagehand.page.goto(newOpenedTab.url());
|
|
@@ -1829,28 +2332,40 @@ Trace: ${e.stack}`,
|
|
|
1829
2332
|
]).catch((e) => {
|
|
1830
2333
|
this.logger({
|
|
1831
2334
|
category: "action",
|
|
1832
|
-
message:
|
|
2335
|
+
message: "network idle timeout hit",
|
|
1833
2336
|
level: 1
|
|
1834
2337
|
});
|
|
1835
2338
|
});
|
|
1836
2339
|
this.logger({
|
|
1837
2340
|
category: "action",
|
|
1838
|
-
message:
|
|
2341
|
+
message: "finished waiting for (possible) page navigation",
|
|
1839
2342
|
level: 1
|
|
1840
2343
|
});
|
|
1841
2344
|
if (this.stagehand.page.url() !== initialUrl) {
|
|
1842
2345
|
this.logger({
|
|
1843
2346
|
category: "action",
|
|
1844
|
-
message:
|
|
1845
|
-
level: 1
|
|
2347
|
+
message: "new page detected with URL",
|
|
2348
|
+
level: 1,
|
|
2349
|
+
auxiliary: {
|
|
2350
|
+
url: {
|
|
2351
|
+
value: this.stagehand.page.url(),
|
|
2352
|
+
type: "string"
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
1846
2355
|
});
|
|
1847
2356
|
}
|
|
1848
2357
|
}
|
|
1849
2358
|
} else {
|
|
1850
2359
|
this.logger({
|
|
1851
2360
|
category: "action",
|
|
1852
|
-
message:
|
|
1853
|
-
level: 1
|
|
2361
|
+
message: "chosen method is invalid",
|
|
2362
|
+
level: 1,
|
|
2363
|
+
auxiliary: {
|
|
2364
|
+
method: {
|
|
2365
|
+
value: method,
|
|
2366
|
+
type: "string"
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
1854
2369
|
});
|
|
1855
2370
|
throw new PlaywrightCommandMethodNotSupportedException(
|
|
1856
2371
|
`Method ${method} not supported`
|
|
@@ -1892,8 +2407,18 @@ Trace: ${e.stack}`,
|
|
|
1892
2407
|
} catch (e) {
|
|
1893
2408
|
this.logger({
|
|
1894
2409
|
category: "action",
|
|
1895
|
-
message:
|
|
1896
|
-
level: 1
|
|
2410
|
+
message: "element not found within timeout",
|
|
2411
|
+
level: 1,
|
|
2412
|
+
auxiliary: {
|
|
2413
|
+
xpath: {
|
|
2414
|
+
value: xpath,
|
|
2415
|
+
type: "string"
|
|
2416
|
+
},
|
|
2417
|
+
timeout_ms: {
|
|
2418
|
+
value: timeout.toString(),
|
|
2419
|
+
type: "integer"
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
1897
2422
|
});
|
|
1898
2423
|
return null;
|
|
1899
2424
|
}
|
|
@@ -1903,34 +2428,62 @@ Trace: ${e.stack}`,
|
|
|
1903
2428
|
return __async(this, null, function* () {
|
|
1904
2429
|
this.logger({
|
|
1905
2430
|
category: "action",
|
|
1906
|
-
message:
|
|
1907
|
-
level: 1
|
|
2431
|
+
message: "checking if cached step is valid",
|
|
2432
|
+
level: 1,
|
|
2433
|
+
auxiliary: {
|
|
2434
|
+
xpath: {
|
|
2435
|
+
value: cachedStep.xpath,
|
|
2436
|
+
type: "string"
|
|
2437
|
+
},
|
|
2438
|
+
savedComponentString: {
|
|
2439
|
+
value: cachedStep.savedComponentString,
|
|
2440
|
+
type: "string"
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
1908
2443
|
});
|
|
1909
2444
|
try {
|
|
1910
2445
|
const locator = yield this.getElement(cachedStep.xpath);
|
|
1911
2446
|
if (!locator) {
|
|
1912
2447
|
this.logger({
|
|
1913
2448
|
category: "action",
|
|
1914
|
-
message:
|
|
1915
|
-
level: 1
|
|
2449
|
+
message: "locator not found for xpath",
|
|
2450
|
+
level: 1,
|
|
2451
|
+
auxiliary: {
|
|
2452
|
+
xpath: {
|
|
2453
|
+
value: cachedStep.xpath,
|
|
2454
|
+
type: "string"
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
1916
2457
|
});
|
|
1917
2458
|
return false;
|
|
1918
2459
|
}
|
|
1919
2460
|
this.logger({
|
|
1920
2461
|
category: "action",
|
|
1921
|
-
message:
|
|
1922
|
-
level: 1
|
|
2462
|
+
message: "locator element",
|
|
2463
|
+
level: 1,
|
|
2464
|
+
auxiliary: {
|
|
2465
|
+
componentString: {
|
|
2466
|
+
value: yield this._getComponentString(locator),
|
|
2467
|
+
type: "string"
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
1923
2470
|
});
|
|
1924
2471
|
let currentComponent = yield this._getComponentString(locator);
|
|
1925
2472
|
this.logger({
|
|
1926
2473
|
category: "action",
|
|
1927
|
-
message:
|
|
1928
|
-
level: 1
|
|
2474
|
+
message: "current text",
|
|
2475
|
+
level: 1,
|
|
2476
|
+
auxiliary: {
|
|
2477
|
+
componentString: {
|
|
2478
|
+
value: currentComponent,
|
|
2479
|
+
type: "string"
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
1929
2482
|
});
|
|
1930
2483
|
if (!currentComponent || !cachedStep.savedComponentString) {
|
|
1931
2484
|
this.logger({
|
|
1932
2485
|
category: "action",
|
|
1933
|
-
message:
|
|
2486
|
+
message: "current text or cached text is undefined",
|
|
1934
2487
|
level: 1
|
|
1935
2488
|
});
|
|
1936
2489
|
return false;
|
|
@@ -1940,8 +2493,18 @@ Trace: ${e.stack}`,
|
|
|
1940
2493
|
if (normalizedCurrentText !== normalizedCachedText) {
|
|
1941
2494
|
this.logger({
|
|
1942
2495
|
category: "action",
|
|
1943
|
-
message:
|
|
1944
|
-
level: 1
|
|
2496
|
+
message: "current text and cached text do not match",
|
|
2497
|
+
level: 1,
|
|
2498
|
+
auxiliary: {
|
|
2499
|
+
currentText: {
|
|
2500
|
+
value: normalizedCurrentText,
|
|
2501
|
+
type: "string"
|
|
2502
|
+
},
|
|
2503
|
+
cachedText: {
|
|
2504
|
+
value: normalizedCachedText,
|
|
2505
|
+
type: "string"
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
1945
2508
|
});
|
|
1946
2509
|
return false;
|
|
1947
2510
|
}
|
|
@@ -1949,9 +2512,18 @@ Trace: ${e.stack}`,
|
|
|
1949
2512
|
} catch (e) {
|
|
1950
2513
|
this.logger({
|
|
1951
2514
|
category: "action",
|
|
1952
|
-
message:
|
|
1953
|
-
|
|
1954
|
-
|
|
2515
|
+
message: "error checking if cached step is valid",
|
|
2516
|
+
level: 1,
|
|
2517
|
+
auxiliary: {
|
|
2518
|
+
error: {
|
|
2519
|
+
value: e.message,
|
|
2520
|
+
type: "string"
|
|
2521
|
+
},
|
|
2522
|
+
trace: {
|
|
2523
|
+
value: e.stack,
|
|
2524
|
+
type: "string"
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
1955
2527
|
});
|
|
1956
2528
|
return false;
|
|
1957
2529
|
}
|
|
@@ -1987,6 +2559,10 @@ Trace: ${e.stack}`,
|
|
|
1987
2559
|
model,
|
|
1988
2560
|
domSettleTimeoutMs
|
|
1989
2561
|
}) {
|
|
2562
|
+
var _a, _b;
|
|
2563
|
+
if (!this.enableCaching) {
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
1990
2566
|
const cacheObj = {
|
|
1991
2567
|
url: this.stagehand.page.url(),
|
|
1992
2568
|
action,
|
|
@@ -1995,24 +2571,40 @@ Trace: ${e.stack}`,
|
|
|
1995
2571
|
};
|
|
1996
2572
|
this.logger({
|
|
1997
2573
|
category: "action",
|
|
1998
|
-
message:
|
|
1999
|
-
level: 1
|
|
2574
|
+
message: "checking action cache",
|
|
2575
|
+
level: 1,
|
|
2576
|
+
auxiliary: {
|
|
2577
|
+
cacheObj: {
|
|
2578
|
+
value: JSON.stringify(cacheObj),
|
|
2579
|
+
type: "object"
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2000
2582
|
});
|
|
2001
2583
|
const cachedStep = yield this.actionCache.getActionStep(cacheObj);
|
|
2002
2584
|
if (!cachedStep) {
|
|
2003
2585
|
this.logger({
|
|
2004
2586
|
category: "action",
|
|
2005
|
-
message:
|
|
2006
|
-
level: 1
|
|
2587
|
+
message: "action cache miss",
|
|
2588
|
+
level: 1,
|
|
2589
|
+
auxiliary: {
|
|
2590
|
+
cacheObj: {
|
|
2591
|
+
value: JSON.stringify(cacheObj),
|
|
2592
|
+
type: "object"
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2007
2595
|
});
|
|
2008
2596
|
return null;
|
|
2009
2597
|
}
|
|
2010
2598
|
this.logger({
|
|
2011
2599
|
category: "action",
|
|
2012
|
-
message:
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2600
|
+
message: "action cache semi-hit",
|
|
2601
|
+
level: 1,
|
|
2602
|
+
auxiliary: {
|
|
2603
|
+
playwrightCommand: {
|
|
2604
|
+
value: JSON.stringify(cachedStep.playwrightCommand),
|
|
2605
|
+
type: "object"
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2016
2608
|
});
|
|
2017
2609
|
try {
|
|
2018
2610
|
const validXpath = yield this._getValidCachedStepXpath({
|
|
@@ -2021,24 +2613,40 @@ Trace: ${e.stack}`,
|
|
|
2021
2613
|
});
|
|
2022
2614
|
this.logger({
|
|
2023
2615
|
category: "action",
|
|
2024
|
-
message:
|
|
2025
|
-
level: 1
|
|
2616
|
+
message: "cached action step is valid",
|
|
2617
|
+
level: 1,
|
|
2618
|
+
auxiliary: {
|
|
2619
|
+
validXpath: {
|
|
2620
|
+
value: validXpath,
|
|
2621
|
+
type: "string"
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2026
2624
|
});
|
|
2027
2625
|
if (!validXpath) {
|
|
2028
2626
|
this.logger({
|
|
2029
2627
|
category: "action",
|
|
2030
|
-
message:
|
|
2031
|
-
level: 1
|
|
2628
|
+
message: "cached action step is invalid, removing...",
|
|
2629
|
+
level: 1,
|
|
2630
|
+
auxiliary: {
|
|
2631
|
+
cacheObj: {
|
|
2632
|
+
value: JSON.stringify(cacheObj),
|
|
2633
|
+
type: "object"
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2032
2636
|
});
|
|
2033
|
-
yield this.actionCache.removeActionStep(cacheObj);
|
|
2637
|
+
yield (_a = this.actionCache) == null ? void 0 : _a.removeActionStep(cacheObj);
|
|
2034
2638
|
return null;
|
|
2035
2639
|
}
|
|
2036
2640
|
this.logger({
|
|
2037
2641
|
category: "action",
|
|
2038
|
-
message:
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2642
|
+
message: "action cache hit",
|
|
2643
|
+
level: 1,
|
|
2644
|
+
auxiliary: {
|
|
2645
|
+
playwrightCommand: {
|
|
2646
|
+
value: JSON.stringify(cachedStep.playwrightCommand),
|
|
2647
|
+
type: "object"
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2042
2650
|
});
|
|
2043
2651
|
cachedStep.playwrightCommand.args = cachedStep.playwrightCommand.args.map(
|
|
2044
2652
|
(arg) => {
|
|
@@ -2070,13 +2678,19 @@ Trace: ${e.stack}`,
|
|
|
2070
2678
|
});
|
|
2071
2679
|
this.logger({
|
|
2072
2680
|
category: "action",
|
|
2073
|
-
message:
|
|
2074
|
-
level: 1
|
|
2681
|
+
message: "action completion verification result from cache",
|
|
2682
|
+
level: 1,
|
|
2683
|
+
auxiliary: {
|
|
2684
|
+
actionCompleted: {
|
|
2685
|
+
value: actionCompleted.toString(),
|
|
2686
|
+
type: "boolean"
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2075
2689
|
});
|
|
2076
2690
|
if (actionCompleted) {
|
|
2077
2691
|
return {
|
|
2078
2692
|
success: true,
|
|
2079
|
-
message: "
|
|
2693
|
+
message: "action completed successfully using cached step",
|
|
2080
2694
|
action
|
|
2081
2695
|
};
|
|
2082
2696
|
}
|
|
@@ -2098,11 +2712,20 @@ Trace: ${e.stack}`,
|
|
|
2098
2712
|
} catch (exception) {
|
|
2099
2713
|
this.logger({
|
|
2100
2714
|
category: "action",
|
|
2101
|
-
message:
|
|
2102
|
-
|
|
2103
|
-
|
|
2715
|
+
message: "error performing cached action step",
|
|
2716
|
+
level: 1,
|
|
2717
|
+
auxiliary: {
|
|
2718
|
+
error: {
|
|
2719
|
+
value: exception.message,
|
|
2720
|
+
type: "string"
|
|
2721
|
+
},
|
|
2722
|
+
trace: {
|
|
2723
|
+
value: exception.stack,
|
|
2724
|
+
type: "string"
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2104
2727
|
});
|
|
2105
|
-
yield this.actionCache.removeActionStep(cacheObj);
|
|
2728
|
+
yield (_b = this.actionCache) == null ? void 0 : _b.removeActionStep(cacheObj);
|
|
2106
2729
|
return null;
|
|
2107
2730
|
}
|
|
2108
2731
|
});
|
|
@@ -2122,7 +2745,7 @@ Trace: ${exception.stack}`,
|
|
|
2122
2745
|
skipActionCacheForThisStep = false,
|
|
2123
2746
|
domSettleTimeoutMs
|
|
2124
2747
|
}) {
|
|
2125
|
-
var _a;
|
|
2748
|
+
var _a, _b;
|
|
2126
2749
|
try {
|
|
2127
2750
|
yield this.waitForSettledDom(domSettleTimeoutMs);
|
|
2128
2751
|
yield this.startDomDebug();
|
|
@@ -2164,20 +2787,40 @@ Trace: ${exception.stack}`,
|
|
|
2164
2787
|
if (!modelsWithVision.includes(model) && (useVision !== false || verifierUseVision)) {
|
|
2165
2788
|
this.logger({
|
|
2166
2789
|
category: "action",
|
|
2167
|
-
message:
|
|
2168
|
-
level: 1
|
|
2790
|
+
message: "model does not support vision but useVision was not false. defaulting to false.",
|
|
2791
|
+
level: 1,
|
|
2792
|
+
auxiliary: {
|
|
2793
|
+
model: {
|
|
2794
|
+
value: model,
|
|
2795
|
+
type: "string"
|
|
2796
|
+
},
|
|
2797
|
+
useVision: {
|
|
2798
|
+
value: useVision.toString(),
|
|
2799
|
+
type: "boolean"
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2169
2802
|
});
|
|
2170
2803
|
useVision = false;
|
|
2171
2804
|
verifierUseVision = false;
|
|
2172
2805
|
}
|
|
2173
2806
|
this.logger({
|
|
2174
2807
|
category: "action",
|
|
2175
|
-
message:
|
|
2176
|
-
level: 2
|
|
2808
|
+
message: "running / continuing action",
|
|
2809
|
+
level: 2,
|
|
2810
|
+
auxiliary: {
|
|
2811
|
+
action: {
|
|
2812
|
+
value: action,
|
|
2813
|
+
type: "string"
|
|
2814
|
+
},
|
|
2815
|
+
pageUrl: {
|
|
2816
|
+
value: this.stagehand.page.url(),
|
|
2817
|
+
type: "string"
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2177
2820
|
});
|
|
2178
2821
|
this.logger({
|
|
2179
2822
|
category: "action",
|
|
2180
|
-
message:
|
|
2823
|
+
message: "processing DOM",
|
|
2181
2824
|
level: 2
|
|
2182
2825
|
});
|
|
2183
2826
|
const { outputString, selectorMap, chunk, chunks } = yield this.stagehand.page.evaluate(
|
|
@@ -2188,22 +2831,47 @@ Trace: ${exception.stack}`,
|
|
|
2188
2831
|
);
|
|
2189
2832
|
this.logger({
|
|
2190
2833
|
category: "action",
|
|
2191
|
-
message:
|
|
2192
|
-
level: 1
|
|
2834
|
+
message: "looking at chunk",
|
|
2835
|
+
level: 1,
|
|
2836
|
+
auxiliary: {
|
|
2837
|
+
chunk: {
|
|
2838
|
+
value: chunk.toString(),
|
|
2839
|
+
type: "integer"
|
|
2840
|
+
},
|
|
2841
|
+
chunks: {
|
|
2842
|
+
value: chunks.length.toString(),
|
|
2843
|
+
type: "integer"
|
|
2844
|
+
},
|
|
2845
|
+
chunksSeen: {
|
|
2846
|
+
value: chunksSeen.length.toString(),
|
|
2847
|
+
type: "integer"
|
|
2848
|
+
},
|
|
2849
|
+
chunksLeft: {
|
|
2850
|
+
value: (chunks.length - chunksSeen.length).toString(),
|
|
2851
|
+
type: "integer"
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2193
2854
|
});
|
|
2194
2855
|
let annotatedScreenshot;
|
|
2195
2856
|
if (useVision === true) {
|
|
2196
2857
|
if (!modelsWithVision.includes(model)) {
|
|
2197
2858
|
this.logger({
|
|
2198
2859
|
category: "action",
|
|
2199
|
-
message:
|
|
2200
|
-
level: 1
|
|
2860
|
+
message: "model does not support vision. skipping vision processing.",
|
|
2861
|
+
level: 1,
|
|
2862
|
+
auxiliary: {
|
|
2863
|
+
model: {
|
|
2864
|
+
value: model,
|
|
2865
|
+
type: "string"
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2201
2868
|
});
|
|
2202
2869
|
} else {
|
|
2203
2870
|
const screenshotService = new ScreenshotService(
|
|
2204
2871
|
this.stagehand.page,
|
|
2205
2872
|
selectorMap,
|
|
2206
|
-
this.verbose
|
|
2873
|
+
this.verbose,
|
|
2874
|
+
this.logger
|
|
2207
2875
|
);
|
|
2208
2876
|
annotatedScreenshot = yield screenshotService.getAnnotatedScreenshot(false);
|
|
2209
2877
|
}
|
|
@@ -2221,8 +2889,14 @@ Trace: ${exception.stack}`,
|
|
|
2221
2889
|
});
|
|
2222
2890
|
this.logger({
|
|
2223
2891
|
category: "action",
|
|
2224
|
-
message:
|
|
2225
|
-
level: 1
|
|
2892
|
+
message: "received response from LLM",
|
|
2893
|
+
level: 1,
|
|
2894
|
+
auxiliary: {
|
|
2895
|
+
response: {
|
|
2896
|
+
value: JSON.stringify(response),
|
|
2897
|
+
type: "object"
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2226
2900
|
});
|
|
2227
2901
|
yield this.cleanupDomDebug();
|
|
2228
2902
|
if (!response) {
|
|
@@ -2230,8 +2904,14 @@ Trace: ${exception.stack}`,
|
|
|
2230
2904
|
chunksSeen.push(chunk);
|
|
2231
2905
|
this.logger({
|
|
2232
2906
|
category: "action",
|
|
2233
|
-
message:
|
|
2234
|
-
level: 1
|
|
2907
|
+
message: "no action found in current chunk",
|
|
2908
|
+
level: 1,
|
|
2909
|
+
auxiliary: {
|
|
2910
|
+
chunksSeen: {
|
|
2911
|
+
value: chunksSeen.length.toString(),
|
|
2912
|
+
type: "integer"
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2235
2915
|
});
|
|
2236
2916
|
return this.act({
|
|
2237
2917
|
action,
|
|
@@ -2249,8 +2929,14 @@ Trace: ${exception.stack}`,
|
|
|
2249
2929
|
} else if (useVision === "fallback") {
|
|
2250
2930
|
this.logger({
|
|
2251
2931
|
category: "action",
|
|
2252
|
-
message:
|
|
2253
|
-
level: 1
|
|
2932
|
+
message: "switching to vision-based processing",
|
|
2933
|
+
level: 1,
|
|
2934
|
+
auxiliary: {
|
|
2935
|
+
useVision: {
|
|
2936
|
+
value: useVision.toString(),
|
|
2937
|
+
type: "string"
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2254
2940
|
});
|
|
2255
2941
|
yield this.stagehand.page.evaluate(() => window.scrollToHeight(0));
|
|
2256
2942
|
return yield this.act({
|
|
@@ -2269,7 +2955,7 @@ Trace: ${exception.stack}`,
|
|
|
2269
2955
|
} else {
|
|
2270
2956
|
if (this.enableCaching) {
|
|
2271
2957
|
this.llmProvider.cleanRequestCache(requestId);
|
|
2272
|
-
this.actionCache.deleteCacheForRequestId(requestId);
|
|
2958
|
+
(_a = this.actionCache) == null ? void 0 : _a.deleteCacheForRequestId(requestId);
|
|
2273
2959
|
}
|
|
2274
2960
|
return {
|
|
2275
2961
|
success: false,
|
|
@@ -2283,13 +2969,29 @@ Trace: ${exception.stack}`,
|
|
|
2283
2969
|
const method = response["method"];
|
|
2284
2970
|
const args = response["args"];
|
|
2285
2971
|
const elementLines = outputString.split("\n");
|
|
2286
|
-
const elementText = ((
|
|
2972
|
+
const elementText = ((_b = elementLines.find((line) => line.startsWith(`${elementId}:`))) == null ? void 0 : _b.split(":")[1]) || "Element not found";
|
|
2287
2973
|
this.logger({
|
|
2288
2974
|
category: "action",
|
|
2289
|
-
message:
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2975
|
+
message: "executing method",
|
|
2976
|
+
level: 1,
|
|
2977
|
+
auxiliary: {
|
|
2978
|
+
method: {
|
|
2979
|
+
value: method,
|
|
2980
|
+
type: "string"
|
|
2981
|
+
},
|
|
2982
|
+
elementId: {
|
|
2983
|
+
value: elementId.toString(),
|
|
2984
|
+
type: "integer"
|
|
2985
|
+
},
|
|
2986
|
+
xpaths: {
|
|
2987
|
+
value: JSON.stringify(xpaths),
|
|
2988
|
+
type: "object"
|
|
2989
|
+
},
|
|
2990
|
+
args: {
|
|
2991
|
+
value: JSON.stringify(args),
|
|
2992
|
+
type: "object"
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2293
2995
|
});
|
|
2294
2996
|
try {
|
|
2295
2997
|
const initialUrl = this.stagehand.page.url();
|
|
@@ -2333,9 +3035,18 @@ Trace: ${exception.stack}`,
|
|
|
2333
3035
|
}).catch((e) => {
|
|
2334
3036
|
this.logger({
|
|
2335
3037
|
category: "action",
|
|
2336
|
-
message:
|
|
2337
|
-
|
|
2338
|
-
|
|
3038
|
+
message: "error adding action step to cache",
|
|
3039
|
+
level: 1,
|
|
3040
|
+
auxiliary: {
|
|
3041
|
+
error: {
|
|
3042
|
+
value: e.message,
|
|
3043
|
+
type: "string"
|
|
3044
|
+
},
|
|
3045
|
+
trace: {
|
|
3046
|
+
value: e.stack,
|
|
3047
|
+
type: "string"
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
2339
3050
|
});
|
|
2340
3051
|
});
|
|
2341
3052
|
}
|
|
@@ -2356,7 +3067,7 @@ Trace: ${e.stack}`,
|
|
|
2356
3067
|
if (!actionCompleted) {
|
|
2357
3068
|
this.logger({
|
|
2358
3069
|
category: "action",
|
|
2359
|
-
message:
|
|
3070
|
+
message: "continuing to next action step",
|
|
2360
3071
|
level: 1
|
|
2361
3072
|
});
|
|
2362
3073
|
return this.act({
|
|
@@ -2375,7 +3086,7 @@ Trace: ${e.stack}`,
|
|
|
2375
3086
|
} else {
|
|
2376
3087
|
this.logger({
|
|
2377
3088
|
category: "action",
|
|
2378
|
-
message:
|
|
3089
|
+
message: "action completed successfully",
|
|
2379
3090
|
level: 1
|
|
2380
3091
|
});
|
|
2381
3092
|
yield this._recordAction(action, response.step);
|
|
@@ -2388,9 +3099,22 @@ Trace: ${e.stack}`,
|
|
|
2388
3099
|
} catch (error) {
|
|
2389
3100
|
this.logger({
|
|
2390
3101
|
category: "action",
|
|
2391
|
-
message:
|
|
2392
|
-
|
|
2393
|
-
|
|
3102
|
+
message: "error performing action - d",
|
|
3103
|
+
level: 1,
|
|
3104
|
+
auxiliary: {
|
|
3105
|
+
error: {
|
|
3106
|
+
value: error.message,
|
|
3107
|
+
type: "string"
|
|
3108
|
+
},
|
|
3109
|
+
trace: {
|
|
3110
|
+
value: error.stack,
|
|
3111
|
+
type: "string"
|
|
3112
|
+
},
|
|
3113
|
+
retries: {
|
|
3114
|
+
value: retries.toString(),
|
|
3115
|
+
type: "integer"
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
2394
3118
|
});
|
|
2395
3119
|
if (retries < 2) {
|
|
2396
3120
|
return this.act({
|
|
@@ -2415,16 +3139,25 @@ Trace: ${error.stack}`,
|
|
|
2415
3139
|
}
|
|
2416
3140
|
return {
|
|
2417
3141
|
success: false,
|
|
2418
|
-
message:
|
|
3142
|
+
message: "error performing action - a",
|
|
2419
3143
|
action
|
|
2420
3144
|
};
|
|
2421
3145
|
}
|
|
2422
3146
|
} catch (error) {
|
|
2423
3147
|
this.logger({
|
|
2424
3148
|
category: "action",
|
|
2425
|
-
message:
|
|
2426
|
-
|
|
2427
|
-
|
|
3149
|
+
message: "error performing action - b",
|
|
3150
|
+
level: 1,
|
|
3151
|
+
auxiliary: {
|
|
3152
|
+
error: {
|
|
3153
|
+
value: error.message,
|
|
3154
|
+
type: "string"
|
|
3155
|
+
},
|
|
3156
|
+
trace: {
|
|
3157
|
+
value: error.stack,
|
|
3158
|
+
type: "string"
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
2428
3161
|
});
|
|
2429
3162
|
if (this.enableCaching) {
|
|
2430
3163
|
this.llmProvider.cleanRequestCache(requestId);
|
|
@@ -2440,14 +3173,18 @@ Trace: ${error.stack}`,
|
|
|
2440
3173
|
}
|
|
2441
3174
|
};
|
|
2442
3175
|
|
|
3176
|
+
// lib/dom/build/scriptContent.ts
|
|
3177
|
+
var scriptContent = '(() => {\n // lib/dom/xpathUtils.ts\n function getParentElement(node) {\n return isElementNode(node) ? node.parentElement : node.parentNode;\n }\n function getCombinations(attributes, size) {\n const results = [];\n function helper(start, combo) {\n if (combo.length === size) {\n results.push([...combo]);\n return;\n }\n for (let i = start; i < attributes.length; i++) {\n combo.push(attributes[i]);\n helper(i + 1, combo);\n combo.pop();\n }\n }\n helper(0, []);\n return results;\n }\n function isXPathFirstResultElement(xpath, target) {\n try {\n const result = document.evaluate(\n xpath,\n document.documentElement,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n return result.snapshotItem(0) === target;\n } catch (error) {\n console.warn(`Invalid XPath expression: ${xpath}`, error);\n return false;\n }\n }\n function escapeXPathString(value) {\n if (value.includes("\'")) {\n if (value.includes(\'"\')) {\n return "concat(" + value.split(/(\'+)/).map((part) => {\n if (part === "\'") {\n return `"\'"`;\n } else if (part.startsWith("\'") && part.endsWith("\'")) {\n return `"${part}"`;\n } else {\n return `\'${part}\'`;\n }\n }).join(",") + ")";\n } else {\n return `"${value}"`;\n }\n } else {\n return `\'${value}\'`;\n }\n }\n async function generateXPathsForElement(element) {\n if (!element) return [];\n const [complexXPath, standardXPath, idBasedXPath] = await Promise.all([\n generateComplexXPath(element),\n generateStandardXPath(element),\n generatedIdBasedXPath(element)\n ]);\n return [standardXPath, ...idBasedXPath ? [idBasedXPath] : [], complexXPath];\n }\n async function generateComplexXPath(element) {\n const parts = [];\n let currentElement = element;\n while (currentElement && (isTextNode(currentElement) || isElementNode(currentElement))) {\n if (isElementNode(currentElement)) {\n const el = currentElement;\n let selector = el.tagName.toLowerCase();\n const attributePriority = [\n "data-qa",\n "data-component",\n "data-role",\n "role",\n "aria-role",\n "type",\n "name",\n "aria-label",\n "placeholder",\n "title",\n "alt"\n ];\n const attributes = attributePriority.map((attr) => {\n let value = el.getAttribute(attr);\n if (attr === "href-full" && value) {\n value = el.getAttribute("href");\n }\n return value ? { attr: attr === "href-full" ? "href" : attr, value } : null;\n }).filter((attr) => attr !== null);\n let uniqueSelector = "";\n for (let i = 1; i <= attributes.length; i++) {\n const combinations = getCombinations(attributes, i);\n for (const combo of combinations) {\n const conditions = combo.map((a) => `@${a.attr}=${escapeXPathString(a.value)}`).join(" and ");\n const xpath2 = `//${selector}[${conditions}]`;\n if (isXPathFirstResultElement(xpath2, el)) {\n uniqueSelector = xpath2;\n break;\n }\n }\n if (uniqueSelector) break;\n }\n if (uniqueSelector) {\n parts.unshift(uniqueSelector.replace("//", ""));\n break;\n } else {\n const parent = getParentElement(el);\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (sibling) => sibling.tagName === el.tagName\n );\n const index = siblings.indexOf(el) + 1;\n selector += siblings.length > 1 ? `[${index}]` : "";\n }\n parts.unshift(selector);\n }\n }\n currentElement = getParentElement(currentElement);\n }\n const xpath = "//" + parts.join("/");\n return xpath;\n }\n async function generateStandardXPath(element) {\n const parts = [];\n while (element && (isTextNode(element) || isElementNode(element))) {\n let index = 0;\n let hasSameTypeSiblings = false;\n const siblings = element.parentElement ? Array.from(element.parentElement.childNodes) : [];\n for (let i = 0; i < siblings.length; i++) {\n const sibling = siblings[i];\n if (sibling.nodeType === element.nodeType && sibling.nodeName === element.nodeName) {\n index = index + 1;\n hasSameTypeSiblings = true;\n if (sibling.isSameNode(element)) {\n break;\n }\n }\n }\n if (element.nodeName !== "#text") {\n const tagName = element.nodeName.toLowerCase();\n const pathIndex = hasSameTypeSiblings ? `[${index}]` : "";\n parts.unshift(`${tagName}${pathIndex}`);\n }\n element = element.parentElement;\n }\n return parts.length ? `/${parts.join("/")}` : "";\n }\n async function generatedIdBasedXPath(element) {\n if (isElementNode(element) && element.id) {\n return `//*[@id=\'${element.id}\']`;\n }\n return null;\n }\n\n // lib/dom/process.ts\n function isElementNode(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n function isTextNode(node) {\n return node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim());\n }\n async function processDom(chunksSeen) {\n const { chunk, chunksArray } = await pickChunk(chunksSeen);\n const { outputString, selectorMap } = await processElements(chunk);\n console.log(\n `Stagehand (Browser Process): Extracted dom elements:\n${outputString}`\n );\n return {\n outputString,\n selectorMap,\n chunk,\n chunks: chunksArray\n };\n }\n async function processAllOfDom() {\n console.log("Stagehand (Browser Process): Processing all of DOM");\n const viewportHeight = window.innerHeight;\n const documentHeight = document.documentElement.scrollHeight;\n const totalChunks = Math.ceil(documentHeight / viewportHeight);\n let index = 0;\n const results = [];\n for (let chunk = 0; chunk < totalChunks; chunk++) {\n const result = await processElements(chunk, true, index);\n results.push(result);\n index += Object.keys(result.selectorMap).length;\n }\n await scrollToHeight(0);\n const allOutputString = results.map((result) => result.outputString).join("");\n const allSelectorMap = results.reduce(\n (acc, result) => ({ ...acc, ...result.selectorMap }),\n {}\n );\n console.log(\n `Stagehand (Browser Process): All dom elements: ${allOutputString}`\n );\n return {\n outputString: allOutputString,\n selectorMap: allSelectorMap\n };\n }\n async function scrollToHeight(height) {\n window.scrollTo({ top: height, left: 0, behavior: "smooth" });\n await new Promise((resolve) => {\n let scrollEndTimer;\n const handleScrollEnd = () => {\n clearTimeout(scrollEndTimer);\n scrollEndTimer = window.setTimeout(() => {\n window.removeEventListener("scroll", handleScrollEnd);\n resolve();\n }, 100);\n };\n window.addEventListener("scroll", handleScrollEnd, { passive: true });\n handleScrollEnd();\n });\n }\n var xpathCache = /* @__PURE__ */ new Map();\n async function processElements(chunk, scrollToChunk = true, indexOffset = 0) {\n console.time("processElements:total");\n const viewportHeight = window.innerHeight;\n const chunkHeight = viewportHeight * chunk;\n const maxScrollTop = document.documentElement.scrollHeight - window.innerHeight;\n const offsetTop = Math.min(chunkHeight, maxScrollTop);\n if (scrollToChunk) {\n console.time("processElements:scroll");\n await scrollToHeight(offsetTop);\n console.timeEnd("processElements:scroll");\n }\n const candidateElements = [];\n const DOMQueue = [...document.body.childNodes];\n console.log("Stagehand (Browser Process): Generating candidate elements");\n console.time("processElements:findCandidates");\n while (DOMQueue.length > 0) {\n const element = DOMQueue.pop();\n let shouldAddElement = false;\n if (element && isElementNode(element)) {\n const childrenCount = element.childNodes.length;\n for (let i = childrenCount - 1; i >= 0; i--) {\n const child = element.childNodes[i];\n DOMQueue.push(child);\n }\n if (isInteractiveElement(element)) {\n if (isActive(element) && isVisible(element)) {\n shouldAddElement = true;\n }\n }\n if (isLeafElement(element)) {\n if (isActive(element) && isVisible(element)) {\n shouldAddElement = true;\n }\n }\n }\n if (element && isTextNode(element) && isTextVisible(element)) {\n shouldAddElement = true;\n }\n if (shouldAddElement) {\n candidateElements.push(element);\n }\n }\n console.timeEnd("processElements:findCandidates");\n const selectorMap = {};\n let outputString = "";\n console.log(\n `Stagehand (Browser Process): Processing candidate elements: ${candidateElements.length}`\n );\n console.time("processElements:processCandidates");\n console.time("processElements:generateXPaths");\n const xpathLists = await Promise.all(\n candidateElements.map(async (element) => {\n if (xpathCache.has(element)) {\n return xpathCache.get(element);\n }\n const xpaths = await generateXPathsForElement(element);\n xpathCache.set(element, xpaths);\n return xpaths;\n })\n );\n console.timeEnd("processElements:generateXPaths");\n candidateElements.forEach((element, index) => {\n const xpaths = xpathLists[index];\n let elementOutput = "";\n if (isTextNode(element)) {\n const textContent = element.textContent?.trim();\n if (textContent) {\n elementOutput += `${index + indexOffset}:${textContent}\n`;\n }\n } else if (isElementNode(element)) {\n const tagName = element.tagName.toLowerCase();\n const attributes = collectEssentialAttributes(element);\n const openingTag = `<${tagName}${attributes ? " " + attributes : ""}>`;\n const closingTag = `</${tagName}>`;\n const textContent = element.textContent?.trim() || "";\n elementOutput += `${index + indexOffset}:${openingTag}${textContent}${closingTag}\n`;\n }\n outputString += elementOutput;\n selectorMap[index + indexOffset] = xpaths;\n });\n console.timeEnd("processElements:processCandidates");\n console.timeEnd("processElements:total");\n return {\n outputString,\n selectorMap\n };\n }\n function collectEssentialAttributes(element) {\n const essentialAttributes = [\n "id",\n "class",\n "href",\n "src",\n "aria-label",\n "aria-name",\n "aria-role",\n "aria-description",\n "aria-expanded",\n "aria-haspopup"\n ];\n const attrs = essentialAttributes.map((attr) => {\n const value = element.getAttribute(attr);\n return value ? `${attr}="${value}"` : "";\n }).filter((attr) => attr !== "");\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith("data-")) {\n attrs.push(`${attr.name}="${attr.value}"`);\n }\n });\n return attrs.join(" ");\n }\n window.processDom = processDom;\n window.processAllOfDom = processAllOfDom;\n window.processElements = processElements;\n window.scrollToHeight = scrollToHeight;\n var leafElementDenyList = ["SVG", "IFRAME", "SCRIPT", "STYLE", "LINK"];\n var interactiveElementTypes = [\n "A",\n "BUTTON",\n "DETAILS",\n "EMBED",\n "INPUT",\n "LABEL",\n "MENU",\n "MENUITEM",\n "OBJECT",\n "SELECT",\n "TEXTAREA",\n "SUMMARY"\n ];\n var interactiveRoles = [\n "button",\n "menu",\n "menuitem",\n "link",\n "checkbox",\n "radio",\n "slider",\n "tab",\n "tabpanel",\n "textbox",\n "combobox",\n "grid",\n "listbox",\n "option",\n "progressbar",\n "scrollbar",\n "searchbox",\n "switch",\n "tree",\n "treeitem",\n "spinbutton",\n "tooltip"\n ];\n var interactiveAriaRoles = ["menu", "menuitem", "button"];\n var isVisible = (element) => {\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0 || rect.top < 0 || rect.top > window.innerHeight) {\n return false;\n }\n if (!isTopElement(element, rect)) {\n return false;\n }\n const visible = element.checkVisibility({\n checkOpacity: true,\n checkVisibilityCSS: true\n });\n return visible;\n };\n var isTextVisible = (element) => {\n const range = document.createRange();\n range.selectNodeContents(element);\n const rect = range.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0 || rect.top < 0 || rect.top > window.innerHeight) {\n return false;\n }\n const parent = element.parentElement;\n if (!parent) {\n return false;\n }\n if (!isTopElement(parent, rect)) {\n return false;\n }\n const visible = parent.checkVisibility({\n checkOpacity: true,\n checkVisibilityCSS: true\n });\n return visible;\n };\n function isTopElement(elem, rect) {\n const points = [\n { x: rect.left + rect.width * 0.25, y: rect.top + rect.height * 0.25 },\n { x: rect.left + rect.width * 0.75, y: rect.top + rect.height * 0.25 },\n { x: rect.left + rect.width * 0.25, y: rect.top + rect.height * 0.75 },\n { x: rect.left + rect.width * 0.75, y: rect.top + rect.height * 0.75 },\n { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }\n ];\n return points.some((point) => {\n const topEl = document.elementFromPoint(point.x, point.y);\n let current = topEl;\n while (current && current !== document.body) {\n if (current.isSameNode(elem)) {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n });\n }\n var isActive = (element) => {\n if (element.hasAttribute("disabled") || element.hasAttribute("hidden") || element.getAttribute("aria-disabled") === "true") {\n return false;\n }\n return true;\n };\n var isInteractiveElement = (element) => {\n const elementType = element.tagName;\n const elementRole = element.getAttribute("role");\n const elementAriaRole = element.getAttribute("aria-role");\n return elementType && interactiveElementTypes.includes(elementType) || elementRole && interactiveRoles.includes(elementRole) || elementAriaRole && interactiveAriaRoles.includes(elementAriaRole);\n };\n var isLeafElement = (element) => {\n if (element.textContent === "") {\n return false;\n }\n if (element.childNodes.length === 0) {\n return !leafElementDenyList.includes(element.tagName);\n }\n if (element.childNodes.length === 1 && isTextNode(element.childNodes[0])) {\n return true;\n }\n return false;\n };\n async function pickChunk(chunksSeen) {\n const viewportHeight = window.innerHeight;\n const documentHeight = document.documentElement.scrollHeight;\n const chunks = Math.ceil(documentHeight / viewportHeight);\n const chunksArray = Array.from({ length: chunks }, (_, i) => i);\n const chunksRemaining = chunksArray.filter((chunk2) => {\n return !chunksSeen.includes(chunk2);\n });\n const currentScrollPosition = window.scrollY;\n const closestChunk = chunksRemaining.reduce((closest, current) => {\n const currentChunkTop = viewportHeight * current;\n const closestChunkTop = viewportHeight * closest;\n return Math.abs(currentScrollPosition - currentChunkTop) < Math.abs(currentScrollPosition - closestChunkTop) ? current : closest;\n }, chunksRemaining[0]);\n const chunk = closestChunk;\n if (chunk === void 0) {\n throw new Error(`No chunks remaining to check: ${chunksRemaining}`);\n }\n return {\n chunk,\n chunksArray\n };\n }\n\n // lib/dom/utils.ts\n async function waitForDomSettle() {\n return new Promise((resolve) => {\n const createTimeout = () => {\n return setTimeout(() => {\n resolve();\n }, 2e3);\n };\n let timeout = createTimeout();\n const observer = new MutationObserver(() => {\n clearTimeout(timeout);\n timeout = createTimeout();\n });\n observer.observe(window.document.body, { childList: true, subtree: true });\n });\n }\n window.waitForDomSettle = waitForDomSettle;\n\n // lib/dom/debug.ts\n async function debugDom() {\n window.chunkNumber = 0;\n const { selectorMap: multiSelectorMap, outputString } = await window.processElements(window.chunkNumber);\n const selectorMap = multiSelectorMapToSelectorMap(multiSelectorMap);\n drawChunk(selectorMap);\n setupChunkNav();\n }\n function multiSelectorMapToSelectorMap(multiSelectorMap) {\n return Object.fromEntries(\n Object.entries(multiSelectorMap).map(([key, selectors]) => [\n Number(key),\n selectors[0]\n ])\n );\n }\n function drawChunk(selectorMap) {\n cleanupMarkers();\n Object.entries(selectorMap).forEach(([_index, selector]) => {\n const element = document.evaluate(\n selector,\n document,\n null,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null\n ).singleNodeValue;\n if (element) {\n let rect;\n if (element.nodeType === Node.ELEMENT_NODE) {\n rect = element.getBoundingClientRect();\n } else {\n const range = document.createRange();\n range.selectNodeContents(element);\n rect = range.getBoundingClientRect();\n }\n const color = "grey";\n const overlay = document.createElement("div");\n overlay.style.position = "absolute";\n overlay.style.left = `${rect.left + window.scrollX}px`;\n overlay.style.top = `${rect.top + window.scrollY}px`;\n overlay.style.padding = "2px";\n overlay.style.width = `${rect.width}px`;\n overlay.style.height = `${rect.height}px`;\n overlay.style.backgroundColor = color;\n overlay.className = "stagehand-marker";\n overlay.style.opacity = "0.3";\n overlay.style.zIndex = "1000000000";\n overlay.style.border = "1px solid";\n overlay.style.pointerEvents = "none";\n document.body.appendChild(overlay);\n }\n });\n }\n async function cleanupDebug() {\n cleanupMarkers();\n cleanupNav();\n }\n function cleanupMarkers() {\n const markers = document.querySelectorAll(".stagehand-marker");\n markers.forEach((marker) => {\n marker.remove();\n });\n }\n function cleanupNav() {\n const stagehandNavElements = document.querySelectorAll(".stagehand-nav");\n stagehandNavElements.forEach((element) => {\n element.remove();\n });\n }\n function setupChunkNav() {\n const viewportHeight = window.innerHeight;\n const documentHeight = document.documentElement.scrollHeight;\n const totalChunks = Math.ceil(documentHeight / viewportHeight);\n if (window.chunkNumber > 0) {\n const prevChunkButton = document.createElement("button");\n prevChunkButton.className = "stagehand-nav";\n prevChunkButton.textContent = "Previous";\n prevChunkButton.style.marginLeft = "50px";\n prevChunkButton.style.position = "fixed";\n prevChunkButton.style.bottom = "10px";\n prevChunkButton.style.left = "50%";\n prevChunkButton.style.transform = "translateX(-50%)";\n prevChunkButton.style.zIndex = "1000000000";\n prevChunkButton.onclick = async () => {\n cleanupMarkers();\n cleanupNav();\n window.chunkNumber -= 1;\n window.scrollTo(0, window.chunkNumber * window.innerHeight);\n await window.waitForDomSettle();\n const { selectorMap: multiSelectorMap } = await window.processElements(\n window.chunkNumber\n );\n const selectorMap = multiSelectorMapToSelectorMap(multiSelectorMap);\n drawChunk(selectorMap);\n setupChunkNav();\n };\n document.body.appendChild(prevChunkButton);\n }\n if (totalChunks > window.chunkNumber) {\n const nextChunkButton = document.createElement("button");\n nextChunkButton.className = "stagehand-nav";\n nextChunkButton.textContent = "Next";\n nextChunkButton.style.marginRight = "50px";\n nextChunkButton.style.position = "fixed";\n nextChunkButton.style.bottom = "10px";\n nextChunkButton.style.right = "50%";\n nextChunkButton.style.transform = "translateX(50%)";\n nextChunkButton.style.zIndex = "1000000000";\n nextChunkButton.onclick = async () => {\n cleanupMarkers();\n cleanupNav();\n window.chunkNumber += 1;\n window.scrollTo(0, window.chunkNumber * window.innerHeight);\n await window.waitForDomSettle();\n const { selectorMap: multiSelectorMap } = await window.processElements(\n window.chunkNumber\n );\n const selectorMap = multiSelectorMapToSelectorMap(multiSelectorMap);\n drawChunk(selectorMap);\n setupChunkNav();\n };\n document.body.appendChild(nextChunkButton);\n }\n }\n window.debugDom = debugDom;\n window.cleanupDebug = cleanupDebug;\n})();\n';
|
|
3178
|
+
|
|
2443
3179
|
// lib/index.ts
|
|
3180
|
+
var import_crypto2 = require("crypto");
|
|
2444
3181
|
require("dotenv").config({ path: ".env" });
|
|
2445
3182
|
function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger, browserbaseSessionCreateParams, browserbaseResumeSessionID) {
|
|
2446
3183
|
return __async(this, null, function* () {
|
|
2447
3184
|
if (env === "BROWSERBASE") {
|
|
2448
3185
|
if (!apiKey) {
|
|
2449
3186
|
logger({
|
|
2450
|
-
category: "
|
|
3187
|
+
category: "init",
|
|
2451
3188
|
message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
|
|
2452
3189
|
level: 0
|
|
2453
3190
|
});
|
|
@@ -2455,7 +3192,7 @@ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger,
|
|
|
2455
3192
|
}
|
|
2456
3193
|
if (!projectId) {
|
|
2457
3194
|
logger({
|
|
2458
|
-
category: "
|
|
3195
|
+
category: "init",
|
|
2459
3196
|
message: "BROWSERBASE_PROJECT_ID is required for some Browserbase features that may not work without it.",
|
|
2460
3197
|
level: 1
|
|
2461
3198
|
});
|
|
@@ -2485,22 +3222,38 @@ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger,
|
|
|
2485
3222
|
sessionId = browserbaseResumeSessionID;
|
|
2486
3223
|
connectUrl = `wss://connect.browserbase.com?apiKey=${apiKey}&sessionId=${sessionId}`;
|
|
2487
3224
|
logger({
|
|
2488
|
-
category: "
|
|
2489
|
-
message: "
|
|
2490
|
-
level:
|
|
3225
|
+
category: "init",
|
|
3226
|
+
message: "resuming existing browserbase session...",
|
|
3227
|
+
level: 1,
|
|
3228
|
+
auxiliary: {
|
|
3229
|
+
sessionId: {
|
|
3230
|
+
value: sessionId,
|
|
3231
|
+
type: "string"
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
2491
3234
|
});
|
|
2492
3235
|
} catch (error) {
|
|
2493
3236
|
logger({
|
|
2494
|
-
category: "
|
|
2495
|
-
message:
|
|
2496
|
-
level:
|
|
3237
|
+
category: "init",
|
|
3238
|
+
message: "failed to resume session",
|
|
3239
|
+
level: 1,
|
|
3240
|
+
auxiliary: {
|
|
3241
|
+
error: {
|
|
3242
|
+
value: error.message,
|
|
3243
|
+
type: "string"
|
|
3244
|
+
},
|
|
3245
|
+
trace: {
|
|
3246
|
+
value: error.stack,
|
|
3247
|
+
type: "string"
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
2497
3250
|
});
|
|
2498
3251
|
throw error;
|
|
2499
3252
|
}
|
|
2500
3253
|
} else {
|
|
2501
3254
|
logger({
|
|
2502
|
-
category: "
|
|
2503
|
-
message: "
|
|
3255
|
+
category: "init",
|
|
3256
|
+
message: "creating new browserbase session...",
|
|
2504
3257
|
level: 0
|
|
2505
3258
|
});
|
|
2506
3259
|
if (!projectId) {
|
|
@@ -2513,27 +3266,54 @@ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger,
|
|
|
2513
3266
|
}, browserbaseSessionCreateParams));
|
|
2514
3267
|
sessionId = session.id;
|
|
2515
3268
|
connectUrl = session.connectUrl;
|
|
3269
|
+
logger({
|
|
3270
|
+
category: "init",
|
|
3271
|
+
message: "created new browserbase session",
|
|
3272
|
+
level: 1,
|
|
3273
|
+
auxiliary: {
|
|
3274
|
+
sessionId: {
|
|
3275
|
+
value: sessionId,
|
|
3276
|
+
type: "string"
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
});
|
|
2516
3280
|
}
|
|
2517
3281
|
const browser = yield import_test.chromium.connectOverCDP(connectUrl);
|
|
2518
3282
|
const { debuggerUrl } = yield browserbase.sessions.debug(sessionId);
|
|
2519
3283
|
debugUrl = debuggerUrl;
|
|
2520
3284
|
sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
|
|
2521
3285
|
logger({
|
|
2522
|
-
category: "
|
|
2523
|
-
message:
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
3286
|
+
category: "init",
|
|
3287
|
+
message: browserbaseResumeSessionID ? "browserbase session resumed" : "browserbase session started",
|
|
3288
|
+
level: 0,
|
|
3289
|
+
auxiliary: {
|
|
3290
|
+
sessionUrl: {
|
|
3291
|
+
value: sessionUrl,
|
|
3292
|
+
type: "string"
|
|
3293
|
+
},
|
|
3294
|
+
debugUrl: {
|
|
3295
|
+
value: debugUrl,
|
|
3296
|
+
type: "string"
|
|
3297
|
+
},
|
|
3298
|
+
sessionId: {
|
|
3299
|
+
value: sessionId,
|
|
3300
|
+
type: "string"
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
2529
3303
|
});
|
|
2530
3304
|
const context = browser.contexts()[0];
|
|
2531
3305
|
return { browser, context, debugUrl, sessionUrl };
|
|
2532
3306
|
} else {
|
|
2533
3307
|
logger({
|
|
2534
|
-
category: "
|
|
2535
|
-
message:
|
|
2536
|
-
level: 0
|
|
3308
|
+
category: "init",
|
|
3309
|
+
message: "launching local browser",
|
|
3310
|
+
level: 0,
|
|
3311
|
+
auxiliary: {
|
|
3312
|
+
headless: {
|
|
3313
|
+
value: headless.toString(),
|
|
3314
|
+
type: "boolean"
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
2537
3317
|
});
|
|
2538
3318
|
const tmpDir = import_fs2.default.mkdtempSync(`/tmp/pwtest`);
|
|
2539
3319
|
import_fs2.default.mkdirSync(`${tmpDir}/userdir/Default`, { recursive: true });
|
|
@@ -2571,8 +3351,8 @@ Live debug accessible here: ${debugUrl}.`,
|
|
|
2571
3351
|
}
|
|
2572
3352
|
);
|
|
2573
3353
|
logger({
|
|
2574
|
-
category: "
|
|
2575
|
-
message: "
|
|
3354
|
+
category: "init",
|
|
3355
|
+
message: "local browser started successfully."
|
|
2576
3356
|
});
|
|
2577
3357
|
yield applyStealthScripts(context);
|
|
2578
3358
|
return { context };
|
|
@@ -2626,7 +3406,7 @@ var Stagehand = class {
|
|
|
2626
3406
|
this.is_processing_browserbase_logs = false;
|
|
2627
3407
|
this.externalLogger = logger;
|
|
2628
3408
|
this.logger = this.log.bind(this);
|
|
2629
|
-
this.enableCaching = enableCaching != null ? enableCaching :
|
|
3409
|
+
this.enableCaching = enableCaching != null ? enableCaching : process.env.ENABLE_CACHING && process.env.ENABLE_CACHING === "true";
|
|
2630
3410
|
this.llmProvider = llmProvider || new LLMProvider(this.logger, this.enableCaching);
|
|
2631
3411
|
this.env = env;
|
|
2632
3412
|
this.observations = {};
|
|
@@ -2685,32 +3465,7 @@ var Stagehand = class {
|
|
|
2685
3465
|
yield this.page.setViewportSize({ width: 1280, height: 720 });
|
|
2686
3466
|
}
|
|
2687
3467
|
yield this.context.addInitScript({
|
|
2688
|
-
|
|
2689
|
-
content: import_fs2.default.readFileSync(
|
|
2690
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "xpathUtils.js"),
|
|
2691
|
-
"utf8"
|
|
2692
|
-
)
|
|
2693
|
-
});
|
|
2694
|
-
yield this.context.addInitScript({
|
|
2695
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
2696
|
-
content: import_fs2.default.readFileSync(
|
|
2697
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
2698
|
-
"utf8"
|
|
2699
|
-
)
|
|
2700
|
-
});
|
|
2701
|
-
yield this.context.addInitScript({
|
|
2702
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
2703
|
-
content: import_fs2.default.readFileSync(
|
|
2704
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
2705
|
-
"utf8"
|
|
2706
|
-
)
|
|
2707
|
-
});
|
|
2708
|
-
yield this.context.addInitScript({
|
|
2709
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
2710
|
-
content: import_fs2.default.readFileSync(
|
|
2711
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
2712
|
-
"utf8"
|
|
2713
|
-
)
|
|
3468
|
+
content: scriptContent
|
|
2714
3469
|
});
|
|
2715
3470
|
return { debugUrl, sessionUrl };
|
|
2716
3471
|
});
|
|
@@ -2731,52 +3486,21 @@ var Stagehand = class {
|
|
|
2731
3486
|
yield this.page.setViewportSize({ width: 1280, height: 720 });
|
|
2732
3487
|
}
|
|
2733
3488
|
yield this.context.addInitScript({
|
|
2734
|
-
|
|
2735
|
-
content: import_fs2.default.readFileSync(
|
|
2736
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "xpathUtils.js"),
|
|
2737
|
-
"utf8"
|
|
2738
|
-
)
|
|
2739
|
-
});
|
|
2740
|
-
yield this.context.addInitScript({
|
|
2741
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
2742
|
-
content: import_fs2.default.readFileSync(
|
|
2743
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
2744
|
-
"utf8"
|
|
2745
|
-
)
|
|
2746
|
-
});
|
|
2747
|
-
yield this.context.addInitScript({
|
|
2748
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
2749
|
-
content: import_fs2.default.readFileSync(
|
|
2750
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
2751
|
-
"utf8"
|
|
2752
|
-
)
|
|
2753
|
-
});
|
|
2754
|
-
yield this.context.addInitScript({
|
|
2755
|
-
path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
2756
|
-
content: import_fs2.default.readFileSync(
|
|
2757
|
-
import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
2758
|
-
"utf8"
|
|
2759
|
-
)
|
|
3489
|
+
content: scriptContent
|
|
2760
3490
|
});
|
|
2761
3491
|
return { context: this.context };
|
|
2762
3492
|
});
|
|
2763
3493
|
}
|
|
2764
|
-
log({
|
|
2765
|
-
message,
|
|
2766
|
-
category,
|
|
2767
|
-
level
|
|
2768
|
-
}) {
|
|
2769
|
-
const logObj = { category, message, level };
|
|
3494
|
+
log(logObj) {
|
|
2770
3495
|
logObj.level = logObj.level || 1;
|
|
2771
3496
|
if (this.externalLogger) {
|
|
2772
3497
|
this.externalLogger(logObj);
|
|
2773
3498
|
} else {
|
|
2774
|
-
const
|
|
2775
|
-
const logMessage = `[stagehand${categoryString}] ${logObj.message}`;
|
|
3499
|
+
const logMessage = logLineToString(logObj);
|
|
2776
3500
|
console.log(logMessage);
|
|
2777
3501
|
}
|
|
2778
3502
|
this.pending_logs_to_send_to_browserbase.push(__spreadProps(__spreadValues({}, logObj), {
|
|
2779
|
-
id:
|
|
3503
|
+
id: (0, import_crypto2.randomUUID)()
|
|
2780
3504
|
}));
|
|
2781
3505
|
this._run_browserbase_log_processing_cycle();
|
|
2782
3506
|
}
|
|
@@ -2801,7 +3525,7 @@ var Stagehand = class {
|
|
|
2801
3525
|
}
|
|
2802
3526
|
if (this.verbose >= logObj.level) {
|
|
2803
3527
|
yield this.page.evaluate((logObj2) => {
|
|
2804
|
-
const logMessage =
|
|
3528
|
+
const logMessage = logLineToString(logObj2);
|
|
2805
3529
|
if (logObj2.message.toLowerCase().includes("trace") || logObj2.message.toLowerCase().includes("error:")) {
|
|
2806
3530
|
console.error(logMessage);
|
|
2807
3531
|
} else {
|
|
@@ -2825,8 +3549,14 @@ var Stagehand = class {
|
|
|
2825
3549
|
timeoutHandle = setTimeout(() => {
|
|
2826
3550
|
this.log({
|
|
2827
3551
|
category: "dom",
|
|
2828
|
-
message:
|
|
2829
|
-
level: 1
|
|
3552
|
+
message: "DOM settle timeout exceeded, continuing anyway",
|
|
3553
|
+
level: 1,
|
|
3554
|
+
auxiliary: {
|
|
3555
|
+
timeout_ms: {
|
|
3556
|
+
value: timeout.toString(),
|
|
3557
|
+
type: "integer"
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
2830
3560
|
});
|
|
2831
3561
|
resolve();
|
|
2832
3562
|
}, timeout);
|
|
@@ -2855,9 +3585,18 @@ var Stagehand = class {
|
|
|
2855
3585
|
} catch (e) {
|
|
2856
3586
|
this.log({
|
|
2857
3587
|
category: "dom",
|
|
2858
|
-
message:
|
|
2859
|
-
|
|
2860
|
-
|
|
3588
|
+
message: "Error in waitForSettledDom",
|
|
3589
|
+
level: 1,
|
|
3590
|
+
auxiliary: {
|
|
3591
|
+
error: {
|
|
3592
|
+
value: e.message,
|
|
3593
|
+
type: "string"
|
|
3594
|
+
},
|
|
3595
|
+
trace: {
|
|
3596
|
+
value: e.stack,
|
|
3597
|
+
type: "string"
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
2861
3600
|
});
|
|
2862
3601
|
}
|
|
2863
3602
|
});
|
|
@@ -2880,9 +3619,18 @@ Trace: ${e.stack}`,
|
|
|
2880
3619
|
} catch (e) {
|
|
2881
3620
|
this.log({
|
|
2882
3621
|
category: "dom",
|
|
2883
|
-
message:
|
|
2884
|
-
|
|
2885
|
-
|
|
3622
|
+
message: "Error in startDomDebug",
|
|
3623
|
+
level: 1,
|
|
3624
|
+
auxiliary: {
|
|
3625
|
+
error: {
|
|
3626
|
+
value: e.message,
|
|
3627
|
+
type: "string"
|
|
3628
|
+
},
|
|
3629
|
+
trace: {
|
|
3630
|
+
value: e.stack,
|
|
3631
|
+
type: "string"
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
2886
3634
|
});
|
|
2887
3635
|
}
|
|
2888
3636
|
});
|
|
@@ -2916,8 +3664,14 @@ Trace: ${e.stack}`,
|
|
|
2916
3664
|
}) {
|
|
2917
3665
|
this.log({
|
|
2918
3666
|
category: "extraction",
|
|
2919
|
-
message:
|
|
2920
|
-
level: 1
|
|
3667
|
+
message: "starting extraction",
|
|
3668
|
+
level: 1,
|
|
3669
|
+
auxiliary: {
|
|
3670
|
+
instruction: {
|
|
3671
|
+
value: instruction,
|
|
3672
|
+
type: "string"
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
2921
3675
|
});
|
|
2922
3676
|
yield this._waitForSettledDom(domSettleTimeoutMs);
|
|
2923
3677
|
yield this.startDomDebug();
|
|
@@ -2927,8 +3681,22 @@ Trace: ${e.stack}`,
|
|
|
2927
3681
|
);
|
|
2928
3682
|
this.log({
|
|
2929
3683
|
category: "extraction",
|
|
2930
|
-
message:
|
|
2931
|
-
level: 1
|
|
3684
|
+
message: "received output from processDom.",
|
|
3685
|
+
level: 1,
|
|
3686
|
+
auxiliary: {
|
|
3687
|
+
chunk: {
|
|
3688
|
+
value: chunk.toString(),
|
|
3689
|
+
type: "integer"
|
|
3690
|
+
},
|
|
3691
|
+
chunks_left: {
|
|
3692
|
+
value: (chunks.length - chunksSeen.length).toString(),
|
|
3693
|
+
type: "integer"
|
|
3694
|
+
},
|
|
3695
|
+
chunks_total: {
|
|
3696
|
+
value: chunks.length.toString(),
|
|
3697
|
+
type: "integer"
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
2932
3700
|
});
|
|
2933
3701
|
const extractionResponse = yield extract({
|
|
2934
3702
|
instruction,
|
|
@@ -2950,22 +3718,40 @@ Trace: ${e.stack}`,
|
|
|
2950
3718
|
yield this.cleanupDomDebug();
|
|
2951
3719
|
this.log({
|
|
2952
3720
|
category: "extraction",
|
|
2953
|
-
message:
|
|
2954
|
-
level: 1
|
|
3721
|
+
message: "received extraction response",
|
|
3722
|
+
level: 1,
|
|
3723
|
+
auxiliary: {
|
|
3724
|
+
extraction_response: {
|
|
3725
|
+
value: JSON.stringify(extractionResponse),
|
|
3726
|
+
type: "object"
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
2955
3729
|
});
|
|
2956
3730
|
chunksSeen.push(chunk);
|
|
2957
3731
|
if (completed || chunksSeen.length === chunks.length) {
|
|
2958
3732
|
this.log({
|
|
2959
3733
|
category: "extraction",
|
|
2960
|
-
message:
|
|
2961
|
-
level: 1
|
|
3734
|
+
message: "got response",
|
|
3735
|
+
level: 1,
|
|
3736
|
+
auxiliary: {
|
|
3737
|
+
extraction_response: {
|
|
3738
|
+
value: JSON.stringify(extractionResponse),
|
|
3739
|
+
type: "object"
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
2962
3742
|
});
|
|
2963
3743
|
return output;
|
|
2964
3744
|
} else {
|
|
2965
3745
|
this.log({
|
|
2966
3746
|
category: "extraction",
|
|
2967
|
-
message:
|
|
2968
|
-
level: 1
|
|
3747
|
+
message: "continuing extraction",
|
|
3748
|
+
level: 1,
|
|
3749
|
+
auxiliary: {
|
|
3750
|
+
extraction_response: {
|
|
3751
|
+
value: JSON.stringify(extractionResponse),
|
|
3752
|
+
type: "object"
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
2969
3755
|
});
|
|
2970
3756
|
yield this._waitForSettledDom(domSettleTimeoutMs);
|
|
2971
3757
|
return this._extract({
|
|
@@ -2995,8 +3781,14 @@ Trace: ${e.stack}`,
|
|
|
2995
3781
|
const model = modelName != null ? modelName : this.defaultModelName;
|
|
2996
3782
|
this.log({
|
|
2997
3783
|
category: "observation",
|
|
2998
|
-
message:
|
|
2999
|
-
level: 1
|
|
3784
|
+
message: "starting observation",
|
|
3785
|
+
level: 1,
|
|
3786
|
+
auxiliary: {
|
|
3787
|
+
instruction: {
|
|
3788
|
+
value: instruction,
|
|
3789
|
+
type: "string"
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3000
3792
|
});
|
|
3001
3793
|
yield this._waitForSettledDom(domSettleTimeoutMs);
|
|
3002
3794
|
yield this.startDomDebug();
|
|
@@ -3009,14 +3801,21 @@ Trace: ${e.stack}`,
|
|
|
3009
3801
|
if (!modelsWithVision.includes(model)) {
|
|
3010
3802
|
this.log({
|
|
3011
3803
|
category: "observation",
|
|
3012
|
-
message:
|
|
3013
|
-
level: 1
|
|
3804
|
+
message: "Model does not support vision. Skipping vision processing.",
|
|
3805
|
+
level: 1,
|
|
3806
|
+
auxiliary: {
|
|
3807
|
+
model: {
|
|
3808
|
+
value: model,
|
|
3809
|
+
type: "string"
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3014
3812
|
});
|
|
3015
3813
|
} else {
|
|
3016
3814
|
const screenshotService = new ScreenshotService(
|
|
3017
3815
|
this.page,
|
|
3018
3816
|
selectorMap,
|
|
3019
|
-
this.verbose
|
|
3817
|
+
this.verbose,
|
|
3818
|
+
this.externalLogger
|
|
3020
3819
|
);
|
|
3021
3820
|
annotatedScreenshot = yield screenshotService.getAnnotatedScreenshot(fullPage);
|
|
3022
3821
|
outputString = "n/a. use the image to find the elements.";
|
|
@@ -3042,8 +3841,14 @@ Trace: ${e.stack}`,
|
|
|
3042
3841
|
this._recordObservation(instruction, elementsWithSelectors);
|
|
3043
3842
|
this.log({
|
|
3044
3843
|
category: "observation",
|
|
3045
|
-
message:
|
|
3046
|
-
level: 1
|
|
3844
|
+
message: "found elements",
|
|
3845
|
+
level: 1,
|
|
3846
|
+
auxiliary: {
|
|
3847
|
+
elements: {
|
|
3848
|
+
value: JSON.stringify(elementsWithSelectors),
|
|
3849
|
+
type: "object"
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3047
3852
|
});
|
|
3048
3853
|
yield this._recordObservation(instruction, elementsWithSelectors);
|
|
3049
3854
|
return elementsWithSelectors;
|
|
@@ -3059,9 +3864,20 @@ Trace: ${e.stack}`,
|
|
|
3059
3864
|
}) {
|
|
3060
3865
|
useVision = useVision != null ? useVision : "fallback";
|
|
3061
3866
|
const requestId = Math.random().toString(36).substring(2);
|
|
3062
|
-
this.
|
|
3867
|
+
this.log({
|
|
3063
3868
|
category: "act",
|
|
3064
|
-
message:
|
|
3869
|
+
message: "running act",
|
|
3870
|
+
level: 1,
|
|
3871
|
+
auxiliary: {
|
|
3872
|
+
action: {
|
|
3873
|
+
value: action,
|
|
3874
|
+
type: "string"
|
|
3875
|
+
},
|
|
3876
|
+
requestId: {
|
|
3877
|
+
value: requestId,
|
|
3878
|
+
type: "string"
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3065
3881
|
});
|
|
3066
3882
|
if (variables) {
|
|
3067
3883
|
this.variables = __spreadValues(__spreadValues({}, this.variables), variables);
|
|
@@ -3078,10 +3894,20 @@ Trace: ${e.stack}`,
|
|
|
3078
3894
|
skipActionCacheForThisStep: false,
|
|
3079
3895
|
domSettleTimeoutMs
|
|
3080
3896
|
}).catch((e) => {
|
|
3081
|
-
this.
|
|
3897
|
+
this.log({
|
|
3082
3898
|
category: "act",
|
|
3083
|
-
message:
|
|
3084
|
-
|
|
3899
|
+
message: "error acting",
|
|
3900
|
+
level: 1,
|
|
3901
|
+
auxiliary: {
|
|
3902
|
+
error: {
|
|
3903
|
+
value: e.message,
|
|
3904
|
+
type: "string"
|
|
3905
|
+
},
|
|
3906
|
+
trace: {
|
|
3907
|
+
value: e.stack,
|
|
3908
|
+
type: "string"
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3085
3911
|
});
|
|
3086
3912
|
return {
|
|
3087
3913
|
success: false,
|
|
@@ -3101,7 +3927,18 @@ Trace: ${e.stack}`
|
|
|
3101
3927
|
const requestId = Math.random().toString(36).substring(2);
|
|
3102
3928
|
this.logger({
|
|
3103
3929
|
category: "extract",
|
|
3104
|
-
message:
|
|
3930
|
+
message: "running extract",
|
|
3931
|
+
level: 1,
|
|
3932
|
+
auxiliary: {
|
|
3933
|
+
instruction: {
|
|
3934
|
+
value: instruction,
|
|
3935
|
+
type: "string"
|
|
3936
|
+
},
|
|
3937
|
+
requestId: {
|
|
3938
|
+
value: requestId,
|
|
3939
|
+
type: "string"
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3105
3942
|
});
|
|
3106
3943
|
return this._extract({
|
|
3107
3944
|
instruction,
|
|
@@ -3112,8 +3949,18 @@ Trace: ${e.stack}`
|
|
|
3112
3949
|
}).catch((e) => {
|
|
3113
3950
|
this.logger({
|
|
3114
3951
|
category: "extract",
|
|
3115
|
-
message:
|
|
3116
|
-
|
|
3952
|
+
message: "error extracting",
|
|
3953
|
+
level: 1,
|
|
3954
|
+
auxiliary: {
|
|
3955
|
+
error: {
|
|
3956
|
+
value: e.message,
|
|
3957
|
+
type: "string"
|
|
3958
|
+
},
|
|
3959
|
+
trace: {
|
|
3960
|
+
value: e.stack,
|
|
3961
|
+
type: "string"
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3117
3964
|
});
|
|
3118
3965
|
if (this.enableCaching) {
|
|
3119
3966
|
this.llmProvider.cleanRequestCache(requestId);
|
|
@@ -3128,7 +3975,18 @@ Trace: ${e.stack}`
|
|
|
3128
3975
|
const requestId = Math.random().toString(36).substring(2);
|
|
3129
3976
|
this.logger({
|
|
3130
3977
|
category: "observe",
|
|
3131
|
-
message:
|
|
3978
|
+
message: "running observe",
|
|
3979
|
+
level: 1,
|
|
3980
|
+
auxiliary: {
|
|
3981
|
+
instruction: {
|
|
3982
|
+
value: options == null ? void 0 : options.instruction,
|
|
3983
|
+
type: "string"
|
|
3984
|
+
},
|
|
3985
|
+
requestId: {
|
|
3986
|
+
value: requestId,
|
|
3987
|
+
type: "string"
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3132
3990
|
});
|
|
3133
3991
|
return this._observe({
|
|
3134
3992
|
instruction: (_a = options == null ? void 0 : options.instruction) != null ? _a : "Find actions that can be performed on this page.",
|
|
@@ -3140,8 +3998,26 @@ Trace: ${e.stack}`
|
|
|
3140
3998
|
}).catch((e) => {
|
|
3141
3999
|
this.logger({
|
|
3142
4000
|
category: "observe",
|
|
3143
|
-
message:
|
|
3144
|
-
|
|
4001
|
+
message: "error observing",
|
|
4002
|
+
level: 1,
|
|
4003
|
+
auxiliary: {
|
|
4004
|
+
error: {
|
|
4005
|
+
value: e.message,
|
|
4006
|
+
type: "string"
|
|
4007
|
+
},
|
|
4008
|
+
trace: {
|
|
4009
|
+
value: e.stack,
|
|
4010
|
+
type: "string"
|
|
4011
|
+
},
|
|
4012
|
+
requestId: {
|
|
4013
|
+
value: requestId,
|
|
4014
|
+
type: "string"
|
|
4015
|
+
},
|
|
4016
|
+
instruction: {
|
|
4017
|
+
value: options == null ? void 0 : options.instruction,
|
|
4018
|
+
type: "string"
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
3145
4021
|
});
|
|
3146
4022
|
if (this.enableCaching) {
|
|
3147
4023
|
this.llmProvider.cleanRequestCache(requestId);
|