@browserbasehq/stagehand 1.0.3-alpha.2 → 1.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +10 -5
  2. package/dist/dom/build/types.js +2 -0
  3. package/dist/evals/index.eval.js +1075 -0
  4. package/dist/evals/index.eval.js.map +1 -0
  5. package/dist/evals/playground.js +112 -0
  6. package/dist/evals/playground.js.map +1 -0
  7. package/dist/evals/utils.js +52 -0
  8. package/dist/evals/utils.js.map +1 -0
  9. package/dist/examples/2048.js +108 -0
  10. package/dist/examples/2048.js.map +1 -0
  11. package/dist/examples/debugUrl.js +35 -0
  12. package/dist/examples/debugUrl.js.map +1 -0
  13. package/dist/examples/example.js +37 -0
  14. package/dist/examples/example.js.map +1 -0
  15. package/dist/index.d.ts +25 -6
  16. package/dist/index.js +666 -151
  17. package/dist/lib/browserbase.js +56 -0
  18. package/dist/lib/browserbase.js.map +1 -0
  19. package/dist/lib/cache.js +78 -0
  20. package/dist/lib/cache.js.map +1 -0
  21. package/dist/lib/dom/debug.js +119 -0
  22. package/dist/lib/dom/debug.js.map +1 -0
  23. package/dist/lib/dom/index.js +20 -0
  24. package/dist/lib/dom/index.js.map +1 -0
  25. package/dist/lib/dom/process.js +396 -0
  26. package/dist/lib/dom/process.js.map +1 -0
  27. package/dist/lib/dom/utils.js +28 -0
  28. package/dist/lib/dom/utils.js.map +1 -0
  29. package/dist/lib/index.js +978 -0
  30. package/dist/lib/index.js.map +1 -0
  31. package/dist/lib/inference.js +226 -0
  32. package/dist/lib/inference.js.map +1 -0
  33. package/dist/lib/llm/AnthropicClient.js +150 -0
  34. package/dist/lib/llm/AnthropicClient.js.map +1 -0
  35. package/dist/lib/llm/LLMClient.js +12 -0
  36. package/dist/lib/llm/LLMClient.js.map +1 -0
  37. package/dist/lib/llm/LLMProvider.js +34 -0
  38. package/dist/lib/llm/LLMProvider.js.map +1 -0
  39. package/dist/lib/llm/OpenAIClient.js +69 -0
  40. package/dist/lib/llm/OpenAIClient.js.map +1 -0
  41. package/dist/lib/prompt.js +288 -0
  42. package/dist/lib/prompt.js.map +1 -0
  43. package/dist/lib/vision.js +194 -0
  44. package/dist/lib/vision.js.map +1 -0
  45. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ module.exports = __toCommonJS(lib_exports);
84
84
  var import_test = require("@playwright/test");
85
85
  var import_crypto = __toESM(require("crypto"));
86
86
  var import_fs2 = __toESM(require("fs"));
87
+ var import_sdk2 = require("@browserbasehq/sdk");
87
88
 
88
89
  // lib/prompt.ts
89
90
  var actSystemPrompt = `
@@ -283,8 +284,10 @@ var metadataSystemPrompt = `You are an AI assistant tasked with evaluating the p
283
284
  Analyze the extraction response and determine if the task is completed or if more information is needed.
284
285
 
285
286
  Strictly abide by the following criteria:
