@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.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
- You are given:
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
- 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.
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
- Also, verify if the goal has been accomplished already. Do this by checking if the goal has been accomplished based on the previous steps completed, the current page DOM elements and the current page URL / starting page URL. If it has, set completed to true and finish the task.
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
- Do exactly what the user's goal is. Do not exceed the scope of the goal.
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: `LLM Cache hit - returning cached response`,
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: `LLM Cache miss - no cached response found`,
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 _a = options, { image, response_model } = _a, openAiOptions = __objRest(_a, ["image", "response_model"]);
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 _a, _b, _c, _d, _e, _f, _g;
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: `LLM Cache hit - returning cached response`,
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: `LLM Cache miss - no cached response found`,
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 = (_a = options.tools) == null ? void 0 : _a.map((tool) => {
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 = ((_c = (_b = jsonSchema.definitions) == null ? void 0 : _b.MySchema) == null ? void 0 : _c.properties) || jsonSchema.properties;
757
- const schemaRequired = ((_e = (_d = jsonSchema.definitions) == null ? void 0 : _d.MySchema) == null ? void 0 : _e.required) || jsonSchema.required;
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: ((_f = response.content.find((c) => c.type === "text")) == null ? void 0 : _f.text) || null,
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: "Anthropic",
814
- message: "Transformed response: " + JSON.stringify(transformedResponse)
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: ((_g = options.retries) != null ? _g : 0) + 1
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: `Uncaught exception: ${err}`,
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: `Created cache directory at ${this.cacheDir}`,
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: `Error releasing lock: ${error}`,
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: "Failed to acquire lock for cleanup",
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: `Cleaned up ${entriesRemoved} stale cache entries`,
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: `Error during cache cleanup: ${error}`,
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: `Error reading cache file: ${error}. Resetting cache.`,
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: `Error writing cache file: ${error}`,
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: `Error getting cache: ${error}. Resetting cache.`,
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: `Error setting cache: ${error}. Resetting cache.`,
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: `Error removing cache entry: ${error}`,
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: `No cache entries found for requestId ${requestId}`,
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: `Error deleting cache for requestId ${requestId}: ${error}`,
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: `Error resetting cache: ${error}`,
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: `Cleaning up cache for requestId: ${requestId}`
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
- var ScreenshotService = class _ScreenshotService {
1321
- constructor(page, selectorMap, verbose, isDebugEnabled = false) {
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
- category,
1331
- message,
1332
- level = 1
1333
- }) {
1334
- if (this.verbose >= level) {
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: "Error",
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: "Info",
1366
- message: `Screenshot pixel count: ${pixelCount}`,
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 _ScreenshotService.saveAndOpenScreenshot(annotatedScreenshot);
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: "Vision",
1450
- message: `Warning: Failed to create annotation for element ${id}: ${error}, trace: ${error.stack}`,
1451
- level: 0
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
- static saveAndOpenScreenshot(screenshot) {
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
- console.log(`Screenshot saved to: ${filename}`);
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: `Adding action step to cache: ${action}, requestId: ${requestId}, url: ${url}, previousSelectors: ${previousSelectors}`,
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: `Cleared action for ID: ${requestId}`,
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: `Action marked as completed, Verifying if this is true...`,
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: `Error getting full page screenshot: ${e.message}
1681
- . Trying again...`,
1682
- level: 1
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: `Action completion verification result: ${actionCompleted}`,
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: `Scrolling element into view`,
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: `Error scrolling element into view: ${e.message}
1734
- Trace: ${e.stack}`,
1735
- level: 1
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: `Error scrolling element into view: ${e.message}
1742
- Trace: ${e.stack}`,
1743
- level: 1
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: `Error filling element: ${e.message}
1761
- Trace: ${e.stack}`,
1762
- level: 1
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: `Error pressing key: ${e.message}
1774
- Trace: ${e.stack}`,
1775
- level: 1
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: `Page URL before action: ${this.stagehand.page.url()}`,
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: `Error performing method ${method} with args ${JSON.stringify(
1791
- args
1792
- )}: ${e.message}
1793
- Trace: ${e.stack}`,
1794
- level: 1
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: `Clicking element, checking for page navigation`,
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: `Clicked element, ${newOpenedTab ? "opened a new tab" : "no new tabs opened"}`,
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: `New page detected (new tab) with URL: ${newOpenedTab.url()}`,
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: `Network idle timeout hit`,
2335
+ message: "network idle timeout hit",
1833
2336
  level: 1
1834
2337
  });
1835
2338
  });
1836
2339
  this.logger({
1837
2340
  category: "action",
1838
- message: `Finished waiting for (possible) page navigation`,
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: `New page detected with URL: ${this.stagehand.page.url()}`,
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: `Chosen method ${method} is invalid`,
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: `Element with XPath ${xpath} not found within ${timeout}ms.`,
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: `Checking if cached step is valid: ${cachedStep.xpath}, ${cachedStep.savedComponentString}`,
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: `Locator not found for xpath: ${cachedStep.xpath}`,
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: `locator element: ${yield this._getComponentString(locator)}`,
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: `Current text: ${currentComponent}`,
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: `Current text or cached text is undefined`,
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: `Current text and cached text do not match: ${normalizedCurrentText} !== ${normalizedCachedText}`,
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: `Error checking if cached step is valid: ${e.message}
1953
- Trace: ${e.stack}`,
1954
- level: 1
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: `Checking action cache for: ${JSON.stringify(cacheObj)}`,
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: `Action cache miss: ${JSON.stringify(cacheObj)}`,
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: `Action cache semi-hit: ${cachedStep.playwrightCommand.method} with args: ${JSON.stringify(
2013
- cachedStep.playwrightCommand.args
2014
- )}`,
2015
- level: 1
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: `Cached action step is valid: ${validXpath !== null}`,
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: `Cached action step is invalid, removing...`,
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: `Action Cache Hit: ${cachedStep.playwrightCommand.method} with args: ${JSON.stringify(
2039
- cachedStep.playwrightCommand.args
2040
- )}`,
2041
- level: 1
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: `Action completion verification result from cache: ${actionCompleted}`,
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: "Action completed successfully using cached step",
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: `Error performing cached action step: ${exception.message}
2102
- Trace: ${exception.stack}`,
2103
- level: 1
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: `${model} does not support vision, but useVision was set to ${useVision}. Defaulting to false.`,
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: `Running / Continuing action: ${action} on page: ${this.stagehand.page.url()}`,
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: `Processing DOM...`,
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: `Looking at chunk ${chunk}. Chunks left: ${chunks.length - chunksSeen.length}`,
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: `${model} does not support vision. Skipping vision processing.`,
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: `Received response from LLM: ${JSON.stringify(response)}`,
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: `No action found in current chunk. Chunks seen: ${chunksSeen.length}.`,
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: `Switching to vision-based processing`,
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 = ((_a = elementLines.find((line) => line.startsWith(`${elementId}:`))) == null ? void 0 : _a.split(":")[1]) || "Element not found";
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: `Executing method: ${method} on element: ${elementId} (xpaths: ${xpaths.join(
2290
- ", "
2291
- )}) with args: ${JSON.stringify(args)}`,
2292
- level: 1
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: `Error adding action step to cache: ${e.message}
2337
- Trace: ${e.stack}`,
2338
- level: 1
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: `Continuing to next action step`,
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: `Action completed successfully`,
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: `Error performing action - D (Retries: ${retries}): ${error.message}
2392
- Trace: ${error.stack}`,
2393
- level: 1
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: `Error performing action - A: ${error.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: `Error performing action - B: ${error.message}
2426
- Trace: ${error.stack}`,
2427
- level: 1
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: "Init",
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: "Init",
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: "Init",
2489
- message: "Resuming existing Browserbase session...",
2490
- level: 0
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: "Init",
2495
- message: `Failed to resume session ${browserbaseResumeSessionID}: ${error.message}`,
2496
- level: 0
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: "Init",
2503
- message: "Creating new Browserbase session...",
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: "Init",
2523
- message: `Browserbase session ${browserbaseResumeSessionID ? "resumed" : "started"}.
2524
-
2525
- Session Url: ${sessionUrl}
2526
-
2527
- Live debug accessible here: ${debugUrl}.`,
2528
- level: 0
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: "Init",
2535
- message: `Launching local browser in ${headless ? "headless" : "headed"} mode`,
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: "Init",
2575
- message: "Local browser started successfully."
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 : false;
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
- path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "xpathUtils.js"),
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
- path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "xpathUtils.js"),
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 categoryString = logObj.category ? `:${logObj.category}` : "";
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: Math.random().toString(36).substring(2, 15)
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 = `[stagehand${logObj2.category ? `:${logObj2.category}` : ""}] ${logObj2.message}`;
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: `DOM settle timeout of ${timeout}ms exceeded, continuing anyway`,
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: `Error in waitForSettledDom: ${e.message}
2859
- Trace: ${e.stack}`,
2860
- level: 1
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: `Error in startDomDebug: ${e.message}
2884
- Trace: ${e.stack}`,
2885
- level: 1
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: `starting extraction '${instruction}'`,
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: `received output from processDom. Current chunk index: ${chunk}, Number of chunks left: ${chunks.length - chunksSeen.length}`,
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: `received extraction response: ${JSON.stringify(extractionResponse)}`,
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: `response: ${JSON.stringify(extractionResponse)}`,
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: `continuing extraction, progress: '${newProgress}'`,
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: `starting observation: ${instruction}`,
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: `${model} does not support vision. Skipping vision processing.`,
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: `found element ${JSON.stringify(elementsWithSelectors)}`,
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.logger({
3867
+ this.log({
3063
3868
  category: "act",
3064
- message: `Running act with action: ${action}, requestId: ${requestId}`
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.logger({
3897
+ this.log({
3082
3898
  category: "act",
3083
- message: `Error acting: ${e.message}
3084
- Trace: ${e.stack}`
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: `Running extract with instruction: ${instruction}, requestId: ${requestId}`
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: `Internal error: Error extracting: ${e.message}
3116
- Trace: ${e.stack}`
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: `Running observe with instruction: ${options == null ? void 0 : options.instruction}, requestId: ${requestId}`
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: `Error observing: ${e.message}
3144
- Trace: ${e.stack}`
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);