286
- 1. If you are certain that the instruction is completed, set the completion status to true, even if there are still chunks left.
287
- 2. If there could still be more information to extract and there are still chunks left, set the completion status to false.`;
287
+ 1. Once the instruction has been satisfied by the current extraction response, ALWAYS set completion status to true and stop processing, regardless of remaining chunks.
288
+ 2. Only set completion status to false if BOTH of these conditions are true:
289
+ - The instruction has not been satisfied yet
290
+ - There are still chunks left to process (chunksTotal > chunksSeen)`;
288
291
  function buildMetadataSystemPrompt() {
289
292
  return {
290
293
  role: "system",
@@ -296,8 +299,8 @@ function buildMetadataPrompt(instruction, extractionResponse, chunksSeen, chunks
296
299
  role: "user",
297
300
  content: `Instruction: ${instruction}
298
301
  Extracted content: ${JSON.stringify(extractionResponse, null, 2)}
299
- Chunks seen: ${chunksSeen}
300
- Chunks total: ${chunksTotal}`
302
+ chunksSeen: ${chunksSeen}
303
+ chunksTotal: ${chunksTotal}`
301
304
  };
302
305
  }
303
306
  var observeSystemPrompt = `
@@ -332,6 +335,7 @@ var modelsWithVision = [
332
335
  "gpt-4o-mini",
333
336
  "claude-3-5-sonnet-latest",
334
337
  "claude-3-5-sonnet-20240620",
338
+ "claude-3-5-sonnet-20241022",
335
339
  "gpt-4o-2024-08-06"
336
340
  ];
337
341
  var AnnotatedScreenshotText = "This is a screenshot of the current page state with the elements annotated on it. Each element id is annotated with a number to the top left of it. Duplicate annotations at the same location are under each other vertically.";
@@ -345,9 +349,10 @@ function verifyActCompletion(_0) {
345
349
  modelName,
346
350
  screenshot,
347
351
  domElements,
348
- logger
352
+ logger,
353
+ requestId
349
354
  }) {
350
- const llmClient = llmProvider.getClient(modelName);
355
+ const llmClient = llmProvider.getClient(modelName, requestId);
351
356
  const messages = [
352
357
  buildVerifyActCompletionSystemPrompt(),
353
358
  buildVerifyActCompletionUserPrompt(goal, steps, domElements)
@@ -396,9 +401,10 @@ function act(_0) {
396
401
  modelName,
397
402
  screenshot,
398
403
  retries = 0,
399
- logger
404
+ logger,
405
+ requestId
400
406
  }) {
401
- const llmClient = llmProvider.getClient(modelName);
407
+ const llmClient = llmProvider.getClient(modelName, requestId);
402
408
  const messages = [
403
409
  buildActSystemPrompt(),
404
410
  buildActUserPrompt(action, steps, domElements)
@@ -435,7 +441,8 @@ function act(_0) {
435
441
  llmProvider,
436
442
  modelName,
437
443
  retries: retries + 1,
438
- logger
444
+ logger,
445
+ requestId
439
446
  });
440
447
  }
441
448
  });
@@ -450,9 +457,10 @@ function extract(_0) {
450
457
  llmProvider,
451
458
  modelName,
452
459
  chunksSeen,
453
- chunksTotal
460
+ chunksTotal,
461
+ requestId
454
462
  }) {
455
- const llmClient = llmProvider.getClient(modelName);
463
+ const llmClient = llmProvider.getClient(modelName, requestId);
456
464
  const extractionResponse = yield llmClient.createChatCompletion({
457
465
  model: modelName,
458
466
  messages: [
@@ -525,7 +533,8 @@ function observe(_0) {
525
533
  domElements,
526
534
  llmProvider,
527
535
  modelName,
528
- image
536
+ image,
537
+ requestId
529
538
  }) {
530
539
  const observeSchema = import_zod.z.object({
531
540
  elements: import_zod.z.array(
@@ -537,7 +546,7 @@ function observe(_0) {
537
546
  })
538
547
  ).describe("an array of elements that match the instruction")
539
548
  });
540
- const llmClient = llmProvider.getClient(modelName);
549
+ const llmClient = llmProvider.getClient(modelName, requestId);
541
550
  const observationResponse = yield llmClient.createChatCompletion({
542
551
  model: modelName,
543
552
  messages: [
@@ -565,12 +574,31 @@ function observe(_0) {
565
574
  var import_openai = __toESM(require("openai"));
566
575
  var import_zod2 = require("openai/helpers/zod");
567
576
  var OpenAIClient = class {
568
- constructor(logger) {
577
+ constructor(logger, enableCaching = false, cache, requestId) {
569
578
  this.client = new import_openai.default();
570
579
  this.logger = logger;
580
+ this.requestId = requestId;
581
+ this.cache = cache;
582
+ this.enableCaching = enableCaching;
571
583
  }
572
584
  createChatCompletion(options) {
573
585
  return __async(this, null, function* () {
586
+ const cacheOptions = {
587
+ model: options.model,
588
+ messages: options.messages,
589
+ temperature: options.temperature,
590
+ top_p: options.top_p,
591
+ frequency_penalty: options.frequency_penalty,
592
+ presence_penalty: options.presence_penalty,
593
+ image: options.image,
594
+ response_model: options.response_model
595
+ };
596
+ if (this.enableCaching) {
597
+ const cachedResponse = yield this.cache.get(cacheOptions, this.requestId);
598
+ if (cachedResponse) {
599
+ return cachedResponse;
600
+ }
601
+ }
574
602
  if (options.image) {
575
603
  const screenshotMessage = {
576
604
  role: "user",
@@ -600,8 +628,18 @@ var OpenAIClient = class {
600
628
  if (response_model) {
601
629
  const extractedData = response.choices[0].message.content;
602
630
  const parsedData = JSON.parse(extractedData);
631
+ if (this.enableCaching) {
632
+ this.cache.set(
633
+ cacheOptions,
634
+ __spreadValues({}, parsedData),
635
+ this.requestId
636
+ );
637
+ }
603
638
  return __spreadValues({}, parsedData);
604
639
  }
640
+ if (this.enableCaching) {
641
+ this.cache.set(cacheOptions, response, this.requestId);
642
+ }
605
643
  return response;
606
644
  });
607
645
  }
@@ -611,16 +649,33 @@ var OpenAIClient = class {
611
649
  var import_sdk = __toESM(require("@anthropic-ai/sdk"));
612
650
  var import_zod_to_json_schema = require("zod-to-json-schema");
613
651
  var AnthropicClient = class {
614
- constructor(logger) {
652
+ constructor(logger, enableCaching = false, cache, requestId) {
615
653
  this.client = new import_sdk.default({
616
654
  apiKey: process.env.ANTHROPIC_API_KEY
617
- // Make sure to set this environment variable
618
655
  });
619
656
  this.logger = logger;
657
+ this.cache = cache;
658
+ this.enableCaching = enableCaching;
659
+ this.requestId = requestId;
620
660
  }
621
661
  createChatCompletion(options) {
622
662
  return __async(this, null, function* () {
623
663
  var _a, _b, _c, _d, _e, _f, _g;
664
+ const cacheOptions = {
665
+ model: options.model,
666
+ messages: options.messages,
667
+ temperature: options.temperature,
668
+ image: options.image,
669
+ response_model: options.response_model,
670
+ tools: options.tools,
671
+ retries: options.retries
672
+ };
673
+ if (this.enableCaching) {
674
+ const cachedResponse = yield this.cache.get(cacheOptions, this.requestId);
675
+ if (cachedResponse) {
676
+ return cachedResponse;
677
+ }
678
+ }
624
679
  const systemMessage = options.messages.find((msg) => msg.role === "system");
625
680
  const userMessages = options.messages.filter(
626
681
  (msg) => msg.role !== "system"
@@ -722,26 +777,318 @@ var AnthropicClient = class {
722
777
  if (options.response_model) {
723
778
  const toolUse = response.content.find((c) => c.type === "tool_use");
724
779
  if (toolUse && "input" in toolUse) {
725
- return toolUse.input;
780
+ const result = toolUse.input;
781
+ if (this.enableCaching) {
782
+ this.cache.set(cacheOptions, result, this.requestId);
783
+ }
784
+ return result;
726
785
  } else {
727
- if (!options.retries || options.retries < 2) {
786
+ if (!options.retries || options.retries < 5) {
728
787
  return this.createChatCompletion(__spreadProps(__spreadValues({}, options), {
729
788
  retries: ((_g = options.retries) != null ? _g : 0) + 1
730
789
  }));
731
790
  }
732
791
  throw new Error(
733
- "Extraction failed: No tool use with input in response"
792
+ "Create Chat Completion Failed: No tool use with input in response"
734
793
  );
735
794
  }
736
795
  }
796
+ if (this.enableCaching) {
797
+ this.cache.set(cacheOptions, transformedResponse, this.requestId);
798
+ }
737
799
  return transformedResponse;
738
800
  });
739
801
  }
740
802
  };
741
803
 
804
+ // lib/llm/LLMCache.ts
805
+ var fs = __toESM(require("fs"));
806
+ var path = __toESM(require("path"));
807
+ var crypto = __toESM(require("crypto"));
808
+ var LLMCache = class {
809
+ constructor(logger, cacheDir = path.join(process.cwd(), "tmp", ".cache"), cacheFile = "llm_calls.json") {
810
+ this.CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
811
+ // 1 week in milliseconds
812
+ this.CLEANUP_PROBABILITY = 0.01;
813
+ // 1% chance
814
+ this.LOCK_TIMEOUT_MS = 1e3;
815
+ this.lock_acquired = false;
816
+ this.count_lock_acquire_failures = 0;
817
+ this.request_id_to_used_hashes = {};
818
+ this.logger = logger;
819
+ this.cacheDir = cacheDir;
820
+ this.cacheFile = path.join(cacheDir, cacheFile);
821
+ this.lockFile = path.join(cacheDir, "llm_cache.lock");
822
+ this.ensureCacheDirectory();
823
+ this.setupProcessHandlers();
824
+ }
825
+ setupProcessHandlers() {
826
+ const releaseLockAndExit = () => {
827
+ this.releaseLock();
828
+ process.exit();
829
+ };
830
+ process.on("exit", releaseLockAndExit);
831
+ process.on("SIGINT", releaseLockAndExit);
832
+ process.on("SIGTERM", releaseLockAndExit);
833
+ process.on("uncaughtException", (err) => {
834
+ this.logger({
835
+ category: "llm_cache",
836
+ message: `Uncaught exception: ${err}`,
837
+ level: 2
838
+ });
839
+ if (this.lock_acquired) {
840
+ releaseLockAndExit();
841
+ }
842
+ });
843
+ }
844
+ ensureCacheDirectory() {
845
+ if (!fs.existsSync(this.cacheDir)) {
846
+ fs.mkdirSync(this.cacheDir, { recursive: true });
847
+ }
848
+ }
849
+ createHash(data) {
850
+ const hash = crypto.createHash("sha256");
851
+ return hash.update(JSON.stringify(data)).digest("hex");
852
+ }
853
+ sleep(ms) {
854
+ return new Promise((resolve) => setTimeout(resolve, ms));
855
+ }
856
+ acquireLock() {
857
+ return __async(this, null, function* () {
858
+ const startTime = Date.now();
859
+ while (Date.now() - startTime < this.LOCK_TIMEOUT_MS) {
860
+ try {
861
+ if (fs.existsSync(this.lockFile)) {
862
+ const lockAge = Date.now() - fs.statSync(this.lockFile).mtimeMs;
863
+ if (lockAge > this.LOCK_TIMEOUT_MS) {
864
+ fs.unlinkSync(this.lockFile);
865
+ }
866
+ }
867
+ fs.writeFileSync(this.lockFile, process.pid.toString(), { flag: "wx" });
868
+ this.count_lock_acquire_failures = 0;
869
+ this.lock_acquired = true;
870
+ return true;
871
+ } catch (error) {
872
+ yield this.sleep(5);
873
+ }
874
+ }
875
+ this.logger({
876
+ category: "llm_cache",
877
+ message: "Failed to acquire lock after timeout",
878
+ level: 2
879
+ });
880
+ this.count_lock_acquire_failures++;
881
+ if (this.count_lock_acquire_failures >= 3) {
882
+ this.logger({
883
+ category: "llm_cache",
884
+ message: "Failed to acquire lock 3 times in a row. Releasing lock manually.",
885
+ level: 1
886
+ });
887
+ this.releaseLock();
888
+ }
889
+ return false;
890
+ });
891
+ }
892
+ releaseLock() {
893
+ try {
894
+ if (fs.existsSync(this.lockFile)) {
895
+ fs.unlinkSync(this.lockFile);
896
+ }
897
+ this.lock_acquired = false;
898
+ } catch (error) {
899
+ this.logger({
900
+ category: "llm_cache",
901
+ message: `Error releasing lock: ${error}`,
902
+ level: 2
903
+ });
904
+ }
905
+ }
906
+ readCache() {
907
+ if (fs.existsSync(this.cacheFile)) {
908
+ return JSON.parse(fs.readFileSync(this.cacheFile, "utf-8"));
909
+ }
910
+ return {};
911
+ }
912
+ writeCache(cache) {
913
+ try {
914
+ if (Math.random() < this.CLEANUP_PROBABILITY) {
915
+ this.cleanupStaleEntries(cache);
916
+ }
917
+ fs.writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2));
918
+ } finally {
919
+ this.releaseLock();
920
+ }
921
+ }
922
+ cleanupStaleEntries(cache) {
923
+ if (!this.acquireLock()) {
924
+ this.logger({
925
+ category: "llm_cache",
926
+ message: "Failed to acquire lock for cleaning up cache",
927
+ level: 2
928
+ });
929
+ return;
930
+ }
931
+ try {
932
+ const now = Date.now();
933
+ let entriesRemoved = 0;
934
+ for (const [hash, entry] of Object.entries(cache)) {
935
+ if (now - entry.timestamp > this.CACHE_MAX_AGE_MS) {
936
+ delete cache[hash];
937
+ entriesRemoved++;
938
+ }
939
+ }
940
+ if (entriesRemoved > 0) {
941
+ this.logger({
942
+ category: "llm_cache",
943
+ message: `Cleaned up ${entriesRemoved} stale cache entries`,
944
+ level: 1
945
+ });
946
+ }
947
+ } catch (error) {
948
+ this.logger({
949
+ category: "llm_cache",
950
+ message: `Error cleaning up stale cache entries: ${error}`,
951
+ level: 1
952
+ });
953
+ } finally {
954
+ this.releaseLock();
955
+ }
956
+ }
957
+ resetCache() {
958
+ if (!this.acquireLock()) {
959
+ this.logger({
960
+ category: "llm_cache",
961
+ message: "Failed to acquire lock for resetting cache",
962
+ level: 2
963
+ });
964
+ return;
965
+ }
966
+ try {
967
+ this.ensureCacheDirectory();
968
+ fs.writeFileSync(this.cacheFile, "{}");
969
+ } finally {
970
+ this.releaseLock();
971
+ }
972
+ }
973
+ get(options, requestId) {
974
+ return __async(this, null, function* () {
975
+ var _a, _b;
976
+ if (!(yield this.acquireLock())) {
977
+ this.logger({
978
+ category: "llm_cache",
979
+ message: "Failed to acquire lock for getting cache",
980
+ level: 2
981
+ });
982
+ return null;
983
+ }
984
+ try {
985
+ const hash = this.createHash(options);
986
+ const cache = this.readCache();
987
+ if (cache[hash]) {
988
+ this.logger({
989
+ category: "llm_cache",
990
+ message: "Cache hit",
991
+ level: 1
992
+ });
993
+ (_b = (_a = this.request_id_to_used_hashes)[requestId]) != null ? _b : _a[requestId] = [];
994
+ this.request_id_to_used_hashes[requestId].push(hash);
995
+ return cache[hash].response;
996
+ }
997
+ return null;
998
+ } catch (error) {
999
+ this.logger({
1000
+ category: "llm_cache",
1001
+ message: `Error getting cache: ${error}. Resetting cache.`,
1002
+ level: 1
1003
+ });
1004
+ this.resetCache();
1005
+ return null;
1006
+ } finally {
1007
+ this.releaseLock();
1008
+ }
1009
+ });
1010
+ }
1011
+ deleteCacheForRequestId(requestId) {
1012
+ return __async(this, null, function* () {
1013
+ var _a;
1014
+ if (!(yield this.acquireLock())) {
1015
+ this.logger({
1016
+ category: "llm_cache",
1017
+ message: "Failed to acquire lock for deleting cache",
1018
+ level: 2
1019
+ });
1020
+ return;
1021
+ }
1022
+ try {
1023
+ const cache = this.readCache();
1024
+ let entriesRemoved = [];
1025
+ for (const hash of (_a = this.request_id_to_used_hashes[requestId]) != null ? _a : []) {
1026
+ if (cache[hash]) {
1027
+ entriesRemoved.push(cache[hash]);
1028
+ delete cache[hash];
1029
+ }
1030
+ }
1031
+ this.logger({
1032
+ category: "llm_cache",
1033
+ message: `Deleted ${entriesRemoved.length} cache entries for requestId ${requestId}`,
1034
+ level: 1
1035
+ });
1036
+ this.writeCache(cache);
1037
+ } catch (exception) {
1038
+ this.logger({
1039
+ category: "llm_cache",
1040
+ message: `Error deleting cache for requestId ${requestId}: ${exception}`,
1041
+ level: 1
1042
+ });
1043
+ } finally {
1044
+ this.releaseLock();
1045
+ }
1046
+ });
1047
+ }
1048
+ set(options, response, requestId) {
1049
+ return __async(this, null, function* () {
1050
+ var _a, _b;
1051
+ if (!(yield this.acquireLock())) {
1052
+ this.logger({
1053
+ category: "llm_cache",
1054
+ message: "Failed to acquire lock for setting cache",
1055
+ level: 2
1056
+ });
1057
+ return;
1058
+ }
1059
+ try {
1060
+ const hash = this.createHash(options);
1061
+ const cache = this.readCache();
1062
+ cache[hash] = {
1063
+ response,
1064
+ timestamp: Date.now(),
1065
+ requestId
1066
+ };
1067
+ this.writeCache(cache);
1068
+ (_b = (_a = this.request_id_to_used_hashes)[requestId]) != null ? _b : _a[requestId] = [];
1069
+ this.request_id_to_used_hashes[requestId].push(hash);
1070
+ this.logger({
1071
+ category: "llm_cache",
1072
+ message: "Cache miss - saved new response",
1073
+ level: 1
1074
+ });
1075
+ } catch (error) {
1076
+ this.logger({
1077
+ category: "llm_cache",
1078
+ message: `Error setting cache: ${error}. Resetting cache.`,
1079
+ level: 1
1080
+ });
1081
+ this.resetCache();
1082
+ } finally {
1083
+ this.releaseLock();
1084
+ }
1085
+ });
1086
+ }
1087
+ };
1088
+
742
1089
  // lib/llm/LLMProvider.ts
743
1090
  var LLMProvider = class {
744
- constructor(logger) {
1091
+ constructor(logger, enableCaching) {
745
1092
  this.modelToProviderMap = {
746
1093
  "gpt-4o": "openai",
747
1094
  "gpt-4o-mini": "openai",
@@ -751,71 +1098,44 @@ var LLMProvider = class {
751
1098
  "claude-3-5-sonnet-20241022": "anthropic"
752
1099
  };
753
1100
  this.logger = logger;
1101
+ this.enableCaching = enableCaching;
1102
+ this.cache = new LLMCache(logger);
1103
+ }
1104
+ cleanRequestCache(requestId) {
1105
+ this.logger({
1106
+ category: "llm_cache",
1107
+ message: `Cleaning up cache for requestId: ${requestId}`
1108
+ });
1109
+ this.cache.deleteCacheForRequestId(requestId);
754
1110
  }
755
- getClient(modelName) {
1111
+ getClient(modelName, requestId) {
756
1112
  const provider = this.modelToProviderMap[modelName];
757
1113
  if (!provider) {
758
1114
  throw new Error(`Unsupported model: ${modelName}`);
759
1115
  }
760
1116
  switch (provider) {
761
1117
  case "openai":
762
- return new OpenAIClient(this.logger);
1118
+ return new OpenAIClient(
1119
+ this.logger,
1120
+ this.enableCaching,
1121
+ this.cache,
1122
+ requestId
1123
+ );
763
1124
  case "anthropic":
764
- return new AnthropicClient(this.logger);
1125
+ return new AnthropicClient(
1126
+ this.logger,
1127
+ this.enableCaching,
1128
+ this.cache,
1129
+ requestId
1130
+ );
765
1131
  default:
766
1132
  throw new Error(`Unsupported provider: ${provider}`);
767
1133
  }
768
1134
  }
769
1135
  };
770
1136
 
771
- // lib/browserbase.ts
772
- var Browserbase = class {
773
- createSession() {
774
- return __async(this, null, function* () {
775
- if (!process.env.BROWSERBASE_API_KEY || !process.env.BROWSERBASE_PROJECT_ID) {
776
- throw new Error(
777
- "BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID must be set"
778
- );
779
- }
780
- const response = yield fetch(`https://www.browserbase.com/v1/sessions`, {
781
- method: "POST",
782
- headers: {
783
- "x-bb-api-key": `${process.env.BROWSERBASE_API_KEY}`,
784
- "Content-Type": "application/json"
785
- },
786
- body: JSON.stringify({
787
- projectId: process.env.BROWSERBASE_PROJECT_ID
788
- })
789
- });
790
- const json = yield response.json();
791
- if (json.error) {
792
- throw new Error(json.error);
793
- }
794
- return {
795
- sessionId: json.id,
796
- connectUrl: json.connectUrl
797
- };
798
- });
799
- }
800
- retrieveDebugConnectionURL(sessionId) {
801
- return __async(this, null, function* () {
802
- if (!process.env.BROWSERBASE_API_KEY) {
803
- throw new Error("BROWSERBASE_API_KEY must be set");
804
- }
805
- const response = yield fetch(
806
- `https://www.browserbase.com/v1/sessions/${sessionId}/debug`,
807
- {
808
- method: "GET",
809
- headers: {
810
- "x-bb-api-key": `${process.env.BROWSERBASE_API_KEY}`
811
- }
812
- }
813
- );
814
- const json = yield response.json();
815
- return json.debuggerFullscreenUrl;
816
- });
817
- }
818
- };
1137
+ // lib/index.ts
1138
+ var import_path2 = __toESM(require("path"));
819
1139
 
820
1140
  // lib/vision.ts
821
1141
  var import_fs = __toESM(require("fs"));
@@ -999,40 +1319,85 @@ var ScreenshotService = class _ScreenshotService {
999
1319
 
1000
1320
  // lib/index.ts
1001
1321
  require("dotenv").config({ path: ".env" });
1002
- function getBrowser(env = "LOCAL", headless = false, logger) {
1322
+ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger, browserbaseSessionCreateParams, browserbaseResumeSessionID) {
1003
1323
  return __async(this, null, function* () {
1004
- if (env === "BROWSERBASE" && !process.env.BROWSERBASE_API_KEY) {
1005
- logger({
1006
- category: "Init",
1007
- message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
1008
- level: 0
1009
- });
1010
- env = "LOCAL";
1011
- }
1012
- if (env === "BROWSERBASE" && !process.env.BROWSERBASE_PROJECT_ID) {
1013
- logger({
1014
- category: "Init",
1015
- message: "BROWSERBASE_PROJECT_ID is required to use BROWSERBASE env. Defaulting to LOCAL.",
1016
- level: 0
1017
- });
1018
- env = "LOCAL";
1324
+ if (env === "BROWSERBASE") {
1325
+ if (!apiKey) {
1326
+ logger({
1327
+ category: "Init",
1328
+ message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
1329
+ level: 0
1330
+ });
1331
+ env = "LOCAL";
1332
+ }
1333
+ if (!projectId) {
1334
+ logger({
1335
+ category: "Init",
1336
+ message: "BROWSERBASE_PROJECT_ID is required for some Browserbase features that may not work without it.",
1337
+ level: 1
1338
+ });
1339
+ }
1019
1340
  }
1020
1341
  if (env === "BROWSERBASE") {
1342
+ if (!apiKey) {
1343
+ throw new Error("BROWSERBASE_API_KEY is required.");
1344
+ }
1021
1345
  let debugUrl = void 0;
1022
1346
  let sessionUrl = void 0;
1023
- logger({
1024
- category: "Init",
1025
- message: "Connecting you to Browserbase...",
1026
- level: 0
1347
+ let sessionId;
1348
+ let connectUrl;
1349
+ const browserbase = new import_sdk2.Browserbase({
1350
+ apiKey
1027
1351
  });
1028
- const browserbase = new Browserbase();
1029
- const { sessionId, connectUrl } = yield browserbase.createSession();
1352
+ if (browserbaseResumeSessionID) {
1353
+ try {
1354
+ const sessionStatus = yield browserbase.sessions.retrieve(
1355
+ browserbaseResumeSessionID
1356
+ );
1357
+ if (sessionStatus.status !== "RUNNING") {
1358
+ throw new Error(
1359
+ `Session ${browserbaseResumeSessionID} is not running (status: ${sessionStatus.status})`
1360
+ );
1361
+ }
1362
+ sessionId = browserbaseResumeSessionID;
1363
+ connectUrl = `wss://connect.browserbase.com?apiKey=${apiKey}&sessionId=${sessionId}`;
1364
+ logger({
1365
+ category: "Init",
1366
+ message: "Resuming existing Browserbase session...",
1367
+ level: 0
1368
+ });
1369
+ } catch (error) {
1370
+ logger({
1371
+ category: "Init",
1372
+ message: `Failed to resume session ${browserbaseResumeSessionID}: ${error.message}`,
1373
+ level: 0
1374
+ });
1375
+ throw error;
1376
+ }
1377
+ } else {
1378
+ logger({
1379
+ category: "Init",
1380
+ message: "Creating new Browserbase session...",
1381
+ level: 0
1382
+ });
1383
+ if (!projectId) {
1384
+ throw new Error(
1385
+ "BROWSERBASE_PROJECT_ID is required for new Browserbase sessions."
1386
+ );
1387
+ }
1388
+ const session = yield browserbase.sessions.create(__spreadValues({
1389
+ projectId
1390
+ }, browserbaseSessionCreateParams));
1391
+ sessionId = session.id;
1392
+ connectUrl = session.connectUrl;
1393
+ }
1030
1394
  const browser = yield import_test.chromium.connectOverCDP(connectUrl);
1031
- debugUrl = yield browserbase.retrieveDebugConnectionURL(sessionId);
1395
+ const { debuggerUrl } = yield browserbase.sessions.debug(sessionId);
1396
+ debugUrl = debuggerUrl;
1032
1397
  sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
1033
1398
  logger({
1034
1399
  category: "Init",
1035
- message: `Browserbase session started.
1400
+ message: `Browserbase session ${browserbaseResumeSessionID ? "resumed" : "started"}.
1036
1401
 
1037
1402
  Session Url: ${sessionUrl}
1038
1403
 
@@ -1119,12 +1484,17 @@ function applyStealthScripts(context) {
1119
1484
  var Stagehand = class {
1120
1485
  constructor({
1121
1486
  env,
1122
- verbose = 0,
1123
- debugDom = false,
1487
+ apiKey,
1488
+ projectId,
1489
+ verbose,
1490
+ debugDom,
1124
1491
  llmProvider,
1125
- headless = false,
1492
+ headless,
1126
1493
  logger,
1127
- domSettleTimeoutMs = 6e4
1494
+ browserBaseSessionCreateParams,
1495
+ domSettleTimeoutMs,
1496
+ enableCaching,
1497
+ browserbaseResumeSessionID
1128
1498
  } = {
1129
1499
  env: "BROWSERBASE"
1130
1500
  }) {
@@ -1133,24 +1503,34 @@ var Stagehand = class {
1133
1503
  this.is_processing_browserbase_logs = false;
1134
1504
  this.externalLogger = logger;
1135
1505
  this.logger = this.log.bind(this);
1136
- this.llmProvider = llmProvider || new LLMProvider(this.logger);
1506
+ this.enableCaching = enableCaching != null ? enableCaching : false;
1507
+ this.llmProvider = llmProvider || new LLMProvider(this.logger, this.enableCaching);
1137
1508
  this.env = env;
1138
1509
  this.observations = {};
1510
+ this.apiKey = apiKey;
1511
+ this.projectId = projectId;
1139
1512
  this.actions = {};
1140
- this.verbose = verbose;
1141
- this.debugDom = debugDom;
1513
+ this.verbose = verbose != null ? verbose : 0;
1514
+ this.debugDom = debugDom != null ? debugDom : false;
1142
1515
  this.defaultModelName = "gpt-4o";
1143
- this.headless = headless;
1144
- this.domSettleTimeoutMs = domSettleTimeoutMs;
1516
+ this.domSettleTimeoutMs = domSettleTimeoutMs != null ? domSettleTimeoutMs : 3e4;
1517
+ this.headless = headless != null ? headless : false;
1518
+ this.browserBaseSessionCreateParams = browserBaseSessionCreateParams;
1519
+ this.browserbaseResumeSessionID = browserbaseResumeSessionID;
1145
1520
  }
1146
1521
  init() {
1147
1522
  return __async(this, arguments, function* ({
1148
1523
  modelName = "gpt-4o"
1149
1524
  } = {}) {
1525
+ console.log("haha fucker");
1150
1526
  const { context, debugUrl, sessionUrl } = yield getBrowser(
1527
+ this.apiKey,
1528
+ this.projectId,
1151
1529
  this.env,
1152
1530
  this.headless,
1153
- this.logger
1531
+ this.logger,
1532
+ this.browserBaseSessionCreateParams,
1533
+ this.browserbaseResumeSessionID
1154
1534
  ).catch((e) => {
1155
1535
  console.error("Error in init:", e);
1156
1536
  return { context: void 0, debugUrl: void 0, sessionUrl: void 0 };
@@ -1168,12 +1548,54 @@ var Stagehand = class {
1168
1548
  if (this.headless) {
1169
1549
  yield this.page.setViewportSize({ width: 1280, height: 720 });
1170
1550
  }
1171
- yield this.page.addInitScript({
1172
- content: '(()=>{var B=Object.defineProperty;var M=Object.getOwnPropertySymbols;var I=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var $=(e,t,o)=>t in e?B(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o,x=(e,t)=>{for(var o in t||(t={}))I.call(t,o)&&$(e,o,t[o]);if(M)for(var o of M(t))V.call(t,o)&&$(e,o,t[o]);return e};var d=(e,t,o)=>new Promise((n,s)=>{var c=a=>{try{l(o.next(a))}catch(u){s(u)}},i=a=>{try{l(o.throw(a))}catch(u){s(u)}},l=a=>a.done?n(a.value):Promise.resolve(a.value).then(c,i);l((o=o.apply(e,t)).next())});function _(e){return d(this,null,function*(){let{chunk:t,chunksArray:o}=yield Q(e),{outputString:n,selectorMap:s}=yield C(t);return console.log(`Stagehand (Browser Process): Extracted dom elements:${n}`),{outputString:n,selectorMap:s,chunk:t,chunks:o}})}function F(){return d(this,null,function*(){console.log("Stagehand (Browser Process): Processing all of DOM");let e=window.innerHeight,t=document.documentElement.scrollHeight,o=Math.ceil(t/e),n=0,s=[];for(let l=0;l<o;l++){let a=yield C(l,!0,n);s.push(a),n+=Object.keys(a.selectorMap).length}yield k(0);let c=s.map(l=>l.outputString).join(""),i=s.reduce((l,a)=>x(x({},l),a.selectorMap),{});return console.log(`Stagehand (Browser Process): All dom elements: ${c}`),{outputString:c,selectorMap:i}})}function k(e){return d(this,null,function*(){window.scrollTo({top:e,left:0,behavior:"smooth"}),yield new Promise(t=>{let o,n=()=>{clearTimeout(o),o=window.setTimeout(()=>{window.removeEventListener("scroll",n),t()},200)};window.addEventListener("scroll",n,{passive:!0}),n()})})}function C(e,t=!0,o=0){return d(this,null,function*(){console.time("processElements:total");let s=window.innerHeight*e,c=document.documentElement.scrollHeight-window.innerHeight,i=Math.min(s,c);t&&(console.time("processElements:scroll"),yield k(i),console.timeEnd("processElements:scroll"));let l=[],a=[...document.body.childNodes],u=new Map;for(console.log("Stagehand (Browser Process): Generating candidate elements"),console.time("processElements:findCandidates");a.length>0;){let r=a.pop(),h=!1;if(r&&b(r)){let p=r.childNodes.length;for(let w=p-1;w>=0;w--){let E=r.childNodes[w];a.push(E)}J(r)&&D(r)&&H(r)&&(h=!0),K(r)&&D(r)&&H(r)&&(h=!0)}r&&y(r)&&G(r)&&(h=!0),h&&l.push(r)}console.timeEnd("processElements:findCandidates");let m={},g="";return console.log(`Stagehand (Browser Process): Processing candidate elements: ${l.length}`),console.time("processElements:processCandidates"),l.forEach((r,h)=>{var w,E;let p=u.get(r);if(p||(p=j(r),u.set(r,p)),y(r)){let f=(w=r.textContent)==null?void 0:w.trim();f&&(g+=`${h+o}:${f}`)}else if(b(r)){let f=r.tagName.toLowerCase(),A=X(r),L=`<${f}${A?" "+A:""}>`,P=`</${f}>`,O=((E=r.textContent)==null?void 0:E.trim())||"";g+=`${h+o}:${L}${O}${P}`}m[h+o]=p}),console.timeEnd("processElements:processCandidates"),console.timeEnd("processElements:total"),{outputString:g,selectorMap:m}})}function X(e){let o=["id","class","href","src","aria-label","aria-name","aria-role","aria-description","aria-expanded","aria-haspopup"].map(n=>{let s=e.getAttribute(n);return s?`${n}="${s}"`:""}).filter(n=>n!=="");return Array.from(e.attributes).forEach(n=>{n.name.startsWith("data-")&&o.push(`${n.name}="${n.value}"`)}),o.join(" ")}window.processDom=_;window.processAllOfDom=F;window.processElements=C;window.scrollToHeight=k;function j(e){if(b(e)&&e.id)return`//*[@id="${e.id}"]`;let t=[];for(;e&&(y(e)||b(e));){let o=0,n=!1,s=e.parentElement?Array.from(e.parentElement.childNodes):[];for(let c=0;c<s.length;c++){let i=s[c];if(i.nodeType===e.nodeType&&i.nodeName===e.nodeName&&(o=o+1,n=!0,i.isSameNode(e)))break}if(e.nodeName!=="#text"){let c=e.nodeName.toLowerCase(),i=n?`[${o}]`:"";t.unshift(`${c}${i}`)}e=e.parentElement}return t.length?`/${t.join("/")}`:""}var U=["SVG","IFRAME","SCRIPT","STYLE","LINK"],Y=["A","BUTTON","DETAILS","EMBED","INPUT","LABEL","MENU","MENUITEM","OBJECT","SELECT","TEXTAREA","SUMMARY"],z=["button","menu","menuitem","link","checkbox","radio","slider","tab","tabpanel","textbox","combobox","grid","listbox","option","progressbar","scrollbar","searchbox","switch","tree","treeitem","spinbutton","tooltip"],q=["menu","menuitem","button"];function b(e){return e.nodeType===Node.ELEMENT_NODE}function y(e){var o;let t=(o=e.textContent)==null?void 0:o.trim().replace(/s/g,"");return e.nodeType===Node.TEXT_NODE&&t!==""}var H=e=>{let t=e.getBoundingClientRect();return t.width===0||t.height===0||t.top<0||t.top>window.innerHeight||!R(e,t)?!1:e.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0})},G=e=>{let t=document.createRange();t.selectNodeContents(e);let o=t.getBoundingClientRect();if(o.width===0||o.height===0||o.top<0||o.top>window.innerHeight)return!1;let n=e.parentElement;return!n||!R(n,o)?!1:n.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0})};function R(e,t){return[{x:t.left+t.width*.25,y:t.top+t.height*.25},{x:t.left+t.width*.75,y:t.top+t.height*.25},{x:t.left+t.width*.25,y:t.top+t.height*.75},{x:t.left+t.width*.75,y:t.top+t.height*.75},{x:t.left+t.width/2,y:t.top+t.height/2}].some(n=>{let c=document.elementFromPoint(n.x,n.y);for(;c&&c!==document.body;){if(c.isSameNode(e))return!0;c=c.parentElement}return!1})}var D=e=>!(e.hasAttribute("disabled")||e.hasAttribute("hidden")||e.getAttribute("aria-disabled")==="true"),J=e=>{let t=e.tagName,o=e.getAttribute("role"),n=e.getAttribute("aria-role");return t&&Y.includes(t)||o&&z.includes(o)||n&&q.includes(n)},K=e=>e.textContent===""?!1:e.childNodes.length===0?!U.includes(e.tagName):!!(e.childNodes.length===1&&y(e.childNodes[0]));function Q(e){return d(this,null,function*(){let t=window.innerHeight,o=document.documentElement.scrollHeight,n=Math.ceil(o/t),s=Array.from({length:n},(u,m)=>m),c=s.filter(u=>!e.includes(u)),i=window.scrollY,a=c.reduce((u,m)=>{let g=t*m,r=t*u;return Math.abs(i-g)<Math.abs(i-r)?m:u},c[0]);if(a===void 0)throw new Error(`No chunks remaining to check: ${c}`);return{chunk:a,chunksArray:s}})}function W(){return d(this,null,function*(){return new Promise(e=>{let t=()=>setTimeout(()=>{e()},2e3),o=t();new MutationObserver(()=>{clearTimeout(o),o=t()}).observe(window.document.body,{childList:!0,subtree:!0})})})}window.waitForDomSettle=W;function Z(){return d(this,null,function*(){window.chunkNumber=0;let{selectorMap:e,outputString:t}=yield window.processElements(window.chunkNumber);T(e),S()})}function T(e){N(),Object.entries(e).forEach(([t,o])=>{let n=document.evaluate(o,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;if(n){let s;if(n.nodeType===Node.ELEMENT_NODE)s=n.getBoundingClientRect();else{let l=document.createRange();l.selectNodeContents(n),s=l.getBoundingClientRect()}let c="grey",i=document.createElement("div");i.style.position="absolute",i.style.left=`${s.left+window.scrollX}px`,i.style.top=`${s.top+window.scrollY}px`,i.style.padding="2px",i.style.width=`${s.width}px`,i.style.height=`${s.height}px`,i.style.backgroundColor=c,i.className="stagehand-marker",i.style.opacity="0.3",i.style.zIndex="1000000000",i.style.border="1px solid",i.style.pointerEvents="none",document.body.appendChild(i)}})}function ee(){return d(this,null,function*(){N(),v()})}function N(){document.querySelectorAll(".stagehand-marker").forEach(t=>{t.remove()})}function v(){document.querySelectorAll(".stagehand-nav").forEach(t=>{t.remove()})}function S(){let e=window.innerHeight,t=document.documentElement.scrollHeight,o=Math.ceil(t/e);if(window.chunkNumber>0){let n=document.createElement("button");n.className="stagehand-nav",n.textContent="Previous",n.style.marginLeft="50px",n.style.position="fixed",n.style.bottom="10px",n.style.left="50%",n.style.transform="translateX(-50%)",n.style.zIndex="1000000000",n.onclick=()=>d(this,null,function*(){N(),v(),window.chunkNumber-=1,window.scrollTo(0,window.chunkNumber*window.innerHeight),yield window.waitForDomSettle();let{selectorMap:s}=yield processElements(window.chunkNumber);T(s),S()}),document.body.appendChild(n)}if(o>window.chunkNumber){let n=document.createElement("button");n.className="stagehand-nav",n.textContent="Next",n.style.marginRight="50px",n.style.position="fixed",n.style.bottom="10px",n.style.right="50%",n.style.transform="translateX(50%)",n.style.zIndex="1000000000",n.onclick=()=>d(this,null,function*(){N(),v(),window.chunkNumber+=1,window.scrollTo(0,window.chunkNumber*window.innerHeight),yield window.waitForDomSettle();let{selectorMap:s}=yield processElements(window.chunkNumber);T(s),S()}),document.body.appendChild(n)}}window.debugDom=Z;window.cleanupDebug=ee;})();'
1551
+ yield this.context.addInitScript({
1552
+ content: import_fs2.default.readFileSync(
1553
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
1554
+ "utf8"
1555
+ )
1556
+ });
1557
+ yield this.context.addInitScript({
1558
+ content: import_fs2.default.readFileSync(
1559
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
1560
+ "utf8"
1561
+ )
1562
+ });
1563
+ yield this.context.addInitScript({
1564
+ content: import_fs2.default.readFileSync(
1565
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
1566
+ "utf8"
1567
+ )
1173
1568
  });
1174
1569
  return { debugUrl, sessionUrl };
1175
1570
  });
1176
1571
  }
1572
+ initFromPage(page, modelName) {
1573
+ return __async(this, null, function* () {
1574
+ this.page = page;
1575
+ this.context = page.context();
1576
+ this.defaultModelName = modelName || this.defaultModelName;
1577
+ const originalGoto = this.page.goto.bind(this.page);
1578
+ this.page.goto = (url, options) => __async(this, null, function* () {
1579
+ const result = yield originalGoto(url, options);
1580
+ yield this.page.waitForLoadState("domcontentloaded");
1581
+ yield this._waitForSettledDom();
1582
+ return result;
1583
+ });
1584
+ if (this.headless) {
1585
+ yield this.page.setViewportSize({ width: 1280, height: 720 });
1586
+ }
1587
+ yield this.page.addInitScript({
1588
+ path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js")
1589
+ });
1590
+ yield this.page.addInitScript({
1591
+ path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js")
1592
+ });
1593
+ yield this.page.addInitScript({
1594
+ path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js")
1595
+ });
1596
+ return { context: this.context };
1597
+ });
1598
+ }
1177
1599
  log({
1178
1600
  message,
1179
1601
  category,
@@ -1233,11 +1655,9 @@ var Stagehand = class {
1233
1655
  return __async(this, null, function* () {
1234
1656
  try {
1235
1657
  const timeout = timeoutMs != null ? timeoutMs : this.domSettleTimeoutMs;
1236
- const timeoutPromise = new Promise((resolve) => {
1237
- setTimeout(() => {
1238
- console.warn(
1239
- `[stagehand:dom] DOM settle timeout of ${timeout}ms exceeded, continuing anyway`
1240
- );
1658
+ let timeoutHandle;
1659
+ const timeoutPromise = new Promise((resolve, reject) => {
1660
+ timeoutHandle = setTimeout(() => {
1241
1661
  this.log({
1242
1662
  category: "dom",
1243
1663
  message: `DOM settle timeout of ${timeout}ms exceeded, continuing anyway`,
@@ -1246,16 +1666,12 @@ var Stagehand = class {
1246
1666
  resolve();
1247
1667
  }, timeout);
1248
1668
  });
1249
- yield Promise.race([
1250
- (() => __async(this, null, function* () {
1251
- yield this.page.waitForSelector("body");
1252
- yield this.page.waitForLoadState("domcontentloaded");
1253
- yield this.page.evaluate(() => {
1669
+ try {
1670
+ yield Promise.race([
1671
+ this.page.evaluate(() => {
1254
1672
  return new Promise((resolve) => {
1255
1673
  if (typeof window.waitForDomSettle === "function") {
1256
- window.waitForDomSettle().then(() => {
1257
- resolve();
1258
- });
1674
+ window.waitForDomSettle().then(resolve);
1259
1675
  } else {
1260
1676
  console.warn(
1261
1677
  "waitForDomSettle is not defined, considering DOM as settled"
@@ -1263,10 +1679,14 @@ var Stagehand = class {
1263
1679
  resolve();
1264
1680
  }
1265
1681
  });
1266
- });
1267
- }))(),
1268
- timeoutPromise
1269
- ]);
1682
+ }),
1683
+ this.page.waitForLoadState("domcontentloaded"),
1684
+ this.page.waitForSelector("body"),
1685
+ timeoutPromise
1686
+ ]);
1687
+ } finally {
1688
+ clearTimeout(timeoutHandle);
1689
+ }
1270
1690
  } catch (e) {
1271
1691
  this.log({
1272
1692
  category: "dom",
@@ -1336,14 +1756,16 @@ Trace: ${e.stack}`,
1336
1756
  progress = "",
1337
1757
  content = {},
1338
1758
  chunksSeen = [],
1339
- modelName
1759
+ modelName,
1760
+ requestId,
1761
+ domSettleTimeoutMs
1340
1762
  }) {
1341
1763
  this.log({
1342
1764
  category: "extraction",
1343
1765
  message: `starting extraction '${instruction}'`,
1344
1766
  level: 1
1345
1767
  });
1346
- yield this._waitForSettledDom();
1768
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1347
1769
  yield this.startDomDebug();
1348
1770
  const { outputString, chunk, chunks } = yield this.page.evaluate(
1349
1771
  (chunksSeen2) => window.processDom(chunksSeen2 != null ? chunksSeen2 : []),
@@ -1363,7 +1785,8 @@ Trace: ${e.stack}`,
1363
1785
  schema,
1364
1786
  modelName: modelName || this.defaultModelName,
1365
1787
  chunksSeen: chunksSeen.length,
1366
- chunksTotal: chunks.length
1788
+ chunksTotal: chunks.length,
1789
+ requestId
1367
1790
  });
1368
1791
  const _a = extractionResponse, {
1369
1792
  metadata: { progress: newProgress, completed }
@@ -1390,14 +1813,15 @@ Trace: ${e.stack}`,
1390
1813
  message: `continuing extraction, progress: '${newProgress}'`,
1391
1814
  level: 1
1392
1815
  });
1393
- yield this._waitForSettledDom();
1816
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1394
1817
  return this._extract({
1395
1818
  instruction,
1396
1819
  schema,
1397
1820
  progress: newProgress,
1398
1821
  content: output,
1399
1822
  chunksSeen,
1400
- modelName
1823
+ modelName,
1824
+ domSettleTimeoutMs
1401
1825
  });
1402
1826
  }
1403
1827
  });
@@ -1407,7 +1831,9 @@ Trace: ${e.stack}`,
1407
1831
  instruction,
1408
1832
  useVision,
1409
1833
  fullPage,
1410
- modelName
1834
+ modelName,
1835
+ requestId,
1836
+ domSettleTimeoutMs
1411
1837
  }) {
1412
1838
  if (!instruction) {
1413
1839
  instruction = `Find elements that can be used for any future actions in the page. These may be navigation links, related pages, section/subsection links, buttons, or other interactive elements. Be comprehensive: if there are multiple elements that may be relevant for future actions, return all of them.`;
@@ -1418,7 +1844,7 @@ Trace: ${e.stack}`,
1418
1844
  message: `starting observation: ${instruction}`,
1419
1845
  level: 1
1420
1846
  });
1421
- yield this._waitForSettledDom();
1847
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1422
1848
  yield this.startDomDebug();
1423
1849
  let { outputString, selectorMap } = yield this.page.evaluate(
1424
1850
  (fullPage2) => fullPage2 ? window.processAllOfDom() : window.processDom([]),
@@ -1447,7 +1873,8 @@ Trace: ${e.stack}`,
1447
1873
  domElements: outputString,
1448
1874
  llmProvider: this.llmProvider,
1449
1875
  modelName: modelName || this.defaultModelName,
1450
- image: annotatedScreenshot
1876
+ image: annotatedScreenshot,
1877
+ requestId
1451
1878
  });
1452
1879
  const elementsWithSelectors = observationResponse.elements.map(
1453
1880
  (element) => {
@@ -1476,7 +1903,9 @@ Trace: ${e.stack}`,
1476
1903
  modelName,
1477
1904
  useVision,
1478
1905
  verifierUseVision,
1479
- retries = 0
1906
+ retries = 0,
1907
+ requestId,
1908
+ domSettleTimeoutMs
1480
1909
  }) {
1481
1910
  var _a;
1482
1911
  const model = modelName != null ? modelName : this.defaultModelName;
@@ -1494,7 +1923,7 @@ Trace: ${e.stack}`,
1494
1923
  message: `Running / Continuing action: ${action} on page: ${this.page.url()}`,
1495
1924
  level: 2
1496
1925
  });
1497
- yield this._waitForSettledDom();
1926
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1498
1927
  yield this.startDomDebug();
1499
1928
  this.log({
1500
1929
  category: "action",
@@ -1536,7 +1965,8 @@ Trace: ${e.stack}`,
1536
1965
  llmProvider: this.llmProvider,
1537
1966
  modelName: model,
1538
1967
  screenshot: annotatedScreenshot,
1539
- logger: this.logger
1968
+ logger: this.logger,
1969
+ requestId
1540
1970
  });
1541
1971
  this.log({
1542
1972
  category: "action",
@@ -1558,7 +1988,9 @@ Trace: ${e.stack}`,
1558
1988
  chunksSeen,
1559
1989
  modelName,
1560
1990
  useVision,
1561
- verifierUseVision
1991
+ verifierUseVision,
1992
+ requestId,
1993
+ domSettleTimeoutMs
1562
1994
  });
1563
1995
  } else if (useVision === "fallback") {
1564
1996
  this.log({
@@ -1573,9 +2005,14 @@ Trace: ${e.stack}`,
1573
2005
  chunksSeen,
1574
2006
  modelName,
1575
2007
  useVision: true,
1576
- verifierUseVision
2008
+ verifierUseVision,
2009
+ requestId,
2010
+ domSettleTimeoutMs
1577
2011
  });
1578
2012
  } else {
2013
+ if (this.enableCaching) {
2014
+ this.llmProvider.cleanRequestCache(requestId);
2015
+ }
1579
2016
  return {
1580
2017
  success: false,
1581
2018
  message: `Action was not able to be completed.`,
@@ -1632,7 +2069,9 @@ Trace: ${e.stack}`,
1632
2069
  useVision,
1633
2070
  verifierUseVision,
1634
2071
  retries: retries + 1,
1635
- chunksSeen
2072
+ chunksSeen,
2073
+ requestId,
2074
+ domSettleTimeoutMs
1636
2075
  });
1637
2076
  }
1638
2077
  }
@@ -1661,7 +2100,9 @@ Trace: ${e.stack}`,
1661
2100
  useVision,
1662
2101
  verifierUseVision,
1663
2102
  retries: retries + 1,
1664
- chunksSeen
2103
+ chunksSeen,
2104
+ requestId,
2105
+ domSettleTimeoutMs
1665
2106
  });
1666
2107
  }
1667
2108
  }
@@ -1684,7 +2125,9 @@ Trace: ${e.stack}`,
1684
2125
  useVision,
1685
2126
  verifierUseVision,
1686
2127
  retries: retries + 1,
1687
- chunksSeen
2128
+ chunksSeen,
2129
+ requestId,
2130
+ domSettleTimeoutMs
1688
2131
  });
1689
2132
  }
1690
2133
  }
@@ -1713,7 +2156,9 @@ Trace: ${e.stack}`,
1713
2156
  useVision,
1714
2157
  verifierUseVision,
1715
2158
  retries: retries + 1,
1716
- chunksSeen
2159
+ chunksSeen,
2160
+ requestId,
2161
+ domSettleTimeoutMs
1717
2162
  });
1718
2163
  }
1719
2164
  }
@@ -1743,7 +2188,7 @@ Trace: ${e.stack}`,
1743
2188
  yield newOpenedTab.close();
1744
2189
  yield this.page.goto(newOpenedTab.url());
1745
2190
  yield this.page.waitForLoadState("domcontentloaded");
1746
- yield this._waitForSettledDom();
2191
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1747
2192
  }
1748
2193
  yield Promise.race([
1749
2194
  this.page.waitForLoadState("networkidle"),
@@ -1782,9 +2227,14 @@ Trace: ${e.stack}`,
1782
2227
  useVision,
1783
2228
  verifierUseVision,
1784
2229
  retries: retries + 1,
1785
- chunksSeen
2230
+ chunksSeen,
2231
+ requestId,
2232
+ domSettleTimeoutMs
1786
2233
  });
1787
2234
  } else {
2235
+ if (this.enableCaching) {
2236
+ this.llmProvider.cleanRequestCache(requestId);
2237
+ }
1788
2238
  return {
1789
2239
  success: false,
1790
2240
  message: `Internal error: Chosen method ${method} is invalid`,
@@ -1851,7 +2301,8 @@ Trace: ${e.stack}`,
1851
2301
  modelName: model,
1852
2302
  screenshot: fullpageScreenshot,
1853
2303
  domElements,
1854
- logger: this.logger
2304
+ logger: this.logger,
2305
+ requestId
1855
2306
  });
1856
2307
  this.log({
1857
2308
  category: "action",
@@ -1871,7 +2322,9 @@ Trace: ${e.stack}`,
1871
2322
  modelName,
1872
2323
  chunksSeen,
1873
2324
  useVision,
1874
- verifierUseVision
2325
+ verifierUseVision,
2326
+ requestId,
2327
+ domSettleTimeoutMs
1875
2328
  });
1876
2329
  } else {
1877
2330
  this.log({
@@ -1901,10 +2354,15 @@ Trace: ${error.stack}`,
1901
2354
  useVision,
1902
2355
  verifierUseVision,
1903
2356
  retries: retries + 1,
1904
- chunksSeen
2357
+ chunksSeen,
2358
+ requestId,
2359
+ domSettleTimeoutMs
1905
2360
  });
1906
2361
  }
1907
2362
  yield this._recordAction(action, "");
2363
+ if (this.enableCaching) {
2364
+ this.llmProvider.cleanRequestCache(requestId);
2365
+ }
1908
2366
  return {
1909
2367
  success: false,
1910
2368
  message: `Error performing action: ${error.message}`,
@@ -1917,15 +2375,37 @@ Trace: ${error.stack}`,
1917
2375
  return __async(this, arguments, function* ({
1918
2376
  action,
1919
2377
  modelName,
1920
- useVision = "fallback"
2378
+ useVision = "fallback",
2379
+ domSettleTimeoutMs
1921
2380
  }) {
1922
2381
  useVision = useVision != null ? useVision : "fallback";
2382
+ const requestId = Math.random().toString(36).substring(2);
2383
+ this.logger({
2384
+ category: "act",
2385
+ message: `Running act with action: ${action}, requestId: ${requestId}`
2386
+ });
1923
2387
  return this._act({
1924
2388
  action,
1925
2389
  modelName,
1926
2390
  chunksSeen: [],
1927
2391
  useVision,
1928
- verifierUseVision: useVision !== false
2392
+ verifierUseVision: useVision !== false,
2393
+ requestId,
2394
+ domSettleTimeoutMs
2395
+ }).catch((e) => {
2396
+ this.logger({
2397
+ category: "act",
2398
+ message: `Error acting: ${e.message}
2399
+ Trace: ${e.stack}`
2400
+ });
2401
+ if (this.enableCaching) {
2402
+ this.llmProvider.cleanRequestCache(requestId);
2403
+ }
2404
+ return {
2405
+ success: false,
2406
+ message: `Internal error: Error acting: ${e.message}`,
2407
+ action
2408
+ };
1929
2409
  });
1930
2410
  });
1931
2411
  }
@@ -1933,23 +2413,58 @@ Trace: ${error.stack}`,
1933
2413
  return __async(this, arguments, function* ({
1934
2414
  instruction,
1935
2415
  schema,
1936
- modelName
2416
+ modelName,
2417
+ domSettleTimeoutMs
1937
2418
  }) {
2419
+ const requestId = Math.random().toString(36).substring(2);
2420
+ this.logger({
2421
+ category: "extract",
2422
+ message: `Running extract with instruction: ${instruction}, requestId: ${requestId}`
2423
+ });
1938
2424
  return this._extract({
1939
2425
  instruction,
1940
2426
  schema,
1941
- modelName
2427
+ modelName,
2428
+ requestId,
2429
+ domSettleTimeoutMs
2430
+ }).catch((e) => {
2431
+ this.logger({
2432
+ category: "extract",
2433
+ message: `Internal error: Error extracting: ${e.message}
2434
+ Trace: ${e.stack}`
2435
+ });
2436
+ if (this.enableCaching) {
2437
+ this.llmProvider.cleanRequestCache(requestId);
2438
+ }
2439
+ throw e;
1942
2440
  });
1943
2441
  });
1944
2442
  }
1945
2443
  observe(options) {
1946
2444
  return __async(this, null, function* () {
1947
2445
  var _a, _b;
2446
+ const requestId = Math.random().toString(36).substring(2);
2447
+ this.logger({
2448
+ category: "observe",
2449
+ message: `Running observe with instruction: ${options == null ? void 0 : options.instruction}, requestId: ${requestId}`
2450
+ });
1948
2451
  return this._observe({
1949
2452
  instruction: (_a = options == null ? void 0 : options.instruction) != null ? _a : "Find actions that can be performed on this page.",
1950
2453
  modelName: options == null ? void 0 : options.modelName,
1951
2454
  useVision: (_b = options == null ? void 0 : options.useVision) != null ? _b : false,
1952
- fullPage: false
2455
+ fullPage: false,
2456
+ requestId,
2457
+ domSettleTimeoutMs: options == null ? void 0 : options.domSettleTimeoutMs
2458
+ }).catch((e) => {
2459
+ this.logger({
2460
+ category: "observe",
2461
+ message: `Error observing: ${e.message}
2462
+ Trace: ${e.stack}`
2463
+ });
2464
+ if (this.enableCaching) {
2465
+ this.llmProvider.cleanRequestCache(requestId);
2466
+ }
2467
+ throw e;
1953
2468
  });
1954
2469
  });
1955
2470
  }