@shiplightai/sdk 0.1.1 → 0.1.2

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 (38) hide show
  1. package/README.md +18 -10
  2. package/dist/agentHelpers-MRG6DCNX.js +4 -0
  3. package/dist/agentHelpers-MRG6DCNX.js.map +1 -0
  4. package/dist/agentLogin-QZDVIJMB.js +4 -0
  5. package/dist/agentLogin-QZDVIJMB.js.map +1 -0
  6. package/dist/chunk-DIRPNR2B.js +195 -0
  7. package/dist/chunk-DIRPNR2B.js.map +1 -0
  8. package/dist/chunk-FWACDSD6.js +17 -0
  9. package/dist/chunk-FWACDSD6.js.map +1 -0
  10. package/dist/chunk-GVEDIII4.js +25 -0
  11. package/dist/chunk-GVEDIII4.js.map +1 -0
  12. package/dist/{chunk-UHZTPBZ3.js → chunk-N54UPO3H.js} +95 -92
  13. package/dist/chunk-N54UPO3H.js.map +1 -0
  14. package/dist/chunk-ODNKMWXO.js +6 -0
  15. package/dist/chunk-ODNKMWXO.js.map +1 -0
  16. package/dist/{chunk-GPZJYXUG.js → chunk-SSPF674P.js} +19 -6
  17. package/dist/chunk-SSPF674P.js.map +1 -0
  18. package/dist/chunk-USNFIQN5.js +4 -0
  19. package/dist/chunk-USNFIQN5.js.map +1 -0
  20. package/dist/chunk-W6S73J4I.js +4 -0
  21. package/dist/chunk-W6S73J4I.js.map +1 -0
  22. package/dist/handler-O7GYRDNA.js +4 -0
  23. package/dist/handler-O7GYRDNA.js.map +1 -0
  24. package/dist/index.js +12 -9
  25. package/dist/index.js.map +1 -0
  26. package/dist/task-E5YOHPFW.js +193 -0
  27. package/dist/task-E5YOHPFW.js.map +1 -0
  28. package/package.json +13 -13
  29. package/dist/agentHelpers-UCLT5EKK.js +0 -1
  30. package/dist/agentLogin-ARB3NEO4.js +0 -1
  31. package/dist/chunk-6H2NJBNL.js +0 -1
  32. package/dist/chunk-GDTCZALZ.js +0 -192
  33. package/dist/chunk-KFC5I6R5.js +0 -14
  34. package/dist/chunk-QIBDXB3J.js +0 -22
  35. package/dist/chunk-UFLZ3URR.js +0 -1
  36. package/dist/chunk-YR4E7JSB.js +0 -3
  37. package/dist/handler-TPOFKKIB.js +0 -1
  38. package/dist/task-57MAWXLN.js +0 -190
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../types/dist/chunk-KSKXJTXC.js","../../types/dist/chunk-27466TND.js","../../types/dist/chunk-5ER6LZOX.js","../../types/dist/chunk-PFEVHFBC.js","../../types/dist/chunk-IGHJSBFL.js","../../sdk-core/src/actions/impl/perform_accurate_operation.ts","../../sdk-core/src/llm_tools/tools/index.ts","../../sdk-core/src/llm_tools/index.ts","../../sdk-core/src/browser/browserManager.ts","../../sdk-core/src/browser/types.ts","../../sdk-core/src/browser/tabManager.ts","../../sdk-core/src/agent/agentWait.ts","../../sdk-core/src/agent/agentServices.ts","../../sdk-core/src/agent/webAgent.ts","../../sdk-core/src/agent/types.ts","../../sdk-core/src/core/agentContext.ts","../src/agent.ts","../src/index.ts"],"sourcesContent":["// src/devices/web.ts\nvar USER_AGENT = \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36\";\nvar ADDRESS_BAR_HEIGHT = 112;\nvar WINDOW_WIDTH = 1920;\nvar WINDOW_HEIGHT = 1080;\nvar VIEWPORT_WIDTH = 1920;\nvar VIEWPORT_HEIGHT = 1080 - ADDRESS_BAR_HEIGHT;\nvar RECORD_VIDEO_WIDTH = 1280;\nvar RECORD_VIDEO_HEIGHT = 720;\nvar MIN_WINDOW_WIDTH = 500;\nvar MIN_WINDOW_HEIGHT = 500;\nvar DEFAULT_DEVICE_NAME = \"Desktop Chrome\";\nvar BrowserType = /* @__PURE__ */ ((BrowserType2) => {\n BrowserType2[\"Chromium\"] = \"chromium\";\n BrowserType2[\"Firefox\"] = \"firefox\";\n BrowserType2[\"Webkit\"] = \"webkit\";\n return BrowserType2;\n})(BrowserType || {});\nvar PLAYWRIGHT_DEVICES = {\n \"Blackberry PlayBook\": {\n name: \"Blackberry PlayBook\",\n userAgent: \"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/26.0 Safari/536.2+\",\n screen: {\n width: 600,\n height: 1024\n },\n viewport: {\n width: 600,\n height: 1024\n },\n deviceScaleFactor: 1,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"BlackBerry Z30\": {\n name: \"BlackBerry Z30\",\n userAgent: \"Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/26.0 Mobile Safari/537.10+\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Galaxy Note 3\": {\n name: \"Galaxy Note 3\",\n userAgent: \"Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.0 Mobile Safari/534.30\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Galaxy Note II\": {\n name: \"Galaxy Note II\",\n userAgent: \"Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.0 Mobile Safari/534.30\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Galaxy S III\": {\n name: \"Galaxy S III\",\n userAgent: \"Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/26.0 Mobile Safari/534.30\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Galaxy S5\": {\n name: \"Galaxy S5\",\n userAgent: \"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy S8\": {\n name: \"Galaxy S8\",\n userAgent: \"Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 360,\n height: 740\n },\n viewport: {\n width: 360,\n height: 740\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy S9+\": {\n name: \"Galaxy S9+\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 320,\n height: 658\n },\n viewport: {\n width: 320,\n height: 658\n },\n deviceScaleFactor: 4.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy S24\": {\n name: \"Galaxy S24\",\n userAgent: \"Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 360,\n height: 780\n },\n viewport: {\n width: 360,\n height: 780\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy A55\": {\n name: \"Galaxy A55\",\n userAgent: \"Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 480,\n height: 1040\n },\n viewport: {\n width: 480,\n height: 1040\n },\n deviceScaleFactor: 2.25,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy Tab S4\": {\n name: \"Galaxy Tab S4\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 712,\n height: 1138\n },\n viewport: {\n width: 712,\n height: 1138\n },\n deviceScaleFactor: 2.25,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Galaxy Tab S9\": {\n name: \"Galaxy Tab S9\",\n userAgent: \"Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 640,\n height: 1024\n },\n viewport: {\n width: 640,\n height: 1024\n },\n deviceScaleFactor: 2.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"iPad (gen 5)\": {\n name: \"iPad (gen 5)\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 768,\n height: 1024\n },\n viewport: {\n width: 768,\n height: 1024\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPad (gen 6)\": {\n name: \"iPad (gen 6)\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 768,\n height: 1024\n },\n viewport: {\n width: 768,\n height: 1024\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPad (gen 7)\": {\n name: \"iPad (gen 7)\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 810,\n height: 1080\n },\n viewport: {\n width: 810,\n height: 1080\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPad (gen 11)\": {\n name: \"iPad (gen 11)\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/19E241 Safari/604.1\",\n screen: {\n width: 656,\n height: 944\n },\n viewport: {\n width: 656,\n height: 944\n },\n deviceScaleFactor: 2.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPad Mini\": {\n name: \"iPad Mini\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 768,\n height: 1024\n },\n viewport: {\n width: 768,\n height: 1024\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPad Pro 11\": {\n name: \"iPad Pro 11\",\n userAgent: \"Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 834,\n height: 1194\n },\n viewport: {\n width: 834,\n height: 1194\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 6\": {\n name: \"iPhone 6\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 375,\n height: 667\n },\n viewport: {\n width: 375,\n height: 667\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 6 Plus\": {\n name: \"iPhone 6 Plus\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 414,\n height: 736\n },\n viewport: {\n width: 414,\n height: 736\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 7\": {\n name: \"iPhone 7\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 375,\n height: 667\n },\n viewport: {\n width: 375,\n height: 667\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 7 Plus\": {\n name: \"iPhone 7 Plus\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 414,\n height: 736\n },\n viewport: {\n width: 414,\n height: 736\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 8\": {\n name: \"iPhone 8\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 375,\n height: 667\n },\n viewport: {\n width: 375,\n height: 667\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 8 Plus\": {\n name: \"iPhone 8 Plus\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 414,\n height: 736\n },\n viewport: {\n width: 414,\n height: 736\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone SE\": {\n name: \"iPhone SE\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.0 Mobile/14E304 Safari/602.1\",\n screen: {\n width: 320,\n height: 568\n },\n viewport: {\n width: 320,\n height: 568\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone SE (3rd gen)\": {\n name: \"iPhone SE (3rd gen)\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/26.0 Mobile/19E241 Safari/602.1\",\n screen: {\n width: 375,\n height: 667\n },\n viewport: {\n width: 375,\n height: 667\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone X\": {\n name: \"iPhone X\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/26.0 Mobile/15A372 Safari/604.1\",\n screen: {\n width: 375,\n height: 812\n },\n viewport: {\n width: 375,\n height: 812\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone XR\": {\n name: \"iPhone XR\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 414,\n height: 896\n },\n viewport: {\n width: 414,\n height: 896\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 11\": {\n name: \"iPhone 11\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 414,\n height: 896\n },\n viewport: {\n width: 414,\n height: 715\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 11 Pro\": {\n name: \"iPhone 11 Pro\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 375,\n height: 812\n },\n viewport: {\n width: 375,\n height: 635\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 11 Pro Max\": {\n name: \"iPhone 11 Pro Max\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 414,\n height: 896\n },\n viewport: {\n width: 414,\n height: 715\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 12\": {\n name: \"iPhone 12\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 390,\n height: 844\n },\n viewport: {\n width: 390,\n height: 664\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 12 Pro\": {\n name: \"iPhone 12 Pro\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 390,\n height: 844\n },\n viewport: {\n width: 390,\n height: 664\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 12 Pro Max\": {\n name: \"iPhone 12 Pro Max\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 428,\n height: 926\n },\n viewport: {\n width: 428,\n height: 746\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 12 Mini\": {\n name: \"iPhone 12 Mini\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 375,\n height: 812\n },\n viewport: {\n width: 375,\n height: 629\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 13\": {\n name: \"iPhone 13\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 390,\n height: 844\n },\n viewport: {\n width: 390,\n height: 664\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 13 Pro\": {\n name: \"iPhone 13 Pro\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 390,\n height: 844\n },\n viewport: {\n width: 390,\n height: 664\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 13 Pro Max\": {\n name: \"iPhone 13 Pro Max\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 428,\n height: 926\n },\n viewport: {\n width: 428,\n height: 746\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 13 Mini\": {\n name: \"iPhone 13 Mini\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 375,\n height: 812\n },\n viewport: {\n width: 375,\n height: 629\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 14\": {\n name: \"iPhone 14\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 390,\n height: 844\n },\n viewport: {\n width: 390,\n height: 664\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 14 Plus\": {\n name: \"iPhone 14 Plus\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 428,\n height: 926\n },\n viewport: {\n width: 428,\n height: 746\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 14 Pro\": {\n name: \"iPhone 14 Pro\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 393,\n height: 852\n },\n viewport: {\n width: 393,\n height: 660\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 14 Pro Max\": {\n name: \"iPhone 14 Pro Max\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 430,\n height: 932\n },\n viewport: {\n width: 430,\n height: 740\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 15\": {\n name: \"iPhone 15\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 393,\n height: 852\n },\n viewport: {\n width: 393,\n height: 659\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 15 Plus\": {\n name: \"iPhone 15 Plus\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 430,\n height: 932\n },\n viewport: {\n width: 430,\n height: 739\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 15 Pro\": {\n name: \"iPhone 15 Pro\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 393,\n height: 852\n },\n viewport: {\n width: 393,\n height: 659\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"iPhone 15 Pro Max\": {\n name: \"iPhone 15 Pro Max\",\n userAgent: \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1\",\n screen: {\n width: 430,\n height: 932\n },\n viewport: {\n width: 430,\n height: 739\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Kindle Fire HDX\": {\n name: \"Kindle Fire HDX\",\n userAgent: \"Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true\",\n screen: {\n width: 800,\n height: 1280\n },\n viewport: {\n width: 800,\n height: 1280\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"LG Optimus L70\": {\n name: \"LG Optimus L70\",\n userAgent: \"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 384,\n height: 640\n },\n viewport: {\n width: 384,\n height: 640\n },\n deviceScaleFactor: 1.25,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Microsoft Lumia 550\": {\n name: \"Microsoft Lumia 550\",\n userAgent: \"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36 Edge/14.14263\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Microsoft Lumia 950\": {\n name: \"Microsoft Lumia 950\",\n userAgent: \"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36 Edge/14.14263\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 4,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 10\": {\n name: \"Nexus 10\",\n userAgent: \"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 800,\n height: 1280\n },\n viewport: {\n width: 800,\n height: 1280\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 4\": {\n name: \"Nexus 4\",\n userAgent: \"Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 384,\n height: 640\n },\n viewport: {\n width: 384,\n height: 640\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 5\": {\n name: \"Nexus 5\",\n userAgent: \"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 5X\": {\n name: \"Nexus 5X\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 412,\n height: 732\n },\n viewport: {\n width: 412,\n height: 732\n },\n deviceScaleFactor: 2.625,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 6\": {\n name: \"Nexus 6\",\n userAgent: \"Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 412,\n height: 732\n },\n viewport: {\n width: 412,\n height: 732\n },\n deviceScaleFactor: 3.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 6P\": {\n name: \"Nexus 6P\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 412,\n height: 732\n },\n viewport: {\n width: 412,\n height: 732\n },\n deviceScaleFactor: 3.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nexus 7\": {\n name: \"Nexus 7\",\n userAgent: \"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 600,\n height: 960\n },\n viewport: {\n width: 600,\n height: 960\n },\n deviceScaleFactor: 2,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nokia Lumia 520\": {\n name: \"Nokia Lumia 520\",\n userAgent: \"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)\",\n screen: {\n width: 320,\n height: 533\n },\n viewport: {\n width: 320,\n height: 533\n },\n deviceScaleFactor: 1.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Nokia N9\": {\n name: \"Nokia N9\",\n userAgent: \"Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13\",\n screen: {\n width: 480,\n height: 854\n },\n viewport: {\n width: 480,\n height: 854\n },\n deviceScaleFactor: 1,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Pixel 2\": {\n name: \"Pixel 2\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 411,\n height: 731\n },\n viewport: {\n width: 411,\n height: 731\n },\n deviceScaleFactor: 2.625,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 2 XL\": {\n name: \"Pixel 2 XL\",\n userAgent: \"Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 411,\n height: 823\n },\n viewport: {\n width: 411,\n height: 823\n },\n deviceScaleFactor: 3.5,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 3\": {\n name: \"Pixel 3\",\n userAgent: \"Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 393,\n height: 786\n },\n viewport: {\n width: 393,\n height: 786\n },\n deviceScaleFactor: 2.75,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 4\": {\n name: \"Pixel 4\",\n userAgent: \"Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 353,\n height: 745\n },\n viewport: {\n width: 353,\n height: 745\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 4a (5G)\": {\n name: \"Pixel 4a (5G)\",\n userAgent: \"Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 412,\n height: 892\n },\n viewport: {\n width: 412,\n height: 765\n },\n deviceScaleFactor: 2.63,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 5\": {\n name: \"Pixel 5\",\n userAgent: \"Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 393,\n height: 851\n },\n viewport: {\n width: 393,\n height: 727\n },\n deviceScaleFactor: 2.75,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Pixel 7\": {\n name: \"Pixel 7\",\n userAgent: \"Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 412,\n height: 915\n },\n viewport: {\n width: 412,\n height: 839\n },\n deviceScaleFactor: 2.625,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Moto G4\": {\n name: \"Moto G4\",\n userAgent: \"Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Mobile Safari/537.36\",\n screen: {\n width: 360,\n height: 640\n },\n viewport: {\n width: 360,\n height: 640\n },\n deviceScaleFactor: 3,\n isMobile: true,\n hasTouch: true,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Chrome HiDPI\": {\n name: \"Desktop Chrome HiDPI\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 1792,\n height: 1120\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 2,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Edge HiDPI\": {\n name: \"Desktop Edge HiDPI\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36 Edg/141.0.7390.16\",\n screen: {\n width: 1792,\n height: 1120\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 2,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Firefox HiDPI\": {\n name: \"Desktop Firefox HiDPI\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0.1) Gecko/20100101 Firefox/142.0.1\",\n screen: {\n width: 1792,\n height: 1120\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 2,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"firefox\" /* Firefox */\n },\n \"Desktop Safari\": {\n name: \"Desktop Safari\",\n userAgent: \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15\",\n screen: {\n width: 1792,\n height: 1120\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 2,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"webkit\" /* Webkit */\n },\n \"Desktop Chrome\": {\n name: \"Desktop Chrome\",\n displayName: \"Playwright Chromium\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 1920,\n height: 1080\n },\n viewport: {\n width: 1920,\n height: 1080\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Chrome Medium Resolution\": {\n name: \"Desktop Chrome Medium Resolution\",\n displayName: \"Playwright Chromium\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 1280,\n height: 720\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Chrome (Branded)\": {\n name: \"Desktop Chrome (Branded)\",\n displayName: \"Google Chrome\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 1920,\n height: 1080\n },\n viewport: {\n width: 1920,\n height: 1080\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */,\n channel: \"chrome\"\n },\n \"Desktop Chrome Medium Resolution (Branded)\": {\n name: \"Desktop Chrome Medium Resolution (Branded)\",\n displayName: \"Google Chrome\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36\",\n screen: {\n width: 1280,\n height: 720\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */,\n channel: \"chrome\"\n },\n \"Desktop Edge\": {\n name: \"Desktop Edge\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36 Edg/141.0.7390.16\",\n screen: {\n width: 1920,\n height: 1080\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */\n },\n \"Desktop Edge (Branded)\": {\n name: \"Desktop Edge (Branded)\",\n displayName: \"Microsoft Edge\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36 Edg/141.0.7390.16\",\n screen: {\n width: 1920,\n height: 1080\n },\n viewport: {\n width: 1920,\n height: 1080\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */,\n channel: \"msedge\"\n },\n \"Desktop Edge Medium Resolution (Branded)\": {\n name: \"Desktop Edge Medium Resolution (Branded)\",\n displayName: \"Microsoft Edge\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.16 Safari/537.36 Edg/141.0.7390.16\",\n screen: {\n width: 1280,\n height: 720\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"chromium\" /* Chromium */,\n channel: \"msedge\"\n },\n \"Desktop Firefox\": {\n name: \"Desktop Firefox\",\n userAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0.1) Gecko/20100101 Firefox/142.0.1\",\n screen: {\n width: 1920,\n height: 1080\n },\n viewport: {\n width: 1280,\n height: 720\n },\n deviceScaleFactor: 1,\n isMobile: false,\n hasTouch: false,\n defaultBrowserType: \"firefox\" /* Firefox */\n }\n};\nvar DeviceType = /* @__PURE__ */ ((DeviceType2) => {\n DeviceType2[\"Desktop\"] = \"desktop\";\n DeviceType2[\"Mobile\"] = \"mobile\";\n return DeviceType2;\n})(DeviceType || {});\nvar DEVICE_CATEGORIES = {\n [\"desktop\" /* Desktop */]: [\n \"Desktop Chrome\",\n \"Desktop Chrome Medium Resolution\",\n \"Desktop Chrome (Branded)\",\n \"Desktop Chrome Medium Resolution (Branded)\",\n \"Desktop Edge (Branded)\",\n \"Desktop Edge Medium Resolution (Branded)\",\n \"Desktop Safari\"\n // \"Desktop Edge\",\n // \"Desktop Firefox\",\n // \"Desktop Chrome HiDPI\",\n // \"Desktop Edge HiDPI\",\n // \"Desktop Firefox HiDPI\"\n ],\n // [DeviceType.Tablet]: [\n // \"iPad Pro 11\",\n // \"iPad (gen 11)\",\n // \"iPad (gen 7)\",\n // \"iPad (gen 6)\",\n // \"iPad (gen 5)\",\n // \"iPad Mini\",\n // \"Galaxy Tab S9\",\n // \"Galaxy Tab S4\",\n // \"Nexus 10\",\n // \"Nexus 7\",\n // \"Kindle Fire HDX\",\n // \"Blackberry PlayBook\"\n // ],\n [\"mobile\" /* Mobile */]: [\n \"iPhone 15 Pro Max\",\n \"iPhone 15 Pro\",\n \"iPhone 15 Plus\",\n \"iPhone 15\",\n \"iPhone 14 Pro Max\",\n \"iPhone 14 Pro\",\n \"iPhone 14 Plus\",\n \"iPhone 14\",\n \"iPhone 13 Pro Max\",\n \"iPhone 13 Pro\",\n \"iPhone 13\",\n \"iPhone 13 Mini\",\n \"iPhone 12 Pro Max\",\n \"iPhone 12 Pro\",\n \"iPhone 12\",\n \"iPhone 12 Mini\",\n \"iPhone 11 Pro Max\",\n \"iPhone 11 Pro\",\n \"iPhone 11\",\n \"iPhone XR\",\n \"iPhone X\",\n \"iPhone SE (3rd gen)\",\n \"iPhone SE\",\n \"iPhone 8 Plus\",\n \"iPhone 8\",\n \"iPhone 7 Plus\",\n \"iPhone 7\",\n \"iPhone 6 Plus\",\n \"iPhone 6\",\n \"Galaxy S24\",\n \"Galaxy A55\",\n \"Galaxy S9+\",\n \"Galaxy S8\",\n \"Galaxy S5\",\n \"Galaxy Note 3\",\n \"Galaxy Note II\",\n \"Galaxy S III\",\n \"Pixel 7\",\n \"Pixel 5\",\n \"Pixel 4a (5G)\",\n \"Pixel 4\",\n \"Pixel 3\",\n \"Pixel 2 XL\",\n \"Pixel 2\",\n \"Nexus 6P\",\n \"Nexus 6\",\n \"Nexus 5X\",\n \"Nexus 5\",\n \"Nexus 4\",\n \"Moto G4\",\n \"LG Optimus L70\",\n \"Microsoft Lumia 950\",\n \"Microsoft Lumia 550\",\n \"Nokia Lumia 520\",\n \"Nokia N9\",\n \"BlackBerry Z30\"\n ]\n};\nvar getDevicesByCategory = (category, includeWebkit = false) => {\n const allowedBrowserTypes = [\"chromium\" /* Chromium */];\n if (includeWebkit) {\n allowedBrowserTypes.push(\"webkit\" /* Webkit */);\n }\n return DEVICE_CATEGORIES[category].map((deviceName) => PLAYWRIGHT_DEVICES[deviceName]).filter((device) => device.defaultBrowserType && allowedBrowserTypes.includes(device.defaultBrowserType));\n};\nvar getAllDeviceNames = () => {\n return Object.keys(PLAYWRIGHT_DEVICES);\n};\nvar getDeviceByName = (name) => {\n return PLAYWRIGHT_DEVICES[name];\n};\nvar UI_DEVICE_CATEGORIES = {\n desktop: {\n label: \"Desktop\",\n type: \"desktop\" /* Desktop */,\n devices: getDevicesByCategory(\"desktop\" /* Desktop */)\n },\n // tablet: {\n // label: \"Tablet\",\n // type: DeviceType.Tablet,\n // devices: getDevicesByCategory(DeviceType.Tablet)\n // },\n mobile: {\n label: \"Mobile Web\",\n type: \"mobile\" /* Mobile */,\n devices: getDevicesByCategory(\"mobile\" /* Mobile */)\n }\n};\nvar UI_DEVICE_CATEGORIES_ELECTRON = {\n desktop: {\n label: \"Desktop\",\n type: \"desktop\" /* Desktop */,\n devices: getDevicesByCategory(\"desktop\" /* Desktop */, true)\n },\n mobile: {\n label: \"Mobile Web\",\n type: \"mobile\" /* Mobile */,\n devices: getDevicesByCategory(\"mobile\" /* Mobile */, true)\n }\n};\nvar getDeviceOptions = (deviceName, withScale = false) => {\n const defaultDeviceOptions = {\n userAgent: USER_AGENT,\n viewport: {\n width: VIEWPORT_WIDTH,\n height: VIEWPORT_HEIGHT\n },\n isMobile: false,\n hasTouch: false\n };\n if (!deviceName || deviceName === DEFAULT_DEVICE_NAME) {\n return defaultDeviceOptions;\n }\n const originalDeviceOptions = getDeviceByName(deviceName);\n if (!originalDeviceOptions) {\n return defaultDeviceOptions;\n }\n const { width: originalViewportWidth, height: originalViewportHeight } = originalDeviceOptions.viewport;\n const widthScale = Math.max(MIN_WINDOW_WIDTH / originalViewportWidth, 1);\n const heightScale = Math.max(MIN_WINDOW_HEIGHT / originalViewportHeight, 1);\n const scale = Math.max(widthScale, heightScale);\n const viewport = {\n width: Math.round(originalViewportWidth * scale),\n height: Math.round(originalViewportHeight * scale)\n };\n const deviceOptions = {\n userAgent: originalDeviceOptions.userAgent,\n viewport: withScale ? viewport : originalDeviceOptions.viewport,\n isMobile: originalDeviceOptions.isMobile,\n hasTouch: originalDeviceOptions.hasTouch\n };\n return deviceOptions;\n};\nvar getBrowserWindowSize = (deviceName) => {\n const defaultWindowSize = {\n width: WINDOW_WIDTH,\n height: WINDOW_HEIGHT\n };\n if (!deviceName || deviceName === DEFAULT_DEVICE_NAME) {\n return defaultWindowSize;\n }\n const deviceOptions = getDeviceOptions(deviceName);\n if (!deviceOptions.viewport) {\n return defaultWindowSize;\n }\n return {\n width: deviceOptions.viewport.width,\n height: deviceOptions.viewport.height + ADDRESS_BAR_HEIGHT\n };\n};\nvar getRecordVideoSize = (deviceName) => {\n const defaultRecordVideoSize = {\n width: RECORD_VIDEO_WIDTH,\n height: RECORD_VIDEO_HEIGHT\n };\n if (!deviceName || deviceName === DEFAULT_DEVICE_NAME) {\n return defaultRecordVideoSize;\n }\n const deviceOptions = getDeviceOptions(deviceName);\n if (!deviceOptions.viewport) {\n return defaultRecordVideoSize;\n }\n return {\n width: deviceOptions.viewport.width,\n height: deviceOptions.viewport.height\n };\n};\nvar getDeviceChannel = (deviceName) => {\n if (!deviceName || deviceName === DEFAULT_DEVICE_NAME) {\n return void 0;\n }\n const device = getDeviceByName(deviceName);\n return device?.channel;\n};\nvar getDeviceBrowserType = (deviceName) => {\n if (!deviceName || deviceName === DEFAULT_DEVICE_NAME) {\n return \"chromium\" /* Chromium */;\n }\n const device = getDeviceByName(deviceName);\n return device?.defaultBrowserType ?? \"chromium\" /* Chromium */;\n};\n\nexport {\n USER_AGENT,\n ADDRESS_BAR_HEIGHT,\n WINDOW_WIDTH,\n WINDOW_HEIGHT,\n VIEWPORT_WIDTH,\n VIEWPORT_HEIGHT,\n RECORD_VIDEO_WIDTH,\n RECORD_VIDEO_HEIGHT,\n MIN_WINDOW_WIDTH,\n MIN_WINDOW_HEIGHT,\n DEFAULT_DEVICE_NAME,\n BrowserType,\n PLAYWRIGHT_DEVICES,\n DeviceType,\n DEVICE_CATEGORIES,\n getDevicesByCategory,\n getAllDeviceNames,\n getDeviceByName,\n UI_DEVICE_CATEGORIES,\n UI_DEVICE_CATEGORIES_ELECTRON,\n getDeviceOptions,\n getBrowserWindowSize,\n getRecordVideoSize,\n getDeviceChannel,\n getDeviceBrowserType\n};\n","// src/login/index.ts\nvar LoginType = /* @__PURE__ */ ((LoginType2) => {\n LoginType2[\"PASSWORD\"] = \"password\";\n LoginType2[\"OAUTH2\"] = \"oauth2\";\n LoginType2[\"SSO\"] = \"sso\";\n LoginType2[\"API\"] = \"api\";\n return LoginType2;\n})(LoginType || {});\nvar TwoFactorAuthType = /* @__PURE__ */ ((TwoFactorAuthType2) => {\n TwoFactorAuthType2[\"SMS\"] = \"sms\";\n TwoFactorAuthType2[\"EMAIL\"] = \"email\";\n TwoFactorAuthType2[\"TOTP\"] = \"totp\";\n return TwoFactorAuthType2;\n})(TwoFactorAuthType || {});\n\nexport {\n LoginType,\n TwoFactorAuthType\n};\n","// src/test-flow/testFlowSchema.ts\nimport { z } from \"zod\";\nvar ConditionTypeSchema = z.enum([\"JS_CODE\", \"AI_MODE\"]);\nvar ConditionSchema = z.object({\n type: ConditionTypeSchema,\n expression: z.string()\n});\nvar StatementTypeSchema = z.enum([\"DRAFT\", \"STEP\", \"ACTION\", \"IF_ELSE\", \"WHILE_LOOP\"]);\nvar BaseStatementSchema = z.object({\n uid: z.string(),\n type: StatementTypeSchema\n});\nvar ActionEntitySchema = z.object({\n action_data: z.object({\n action_name: z.string(),\n kwargs: z.record(z.any()).optional(),\n args: z.array(z.any()).optional()\n }),\n action_description: z.string().optional(),\n url: z.string().optional(),\n xpath: z.string().nullable().optional(),\n locator: z.string().nullable().optional(),\n css_selector: z.string().nullable().optional(),\n unique_selector: z.string().nullable().optional(),\n element_index: z.number().nullable().optional(),\n frame_path: z.array(z.any()).optional(),\n artifacts: z.record(z.any()).optional(),\n feedback: z.string().optional(),\n original_browser_use_action: z.any().optional()\n}).passthrough();\nvar DraftSchema = BaseStatementSchema.extend({\n type: z.literal(\"DRAFT\"),\n description: z.string()\n});\nvar ActionSchema = BaseStatementSchema.extend({\n type: z.literal(\"ACTION\"),\n description: z.string(),\n action_entity: ActionEntitySchema.optional(),\n locator: z.string().optional(),\n use_pure_vision: z.boolean().optional()\n});\nvar StatementSchema = z.lazy(\n () => z.union([\n DraftSchema,\n ActionSchema,\n BaseStatementSchema.extend({\n type: z.literal(\"STEP\"),\n description: z.string().optional().default(\"\"),\n statements: z.array(StatementSchema),\n reference_id: z.number().optional()\n }),\n BaseStatementSchema.extend({\n type: z.literal(\"IF_ELSE\"),\n description: z.string().optional(),\n condition: ConditionSchema,\n then: z.array(StatementSchema),\n else: z.array(StatementSchema).optional()\n }),\n BaseStatementSchema.extend({\n type: z.literal(\"WHILE_LOOP\"),\n description: z.string().optional(),\n condition: ConditionSchema,\n body: z.array(StatementSchema),\n timeout_ms: z.number().optional()\n })\n ])\n);\nvar TestFlowSchema = z.object({\n version: z.string().optional(),\n goal: z.string(),\n url: z.string(),\n final_feedback: z.string().optional(),\n completed: z.boolean().optional(),\n success: z.boolean().optional(),\n statements: z.array(StatementSchema),\n teardown: z.array(StatementSchema).optional(),\n last_modified_at: z.string().optional()\n});\n\nexport {\n ConditionTypeSchema,\n ConditionSchema,\n StatementTypeSchema,\n BaseStatementSchema,\n ActionEntitySchema,\n DraftSchema,\n ActionSchema,\n StatementSchema,\n TestFlowSchema\n};\n","import {\n TestFlowSchema\n} from \"./chunk-5ER6LZOX.js\";\n\n// src/test-flow/yamlFlowParser.ts\nimport { stringify as yamlStringify, parse as yamlParse } from \"yaml\";\nimport { v4 as uuidv4 } from \"uuid\";\nfunction testFlowToYaml(testFlow) {\n const yamlObj = {\n goal: testFlow.goal,\n url: testFlow.url,\n statements: testFlow.statements.map(statementToYaml)\n };\n if (testFlow.final_feedback) {\n yamlObj.final_feedback = testFlow.final_feedback;\n }\n if (testFlow.teardown && testFlow.teardown.length > 0) {\n yamlObj.teardown = testFlow.teardown.map(statementToYaml);\n }\n return yamlStringify(yamlObj, {\n lineWidth: 120,\n defaultKeyType: \"PLAIN\",\n defaultStringType: \"PLAIN\"\n });\n}\nfunction statementToYaml(stmt) {\n switch (stmt.type) {\n case \"DRAFT\" /* DRAFT */:\n return draftToYaml(stmt);\n case \"ACTION\" /* ACTION */:\n return actionToYaml(stmt);\n case \"STEP\" /* STEP */:\n return stepToYaml(stmt);\n case \"IF_ELSE\" /* IF_ELSE */:\n return ifElseToYaml(stmt);\n case \"WHILE_LOOP\" /* WHILE_LOOP */:\n return whileLoopToYaml(stmt);\n }\n}\nfunction draftToYaml(draft) {\n return draft.description;\n}\nfunction actionToYaml(action) {\n const actionName = action.action_entity?.action_data?.action_name ?? action.action_entity?.action?.action_name;\n if (actionName === \"verify\") {\n const kwargs = action.action_entity?.action_data?.kwargs ?? action.action_entity?.action?.kwargs;\n const statement = kwargs?.statement;\n if (typeof statement === \"string\" && !action.action_entity?.locator && !action.action_entity?.xpath) {\n return `VERIFY: ${statement}`;\n }\n }\n if (!action.action_entity) {\n return action.description;\n }\n const result = {\n description: action.description\n };\n const cleanEntity = cleanActionEntity(action.action_entity);\n if (cleanEntity) {\n result.action_entity = cleanEntity;\n }\n if (action.locator) {\n result.locator = action.locator;\n }\n if (action.use_pure_vision) {\n result.use_pure_vision = true;\n }\n return result;\n}\nfunction cleanActionEntity(entity) {\n const result = {};\n let hasContent = false;\n const actionData = entity.action_data ?? entity.action;\n if (actionData) {\n result.action_data = {\n action_name: actionData.action_name\n };\n if (actionData.kwargs && Object.keys(actionData.kwargs).length > 0) {\n result.action_data.kwargs = actionData.kwargs;\n }\n if (actionData.args && actionData.args.length > 0) {\n result.action_data.args = actionData.args;\n }\n hasContent = true;\n }\n if (entity.locator) {\n result.locator = entity.locator;\n hasContent = true;\n }\n if (entity.xpath) {\n result.xpath = entity.xpath;\n hasContent = true;\n }\n return hasContent ? result : void 0;\n}\nfunction stepToYaml(step) {\n const result = {\n STEP: step.description,\n statements: step.statements.map(statementToYaml)\n };\n if (step.reference_id !== void 0) {\n result.reference_id = step.reference_id;\n }\n return result;\n}\nfunction ifElseToYaml(ifElse) {\n const result = {\n IF: formatCondition(ifElse.condition),\n THEN: ifElse.then.map(statementToYaml)\n };\n if (ifElse.else && ifElse.else.length > 0) {\n result.ELSE = ifElse.else.map(statementToYaml);\n }\n return result;\n}\nfunction whileLoopToYaml(whileLoop) {\n const result = {\n WHILE: formatCondition(whileLoop.condition),\n DO: whileLoop.body.map(statementToYaml)\n };\n if (whileLoop.timeout_ms !== void 0) {\n result.timeout_ms = whileLoop.timeout_ms;\n }\n return result;\n}\nfunction formatCondition(condition) {\n if (condition.type === \"JS_CODE\" /* JS_CODE */) {\n return `js:${condition.expression}`;\n }\n return condition.expression;\n}\nvar MAX_YAML_SIZE = 1024 * 1024;\nfunction yamlToTestFlow(yamlString) {\n if (yamlString.length > MAX_YAML_SIZE) {\n throw new Error(`YAML input too large (${yamlString.length} bytes, max ${MAX_YAML_SIZE})`);\n }\n const parsed = yamlParse(yamlString);\n if (!parsed || typeof parsed !== \"object\") {\n throw new Error(\"Invalid YAML: expected an object at root level\");\n }\n const testFlow = {\n version: \"1.2.0\",\n goal: parsed.goal,\n url: parsed.url,\n statements: parseStatements(parsed.statements ?? [])\n };\n if (parsed.final_feedback) {\n testFlow.final_feedback = parsed.final_feedback;\n }\n if (parsed.teardown && Array.isArray(parsed.teardown)) {\n testFlow.teardown = parseStatements(parsed.teardown);\n }\n const result = TestFlowSchema.safeParse(testFlow);\n if (!result.success) {\n throw new Error(`Invalid TestFlow after YAML conversion: ${JSON.stringify(result.error.errors)}`);\n }\n return result.data;\n}\nfunction parseStatements(items) {\n if (!Array.isArray(items)) {\n throw new Error(\"Expected an array of statements\");\n }\n return items.map(parseStatement);\n}\nfunction parseStatement(item) {\n if (typeof item === \"string\") {\n return parseStringStatement(item);\n }\n if (typeof item !== \"object\" || item === null) {\n throw new Error(`Invalid statement: expected string or object, got ${typeof item}`);\n }\n const obj = item;\n if (\"IF\" in obj) {\n return parseIfElse(obj);\n }\n if (\"WHILE\" in obj) {\n return parseWhileLoop(obj);\n }\n if (\"STEP\" in obj) {\n return parseStep(obj);\n }\n if (\"action_entity\" in obj) {\n return parseActionWithEntity(obj);\n }\n if (\"description\" in obj && typeof obj.description === \"string\") {\n return {\n uid: uuidv4(),\n type: \"DRAFT\" /* DRAFT */,\n description: obj.description\n };\n }\n throw new Error(`Cannot infer statement type from object: ${JSON.stringify(obj)}`);\n}\nfunction parseStringStatement(str) {\n const verifyMatch = str.match(/^VERIFY:\\s*(.+)$/i);\n if (verifyMatch) {\n return {\n uid: uuidv4(),\n type: \"ACTION\" /* ACTION */,\n description: `Verify: ${verifyMatch[1]}`,\n action_entity: {\n action_description: `Verify: ${verifyMatch[1]}`,\n action_data: {\n action_name: \"verify\",\n kwargs: { statement: verifyMatch[1] }\n }\n }\n };\n }\n return {\n uid: uuidv4(),\n type: \"DRAFT\" /* DRAFT */,\n description: str\n };\n}\nfunction parseCondition(conditionStr) {\n if (typeof conditionStr !== \"string\") {\n throw new Error(`Condition must be a string, got ${typeof conditionStr}`);\n }\n if (conditionStr.startsWith(\"js:\")) {\n return {\n type: \"JS_CODE\" /* JS_CODE */,\n expression: conditionStr.slice(3)\n };\n }\n return {\n type: \"AI_MODE\" /* AI_MODE */,\n expression: conditionStr\n };\n}\nfunction parseIfElse(obj) {\n const condition = parseCondition(obj.IF);\n const thenStatements = obj.THEN;\n if (!Array.isArray(thenStatements)) {\n throw new Error(\"IF_ELSE requires a THEN array\");\n }\n const result = {\n uid: uuidv4(),\n type: \"IF_ELSE\" /* IF_ELSE */,\n condition,\n then: parseStatements(thenStatements)\n };\n if (\"ELSE\" in obj && Array.isArray(obj.ELSE)) {\n result.else = parseStatements(obj.ELSE);\n }\n return result;\n}\nfunction parseWhileLoop(obj) {\n const condition = parseCondition(obj.WHILE);\n const body = obj.DO;\n if (!Array.isArray(body)) {\n throw new Error(\"WHILE_LOOP requires a DO array\");\n }\n const result = {\n uid: uuidv4(),\n type: \"WHILE_LOOP\" /* WHILE_LOOP */,\n condition,\n body: parseStatements(body)\n };\n if (typeof obj.timeout_ms === \"number\") {\n result.timeout_ms = obj.timeout_ms;\n }\n return result;\n}\nfunction parseStep(obj) {\n const description = typeof obj.STEP === \"string\" ? obj.STEP : \"\";\n if (!Array.isArray(obj.statements)) {\n throw new Error(\"STEP requires a statements array\");\n }\n const result = {\n uid: uuidv4(),\n type: \"STEP\" /* STEP */,\n description,\n statements: parseStatements(obj.statements)\n };\n if (typeof obj.reference_id === \"number\") {\n result.reference_id = obj.reference_id;\n }\n return result;\n}\nfunction parseActionWithEntity(obj) {\n const description = typeof obj.description === \"string\" ? obj.description : \"\";\n const rawEntity = obj.action_entity;\n let actionEntity;\n if (rawEntity) {\n actionEntity = {\n action_description: description,\n ...rawEntity\n };\n }\n const result = {\n uid: uuidv4(),\n type: \"ACTION\" /* ACTION */,\n description\n };\n if (actionEntity) {\n result.action_entity = actionEntity;\n }\n if (typeof obj.locator === \"string\") {\n result.locator = obj.locator;\n }\n if (typeof obj.use_pure_vision === \"boolean\") {\n result.use_pure_vision = obj.use_pure_vision;\n }\n return result;\n}\n\nexport {\n testFlowToYaml,\n yamlToTestFlow\n};\n","// src/VariableStore.ts\nvar VariableStore = class _VariableStore {\n data = {};\n sensitive = /* @__PURE__ */ new Set();\n /**\n * Get a variable value by key\n */\n get(key) {\n return this.data[key];\n }\n /**\n * Set a variable value\n * @param key - Variable name\n * @param value - Variable value\n * @param sensitive - Whether this variable contains sensitive data (e.g., passwords)\n */\n set(key, value, sensitive = false) {\n this.data[key] = value;\n if (sensitive) {\n this.sensitive.add(key);\n } else if (this.sensitive.has(key)) {\n this.sensitive.delete(key);\n }\n }\n /**\n * Get all variables as a plain object\n * Returns a shallow copy to prevent external modifications\n */\n getAll() {\n return { ...this.data };\n }\n /**\n * Check if a variable is marked as sensitive\n */\n isSensitive(key) {\n return this.sensitive.has(key);\n }\n /**\n * Get all sensitive keys\n * Returns a new Set to prevent external modifications\n */\n getAllSensitiveKeys() {\n return new Set(this.sensitive);\n }\n /**\n * Delete a variable\n */\n delete(key) {\n this.sensitive.delete(key);\n return delete this.data[key];\n }\n /**\n * Clear all variables\n */\n clear() {\n this.data = {};\n this.sensitive.clear();\n }\n /**\n * Check if a variable exists\n */\n has(key) {\n return key in this.data;\n }\n /**\n * Get the number of variables\n */\n get size() {\n return Object.keys(this.data).length;\n }\n /**\n * Merge another VariableStore into this one\n * Values from the other store override existing values\n */\n merge(other) {\n for (const [key, value] of Object.entries(other.getAll())) {\n this.set(key, value, other.isSensitive(key));\n }\n }\n /**\n * Serialize to JSON-compatible format for config files\n */\n toJSON() {\n return {\n data: { ...this.data },\n sensitiveKeys: Array.from(this.sensitive)\n };\n }\n /**\n * Create a VariableStore from JSON format\n */\n static fromJSON(json) {\n const store = new _VariableStore();\n if (json.data) {\n const sensitiveSet = new Set(json.sensitiveKeys || []);\n for (const [key, value] of Object.entries(json.data)) {\n store.set(key, value, sensitiveSet.has(key));\n }\n }\n return store;\n }\n};\n\nexport {\n VariableStore\n};\n","import { ToolRegistry } from '../../llm_tools/registry';\nimport { IAction, ActionEntity } from '../types';\nimport { z } from 'zod';\nimport { generateAction } from '../../agent/action-generation/coordinatesBased';\nimport { TaskExecutionContext } from '../../agent/core/types';\nimport { executeAction } from '../../agent/agentHelpers';\n\nexport const PerformAccurateOperationToolSchema = z.object({\n instruction: z\n .string()\n .describe(\n 'The instruction of the operation to perform. Can only include one operation. Do not inlcude element indexes just describe the element, e.g. \"select the text \"Hello, world!\" in \"Hello, world!\"\"',\n ),\n});\n\nexport function registerPerformAccurateOperationTool(registry: ToolRegistry) {\n registry.register({\n name: 'perform_accurate_operation',\n description:\n 'Perform an operation that requires accurate interaction like dragging or interacting with a specific area of an element. Only use this action when neccecary.',\n schema: PerformAccurateOperationToolSchema,\n usesElementIndex: false,\n\n async execute(args, ctx) {\n const { instruction } = args;\n\n // Create TaskExecutionContext from ToolExecutionContext\n const taskContext: TaskExecutionContext = {\n page: ctx.page,\n agentServices: ctx.agentServices,\n domService: ctx.domService,\n };\n\n // Generate action using coordinates-based vision mode\n const generatedAction = await generateAction(instruction, taskContext, {});\n\n // If generation failed, return error\n if (generatedAction.status === 'error' || !generatedAction.actionEntity) {\n return {\n success: false,\n actionEntity: {\n action_description: instruction,\n action_data: {\n action_name: 'perform_accurate_operation',\n kwargs: { instruction },\n },\n },\n error: generatedAction.error || 'Failed to generate action',\n };\n }\n\n const { actionEntity } = generatedAction;\n\n // Execute the generated action using executeAction\n const executionResult = await executeAction(actionEntity, taskContext);\n\n return {\n success: executionResult.success,\n actionEntity,\n message: executionResult.success \n ? `Successfully executed action: ${actionEntity.action_data?.action_name}`\n : undefined,\n error: executionResult.error,\n };\n },\n });\n}\n","/**\n * LLM Tools - Tool Definitions\n *\n * Central export point for all tool registration functions.\n * Tool registration functions are now co-located with their action implementations in src/actions/impl/.\n */\n\nimport type { ActionHandler } from '../../actions/handler';\nimport { ToolRegistry } from '../registry';\n\n/**\n * Lazy-load ActionHandler to avoid circular dependencies.\n *\n * We use dynamic import to defer loading the handler module until runtime.\n * This prevents circular dependency issues that would occur if we used a\n * static import and immediately instantiated ActionHandler at module load time.\n *\n * Pattern: Cache the instance after first creation to reuse across tool registrations.\n */\nlet cachedActionHandler: ActionHandler | null = null;\nasync function getActionHandler(): Promise<ActionHandler> {\n if (cachedActionHandler) {\n return cachedActionHandler;\n }\n const ActionHandlerClass = (await import('../../actions/handler')).default;\n cachedActionHandler = new ActionHandlerClass();\n return cachedActionHandler;\n}\n\n// Import registration functions from action files\nimport { registerClickTool } from '../../actions/impl/click';\nimport { registerDoubleClickTool } from '../../actions/impl/double_click';\nimport { registerHoverTool } from '../../actions/impl/hover';\nimport { registerRightClickTool } from '../../actions/impl/right_click';\n\nimport { registerGoBackTool } from '../../actions/impl/go_back';\nimport { registerGoToUrlTool } from '../../actions/impl/go_to_url';\nimport { registerReloadPageTool } from '../../actions/impl/reload_page';\nimport { registerWaitTool } from '../../actions/impl/wait';\n\nimport { registerClearInputTool } from '../../actions/impl/clear_input';\nimport { registerInputTextTool } from '../../actions/impl/input_text';\nimport { registerPressTool } from '../../actions/impl/press';\n\nimport { registerScrollTool } from '../../actions/impl/scroll';\nimport { registerScrollOnElementTool } from '../../actions/impl/scroll_on_element';\nimport { registerScrollToTextTool } from '../../actions/impl/scroll_to_text';\n\nimport { registerCloseTabTool } from '../../actions/impl/close_tab';\nimport { registerSwitchTabTool } from '../../actions/impl/switch_tab';\n\nimport { registerUploadFileTool } from '../../actions/impl/upload_file';\nimport { registerWaitForDownloadCompleteTool } from '../../actions/impl/wait_for_download_complete';\n\nimport { registerGetDropdownOptionsTool } from '../../actions/impl/get_dropdown_options';\nimport { registerSelectDropdownOptionTool } from '../../actions/impl/select_dropdown_option';\nimport { registerSetDateForNativeDatePickerTool } from '../../actions/impl/set_date_for_native_date_picker';\n\nimport { registerSaveVariableTool } from '../../actions/impl/save_variable';\nimport { registerDoneTool } from '../../actions/impl/done';\n\nimport { registerAiAssertTool } from '../../actions/impl/ai_assert';\nimport { registerAiExtractTool } from '../../actions/impl/ai_extract';\nimport { registerAiWaitUntilTool } from '../../actions/impl/wait_until';\n\nimport { registerGenerate2faCodeTool } from '../../actions/impl/generate_2fa_code';\nimport { registerPerformAccurateOperationTool } from '../../actions/impl/perform_accurate_operation';\n\n// ============================================================================\n// Registration Functions (for backward compatibility)\n// ============================================================================\n\n/**\n * Register all mouse tools\n * @returns Capability summary for this category\n */\nexport async function registerMouseTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const clickAction = handler.getAction('click')!;\n const hoverAction = handler.getAction('hover')!;\n const rightClickAction = handler.getAction('right_click')!;\n const doubleClickAction = handler.getAction('double_click')!;\n\n registerClickTool(registry, clickAction);\n registerHoverTool(registry, hoverAction);\n registerRightClickTool(registry, rightClickAction);\n registerDoubleClickTool(registry, doubleClickAction);\n\n return \"Click, hover, double-click, right-click, or drag elements\";\n}\n\n/**\n * Register all navigation tools\n * @returns Capability summary for this category\n */\nexport async function registerNavigationTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const goToUrlAction = handler.getAction('go_to_url')!;\n const goBackAction = handler.getAction('go_back')!;\n const reloadPageAction = handler.getAction('reload_page')!;\n\n registerGoToUrlTool(registry, goToUrlAction);\n registerGoBackTool(registry, goBackAction);\n registerReloadPageTool(registry, reloadPageAction);\n\n return \"Navigate to URLs, go back, or reload the page\";\n}\n\n/**\n * Register all input tools\n * @returns Capability summary for this category\n */\nexport async function registerInputTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const clearInputAction = handler.getAction('clear_text')!;\n const inputTextAction = handler.getAction('input_text')!;\n const pressAction = handler.getAction('press')!;\n\n registerClearInputTool(registry, clearInputAction);\n registerInputTextTool(registry, inputTextAction);\n registerPressTool(registry, pressAction);\n\n return \"Type text into inputs, clear input values, or press keyboard keys\";\n}\n\n/**\n * Register all scroll tools\n * @returns Capability summary for this category\n */\nexport async function registerScrollTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const scrollOnElementAction = handler.getAction('scroll_on_element')!;\n const scrollToTextAction = handler.getAction('scroll_to_text')!;\n const scrollAction = handler.getAction('scroll')!;\n\n registerScrollOnElementTool(registry, scrollOnElementAction);\n registerScrollToTextTool(registry, scrollToTextAction);\n registerScrollTool(registry, scrollAction);\n\n return \"Scroll the page or scroll to specific text/elements\";\n}\n\n/**\n * Register all tab tools\n * @returns Capability summary for this category\n */\nexport async function registerTabTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const closeTabAction = handler.getAction('close_tab')!;\n const switchTabAction = handler.getAction('switch_tab')!;\n\n registerCloseTabTool(registry, closeTabAction);\n registerSwitchTabTool(registry, switchTabAction);\n\n return \"Switch between browser tabs or close tabs\";\n}\n\n/**\n * Register all file tools\n * @returns Capability summary for this category\n */\nexport async function registerFileTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const uploadFileAction = handler.getAction('upload_file')!;\n const waitForDownloadCompleteAction = handler.getAction('wait_for_download_complete')!;\n\n registerUploadFileTool(registry, uploadFileAction);\n registerWaitForDownloadCompleteTool(registry, waitForDownloadCompleteAction);\n // registerClickDownloadButtonTool is commented out in original\n\n return \"Upload files or wait for downloads to complete\";\n}\n\n/**\n * Register all form tools\n * @returns Capability summary for this category\n */\nexport async function registerFormTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const getDropdownOptionsAction = handler.getAction('get_dropdown_options')!;\n const selectDropdownAction = handler.getAction('select_dropdown_option')!;\n const setDateForNativeDatePickerAction = handler.getAction('set_date_for_native_date_picker')!;\n\n registerGetDropdownOptionsTool(registry, getDropdownOptionsAction);\n registerSelectDropdownOptionTool(registry, selectDropdownAction);\n registerSetDateForNativeDatePickerTool(registry, setDateForNativeDatePickerAction);\n\n return \"Get dropdown options, select dropdown values, or set date for native date picker input\";\n}\n\n/**\n * Register all utility tools\n * @returns Capability summary for this category\n */\nexport async function registerUtilityTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const waitAction = handler.getAction('wait')!;\n const saveVariableAction = handler.getAction('save_variable')!;\n const doneAction = handler.getAction('done')!;\n\n registerWaitTool(registry, waitAction);\n registerSaveVariableTool(registry, saveVariableAction);\n registerDoneTool(registry, doneAction);\n registerPerformAccurateOperationTool(registry);\n\n return \"Wait for conditions, save variables, or complete tasks\";\n}\n\n/**\n * Register all auth tools\n * @returns Capability summary for this category\n */\nexport async function registerAuthTools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const generate2faCodeAction = handler.getAction('generate_2fa_code')!;\n\n registerGenerate2faCodeTool(registry, generate2faCodeAction);\n\n return \"Generate 2FA codes\";\n}\n\n/**\n * Register all AI tools (MCP-only)\n * @returns Capability summary for this category\n */\nexport async function registerAITools(registry: ToolRegistry): Promise<string> {\n const handler = await getActionHandler();\n const verifyAction = handler.getAction('verify')!;\n const aiExtractAction = handler.getAction('ai_extract')!;\n const aiWaitUntilAction = handler.getAction('ai_wait_until')!;\n\n registerAiAssertTool(registry, verifyAction);\n registerAiExtractTool(registry, aiExtractAction);\n registerAiWaitUntilTool(registry, aiWaitUntilAction);\n\n return \"Perform AI-powered assertions, extractions, or wait conditions\";\n}\n\n// ============================================================================\n// Aggregated Registration and Capability Summary\n// ============================================================================\n\nexport interface ToolCapabilities {\n mouse: string;\n navigation: string;\n input: string;\n scroll: string;\n tabs: string;\n files: string;\n forms: string;\n utility: string;\n auth: string;\n ai: string;\n}\n\n/**\n * Register all browser automation tools and return their capabilities.\n * This is the main entry point for registering tools with capability tracking.\n *\n * @param registry The tool registry to register tools with\n * @returns Object containing capability summaries for each category\n */\nexport async function registerAllToolsWithCapabilities(registry: ToolRegistry): Promise<ToolCapabilities> {\n const [mouse, navigation, input, scroll, tabs, files, forms, utility, auth, ai] = await Promise.all([\n registerMouseTools(registry),\n registerNavigationTools(registry),\n registerInputTools(registry),\n registerScrollTools(registry),\n registerTabTools(registry),\n registerFileTools(registry),\n registerFormTools(registry),\n registerUtilityTools(registry),\n registerAuthTools(registry),\n registerAITools(registry),\n ]);\n\n return { mouse, navigation, input, scroll, tabs, files, forms, utility, auth, ai };\n}\n\n/**\n * Get a formatted capability summary string for use in LLM prompts.\n * This provides a concise description of what actions the browser automation can perform.\n *\n * @param capabilities The capabilities object from registerAllToolsWithCapabilities\n * @returns A formatted string describing all available capabilities\n */\nexport function formatCapabilitySummary(capabilities: ToolCapabilities): string {\n return [\n `- Mouse: ${capabilities.mouse}`,\n `- Navigation: ${capabilities.navigation}`,\n `- Input: ${capabilities.input}`,\n `- Scroll: ${capabilities.scroll}`,\n `- Tabs: ${capabilities.tabs}`,\n `- Files: ${capabilities.files}`,\n `- Forms: ${capabilities.forms}`,\n `- Utility: ${capabilities.utility}`,\n `- Auth: ${capabilities.auth}`,\n `- AI: ${capabilities.ai}`,\n ].join('\\n');\n}\n","/**\n * LLM Tools - Main Entry Point\n *\n * This module provides a clean interface for exposing SDK actions to LLMs\n * via function calling (OpenAI) and MCP (Model Context Protocol).\n *\n * Usage:\n * ```typescript\n * import { toolRegistry, OpenAIToolProvider } from 'sdk-core/llm_tools';\n *\n * // Get OpenAI-compatible tool definitions\n * const openaiProvider = new OpenAIToolProvider(toolRegistry);\n * const tools = openaiProvider.getToolDefinitions();\n *\n * // Use with OpenAI API\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4',\n * messages: [...],\n * tools: tools,\n * });\n *\n * // Execute a tool\n * const result = await toolRegistry.execute('click', { index: 5 }, context);\n * console.log(result.actionEntity); // Save to trajectory\n * ```\n */\n\n// Core exports\nexport { ToolRegistry, toolRegistry } from './registry';\nexport { optimizeSchema, validateStrictMode, zodToMCPSchema, zodToOpenAISchema } from './schema';\nexport * from './types';\n\n// Provider exports\nexport { exportMCPTools, MCPToolProvider } from './providers/mcp';\nexport { OpenAIToolProvider } from './providers/openai';\nexport { VercelAIToolProvider } from './providers/vercel';\n\n// Tool registration exports\nexport * from './tools';\n\n// Auto-register all tools with the singleton registry\nimport { toolRegistry, ToolRegistry as ToolRegistryClass } from './registry';\nimport {\n formatCapabilitySummary,\n registerAITools,\n registerAllToolsWithCapabilities,\n registerAuthTools,\n registerFileTools,\n registerFormTools,\n registerInputTools,\n registerMouseTools,\n registerNavigationTools,\n registerScrollTools,\n registerTabTools,\n registerUtilityTools,\n type ToolCapabilities,\n} from './tools';\n\n// Register all available tools on module import\n// Store promises to allow awaiting completion\nconst toolRegistrationPromises = [\n registerMouseTools(toolRegistry),\n registerNavigationTools(toolRegistry),\n registerInputTools(toolRegistry),\n registerScrollTools(toolRegistry),\n registerTabTools(toolRegistry),\n registerFileTools(toolRegistry),\n registerFormTools(toolRegistry),\n registerUtilityTools(toolRegistry),\n registerAITools(toolRegistry), // MCP-only tools\n registerAuthTools(toolRegistry),\n];\n\n// Promise that resolves when all tools are registered\nconst allToolsRegistered = Promise.all(toolRegistrationPromises);\n\n/**\n * Ensure all tools are registered before proceeding.\n * Call this before generating documentation or using the registry if you need\n * to guarantee tools are available.\n *\n * @returns Promise that resolves when all tools are registered\n */\nexport async function ensureToolsRegistered(): Promise<void> {\n await allToolsRegistered;\n}\n\n/**\n * Get a pre-configured ToolRegistry with all tools registered\n *\n * @returns Singleton tool registry instance\n */\nexport function getToolRegistry(): ToolRegistryClass {\n return toolRegistry;\n}\n\n/**\n * Create a fresh ToolRegistry instance\n *\n * Useful for testing or creating isolated tool environments\n *\n * @param autoRegister - Whether to automatically register all tools (default: true)\n * @returns New ToolRegistry instance\n */\nexport function createToolRegistry(autoRegister: boolean = true): ToolRegistryClass {\n const registry = new ToolRegistryClass();\n\n if (autoRegister) {\n registerMouseTools(registry);\n registerNavigationTools(registry);\n registerInputTools(registry);\n registerScrollTools(registry);\n registerTabTools(registry);\n registerFileTools(registry);\n registerFormTools(registry);\n registerAuthTools(registry);\n registerUtilityTools(registry);\n registerAITools(registry); // MCP-only tools\n }\n\n return registry;\n}\n\n/**\n * Create a fresh ToolRegistry and return both the registry and capability summary.\n *\n * This is useful when you need to know what actions are available without\n * looking at individual tool definitions.\n *\n * @returns Object containing the registry and formatted capability summary\n */\nexport async function createToolRegistryWithCapabilities(): Promise<{\n registry: ToolRegistryClass;\n capabilities: ToolCapabilities;\n summary: string;\n}> {\n const registry = new ToolRegistryClass();\n const capabilities = await registerAllToolsWithCapabilities(registry);\n const summary = formatCapabilitySummary(capabilities);\n return { registry, capabilities, summary };\n}\n\n/**\n * Get a formatted summary of all available browser automation capabilities.\n *\n * This returns a human-readable string describing what actions can be performed,\n * useful for including in LLM prompts to describe available capabilities.\n *\n * @returns Formatted capability summary string\n */\nexport function getCapabilitySummary(): string {\n // Return static summary (doesn't require registry registration)\n return [\n '- Mouse: Click, hover, double-click, right-click or drag on elements',\n '- Navigation: Navigate to URLs, go back, or reload the page',\n '- Input: Type text into inputs, clear input fields, or press keyboard keys',\n '- Scroll: Scroll the page or scroll to specific text/elements',\n '- Tabs: Switch between browser tabs or close tabs',\n '- Files: Upload files or wait for downloads to complete',\n '- Forms: Get dropdown options or select dropdown values',\n '- Utility: Wait for conditions, save variables, or complete tasks',\n '- Auth: Generate 2FA codes or extract email/activation codes',\n '- AI: Perform AI-powered verifications, extractions, or wait conditions',\n ].join('\\n');\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { Browser, BrowserContext, Cookie, chromium, firefox, webkit } from \"playwright\";\nimport { BrowserType, getDeviceBrowserType, getBrowserWindowSize, getDeviceChannel, getDeviceOptions, getRecordVideoSize } from \"shiplight-types\";\nimport { INIT_SCRIPT } from \"./constants\";\nimport { getBrowserCdpUrl, getCommonChromiumArgs, setWindowBounds } from \"./browserUtils\";\nimport logger from \"../utils/logger\";\n\nexport interface BrowserInstance {\n debugPort?: number;\n browser: Browser;\n context: BrowserContext;\n startTime: Date;\n timeout: NodeJS.Timeout | null;\n browserWsUrl: string;\n downloadsBasePath?: string;\n browserType: BrowserType;\n}\n\nexport interface BrowserManagerConfig {\n /** Base directory for videos/downloads/states. If not provided, directories won't be created. */\n testDir?: string;\n /** Auto-termination timeout in ms. null = disabled (default). */\n terminationTimeout?: number | null;\n /** Override headless mode. Default: true (or false if PLAYWRIGHT_HEADED=true) */\n headless?: boolean;\n /** Additional chromium args to pass to the browser */\n additionalArgs?: string[];\n}\n\nexport interface BrowserLaunchOptions {\n /** Debug port for CDP (Chrome DevTools Protocol). Required for devtools URLs and CDP WebSocket access. */\n debugPort?: number;\n /** Path to storage state file for session restoration */\n localStorageStatePath?: string;\n /** Enable video recording (requires testDir in config) */\n recordVideo?: boolean;\n /** Enable camera permission + fake device flags (Chromium-only) */\n enableCamera?: boolean;\n /** Enable microphone permission + fake device flags (Chromium-only) */\n enableMicrophone?: boolean;\n /** Absolute path to .wav file for fake audio capture (requires enableMicrophone) */\n fakeAudioCapturePath?: string;\n /** Disable web security (CORS, etc). Default: true */\n disableSecurity?: boolean;\n /** Override headless mode for this launch */\n headless?: boolean;\n /** Proxy configuration */\n proxy?: {\n server: string;\n username?: string;\n password?: string;\n };\n /** Device name for viewport/user-agent emulation */\n deviceName?: string;\n /** Cookies to set on the browser context */\n cookies?: Cookie[];\n /** Browser locale setting. Default: 'en-US' */\n locale?: string;\n}\n\nexport class BrowserManager {\n private videoBasePath?: string;\n private statesBasePath?: string;\n private downloadsBasePath?: string;\n private readonly terminationTimeout: number | null;\n private readonly defaultHeadless: boolean;\n private readonly additionalArgs: string[];\n\n constructor(config: BrowserManagerConfig = {}) {\n // Set up directories only if testDir is provided\n if (config.testDir) {\n this.videoBasePath = path.join(config.testDir, \"videos\");\n this.statesBasePath = path.join(config.testDir, \"states\");\n this.downloadsBasePath = path.join(config.testDir, \"downloads\");\n\n fs.mkdirSync(this.videoBasePath, { recursive: true });\n fs.mkdirSync(this.statesBasePath, { recursive: true });\n fs.mkdirSync(this.downloadsBasePath, { recursive: true });\n }\n\n this.terminationTimeout = config.terminationTimeout ?? null;\n this.defaultHeadless = config.headless ?? false;\n this.additionalArgs = config.additionalArgs ?? [];\n }\n\n async launchBrowser(options: BrowserLaunchOptions = {}): Promise<BrowserInstance> {\n const debugPort = options.debugPort;\n const isHeadless = options.headless ?? this.defaultHeadless;\n const browserType = getDeviceBrowserType(options.deviceName);\n const deviceChannel = getDeviceChannel(options.deviceName);\n const isChromium = browserType === BrowserType.Chromium;\n\n logger.info(`[BrowserManager] Launching ${browserType} browser for device: ${options.deviceName || 'default'}${debugPort ? ` on port ${debugPort}` : ''}`);\n\n // Build launch options based on browser type\n const launchOptions: any = {\n headless: isHeadless,\n };\n\n // Chromium-specific options\n if (isChromium) {\n const fakeDeviceArgs: string[] = [];\n if (options.enableCamera || options.enableMicrophone) {\n fakeDeviceArgs.push('--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream');\n\n // Add audio capture flag only if microphone is enabled and audio file path is provided\n if (options.enableMicrophone && options.fakeAudioCapturePath) {\n fakeDeviceArgs.push(`--use-file-for-fake-audio-capture=${options.fakeAudioCapturePath}`);\n logger.info(`[BrowserManager] Using fake audio capture file: ${options.fakeAudioCapturePath}`);\n }\n }\n\n launchOptions.args = [\n ...getCommonChromiumArgs(debugPort, options.disableSecurity ?? true, isHeadless),\n ...fakeDeviceArgs,\n ...this.additionalArgs,\n ];\n\n // Add channel if the device specifies one (for branded browsers like Chrome or Edge)\n if (deviceChannel) {\n launchOptions.channel = deviceChannel;\n logger.debug(`[BrowserManager] Launching browser with channel: ${deviceChannel}`);\n }\n }\n\n // Launch the appropriate browser\n let browser: Browser;\n switch (browserType) {\n case BrowserType.Firefox:\n browser = await firefox.launch(launchOptions);\n break;\n case BrowserType.Webkit:\n browser = await webkit.launch(launchOptions);\n break;\n case BrowserType.Chromium:\n default:\n browser = await chromium.launch(launchOptions);\n break;\n }\n\n const deviceOptions = getDeviceOptions(options.deviceName);\n const recordVideoSize = getRecordVideoSize(options.deviceName);\n\n // Build context options\n const contextOptions: any = {\n acceptDownloads: true,\n ...deviceOptions,\n timezoneId: \"America/Los_Angeles\",\n locale: options.locale || \"en-US\",\n };\n\n if (options.localStorageStatePath) {\n contextOptions.storageState = options.localStorageStatePath;\n }\n\n if (options.recordVideo && this.videoBasePath) {\n contextOptions.recordVideo = {\n dir: this.videoBasePath,\n size: { width: recordVideoSize.width, height: recordVideoSize.height },\n };\n }\n\n if (options.proxy) {\n contextOptions.proxy = options.proxy;\n }\n\n const context = await browser.newContext(contextOptions);\n context.addInitScript(INIT_SCRIPT);\n\n // Grant permissions for camera/mic if requested\n if (options.enableCamera || options.enableMicrophone) {\n if (!isChromium) {\n logger.warn('[BrowserManager] Camera/microphone enabled but browser is not Chromium; ignoring.');\n } else {\n const permissions: Array<'camera' | 'microphone'> = [];\n if (options.enableCamera) permissions.push('camera');\n if (options.enableMicrophone) permissions.push('microphone');\n try {\n await context.grantPermissions(permissions);\n logger.info(`[BrowserManager] Granted permissions: ${permissions.join(', ')}`);\n } catch (error) {\n logger.warn('[BrowserManager] Failed to grant camera/microphone permissions:', error);\n }\n }\n }\n\n // Set cookies if provided\n if (options.cookies && options.cookies.length > 0) {\n // Update expiration to be dynamic (365 days from now)\n const expires = (Date.now() + 365 * 24 * 60 * 60 * 1000) / 1000;\n const cookiesWithExpires = options.cookies.map(cookie => ({\n ...cookie,\n expires,\n }));\n await context.addCookies(cookiesWithExpires);\n logger.info(`[BrowserManager] Added ${options.cookies.length} cookies to browser context with expires=${expires}`);\n }\n\n // Set window bounds for Chromium browsers\n const windowSize = getBrowserWindowSize(options.deviceName);\n if (isChromium) {\n context.on('page', async (page) => {\n try {\n await setWindowBounds(page, windowSize.width, windowSize.height);\n } catch (error) {\n logger.warn('[BrowserManager] Failed to set window bounds via CDP:', error);\n }\n });\n }\n\n // Get WebSocket URL (CDP is only available for Chromium with a debug port)\n let wsEndpointData = '';\n if (isChromium && debugPort !== undefined) {\n try {\n wsEndpointData = await getBrowserCdpUrl(debugPort);\n } catch (error) {\n logger.warn('[BrowserManager] Failed to get CDP WebSocket URL:', error);\n }\n }\n\n const browserInstance: BrowserInstance = {\n debugPort,\n browser,\n context,\n startTime: new Date(),\n timeout: null,\n browserWsUrl: wsEndpointData,\n downloadsBasePath: this.downloadsBasePath,\n browserType,\n };\n\n // Set auto-termination timeout if configured\n if (this.terminationTimeout !== null) {\n const timeoutHandle = setTimeout(() => {\n logger.info(`[BrowserManager] Browser auto-terminated after ${this.terminationTimeout}ms`);\n this.terminateBrowser(browserInstance).catch((err) =>\n logger.error('[BrowserManager] Error terminating browser:', err)\n );\n }, this.terminationTimeout);\n\n browserInstance.timeout = timeoutHandle;\n }\n\n return browserInstance;\n }\n\n async terminateBrowser(instance: BrowserInstance): Promise<void> {\n if (instance.timeout) {\n clearTimeout(instance.timeout);\n }\n\n await instance.context.close();\n await instance.browser.close();\n }\n\n /** Get the configured video base path, or undefined if testDir was not provided */\n getVideoBasePath(): string | undefined {\n return this.videoBasePath;\n }\n\n /** Get the configured states base path, or undefined if testDir was not provided */\n getStatesBasePath(): string | undefined {\n return this.statesBasePath;\n }\n\n /** Get the configured downloads base path, or undefined if testDir was not provided */\n getDownloadsBasePath(): string | undefined {\n return this.downloadsBasePath;\n }\n}\n","export enum DevicePlatform {\n Desktop = 'desktop',\n Mobile = 'mobile',\n}","import { BrowserContext, Page } from 'playwright';\n\n/**\n * TabManager manages Playwright pages within a browser context.\n * Tracks the current page for use by validatePage().\n */\nexport class TabManager {\n private context: BrowserContext;\n private currentPage: Page | null = null;\n private currentIndex: number = -1;\n\n constructor(context: BrowserContext) {\n this.context = context;\n this.initialize();\n }\n\n /**\n * Initialize the TabManager by:\n * 1. Setting up listeners for new pages\n * 2. Setting up listeners for page close events\n * 3. Setting the initial current page if pages exist\n */\n private initialize(): void {\n // Listen for new pages created in the context\n this.context.on('page', async (page: Page) => {\n await this.handleNewPage(page);\n });\n\n // Get existing pages and set initial current page\n const existingPages = this.context.pages();\n if (existingPages.length > 0) {\n this.currentPage = existingPages[0];\n this.currentIndex = 0;\n }\n\n // Listen for new pages that might be created before we set up listeners\n // This handles the case where pages exist before TabManager is created\n for (const page of existingPages) {\n this.setupPageListeners(page);\n }\n }\n\n /**\n * Handle a new page being created\n */\n private async handleNewPage(page: Page): Promise<void> {\n try {\n this.setupPageListeners(page);\n const index = this.context.pages().indexOf(page);\n await this.setCurrentPage(page, index);\n } catch (error) {\n // Ignore errors for auto opened and closed pages\n }\n }\n\n /**\n * Set up listeners for a page (close event)\n */\n private setupPageListeners(page: Page): void {\n page.on('close', async () => {\n await this.handlePageClose(page);\n });\n }\n\n /**\n * Handle a page being closed\n */\n private async handlePageClose(closedPage: Page): Promise<void> {\n try {\n // If the closed page was the current page, switch to the last available page\n if (this.currentPage === closedPage) {\n const pages = this.context.pages();\n\n // Find first non-closed page from last to first\n // Use slice() to avoid mutating the original array\n const firstNonClosedPage = pages.slice().reverse().find(page => !page.isClosed());\n if (firstNonClosedPage) {\n await this.setCurrentPage(firstNonClosedPage, pages.indexOf(firstNonClosedPage));\n }\n }\n } catch (error) {\n // Ignore errors during cleanup/shutdown when page/context is closing\n // This is expected behavior when tests end\n }\n }\n\n /**\n * Set the current page\n */\n private async setCurrentPage(page: Page, index: number): Promise<void> {\n const pageChanged = this.currentPage !== page\n\n // Only bring to front if it's a different page\n this.currentPage = page;\n this.currentIndex = index;\n\n // Must first update the current page and index, then bring to front\n if (pageChanged) {\n await page.bringToFront();\n }\n }\n\n /**\n * Switch to a page by index\n * @param index - The index of the page to switch to (0-based)\n * @throws Error if the index is invalid\n */\n async switchToPage(index: number): Promise<Page> {\n const pages = this.context.pages();\n if (index < 0 || index >= pages.length) {\n throw new Error(`Invalid page index: ${index}. Available pages: 0-${pages.length - 1}`);\n }\n\n const targetPage = pages[index];\n if (targetPage.isClosed()) {\n throw new Error(`Page at index ${index} is closed`);\n }\n\n await this.setCurrentPage(targetPage, index);\n return targetPage;\n }\n\n /**\n * Close a page by index\n * @param index - The index of the page to close (0-based)\n * @throws Error if the index is invalid\n */\n async closePage(index: number): Promise<void> {\n const pages = this.context.pages();\n\n if (index < 0 || index >= pages.length) {\n throw new Error(`Invalid page index: ${index}. Available pages: 0-${pages.length - 1}`);\n }\n\n const targetPage = pages[index];\n if (targetPage.isClosed()) {\n throw new Error(`Page at index ${index} is already closed`);\n }\n\n await targetPage.close();\n // The handlePageClose method will automatically handle switching to the last page\n // if the closed page was the current page\n }\n\n /**\n * Get the current page\n * @returns The current page, or null if no pages are available\n */\n getCurrentPage(): Page | null {\n return this.currentPage;\n }\n\n /**\n * Get the index of the current page\n * @returns The index of the current page, or -1 if no page is current\n */\n getCurrentIndex(): number {\n return this.currentIndex;\n }\n\n /**\n * Get all pages in the context\n * @returns Array of all pages\n */\n getAllPages(): Page[] {\n return this.context.pages();\n }\n\n /**\n * Get the number of pages\n * @returns The number of pages in the context\n */\n getPageCount(): number {\n return this.context.pages().length;\n }\n}\n","/**\n * Wait utilities for Agent\n * Contains various wait/polling methods for browser automation\n */\n\nimport { Page } from 'playwright';\nimport logger from '../utils/logger';\nimport type { WebAgentContext } from './types';\nimport { waitForPageAndFramesLoad } from '../browser/browserUtils';\n\n/**\n * Wait for page to be stable (network idle with smart filtering)\n * Uses sophisticated network monitoring that filters out irrelevant requests\n * (analytics, ads, streaming, etc.) to determine when page is truly stable.\n *\n * @param page - Playwright Page object\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 3000)\n * @param minWaitTimeMs - Minimum time to wait before returning (default: 1000)\n */\nexport async function waitUntilStable(page: Page, timeoutMs: number = 3000, minWaitTimeMs: number = 1000): Promise<void> {\n try {\n await waitForPageAndFramesLoad(page, timeoutMs, minWaitTimeMs);\n } catch {\n // Timeout is ok, page might still be loading\n }\n}\n\n/**\n * Wait for a download to complete\n * @param page - Playwright Page object\n * @param context - Agent context containing download status\n * @param timeoutSeconds - Maximum time to wait in seconds\n */\nexport async function waitForDownloadComplete(\n page: Page,\n context: WebAgentContext,\n timeoutSeconds: number\n): Promise<void> {\n const timeoutMs = timeoutSeconds * 1000;\n const downloadCheckInterval = 100; // Check every 100ms\n const startTime = Date.now();\n\n // Wait for the download to complete\n while (!context.downloadStatus || context.downloadStatus.status === 'inProgress') {\n if (Date.now() - startTime > timeoutMs) {\n let errorMsg: string;\n if (!context.downloadStatus) {\n errorMsg = 'No download in progress or completed';\n } else {\n errorMsg = `Timed out after ${timeoutMs}ms waiting for download to complete`;\n }\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n await page.waitForTimeout(downloadCheckInterval);\n }\n\n // Check if download failed\n if (context.downloadStatus.status === 'failed') {\n const errorMsg = `Download failed: ${context.downloadStatus.error || 'Unknown error'}`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n context.agentNote = `Download completed: ${context.downloadStatus.filename}`;\n}\n\n/**\n * Wait until a condition becomes true using AI evaluation\n * Polls the condition at intervals until it's met or timeout is reached\n *\n * @param page - Playwright Page object\n * @param condition - Natural language condition to evaluate (e.g., \"page shows success message\")\n * @param evaluateCondition - Function that evaluates the condition using AI (returns boolean)\n * @param timeoutSeconds - Maximum time to wait in seconds (default: 60, max: 280)\n * @returns true if condition was met, false if timeout was reached\n */\nexport async function waitUntilCondition(\n page: Page,\n condition: string,\n evaluateCondition: (page: Page, condition: string, stepId?: string) => Promise<boolean>,\n timeoutSeconds: number = 60,\n stepId: string\n): Promise<boolean> {\n const maxWaitSeconds = Math.min(timeoutSeconds, 280);\n const maxCheckCount = 10;\n const checkInterval = Math.max(10, maxWaitSeconds / maxCheckCount) * 1000;\n const endTime = Date.now() + maxWaitSeconds * 1000;\n\n logger.info(`Waiting for condition: \"${condition}\" (timeout: ${maxWaitSeconds}s)`);\n\n while (true) {\n const lastCheckTime = Date.now();\n\n try {\n // Evaluate the condition using AI\n const isConditionMet = await evaluateCondition(page, condition, stepId);\n\n if (isConditionMet) {\n logger.info(`Condition met: \"${condition}\"`);\n return true;\n }\n } catch (error: any) {\n logger.warn(`Error evaluating condition: ${error.message}`);\n }\n\n const currentTime = Date.now();\n if (currentTime > endTime) {\n logger.warn(`Timeout waiting for condition: \"${condition}\"`);\n return false;\n }\n\n // Wait before next check\n const waitTime = checkInterval - (Date.now() - lastCheckTime);\n if (waitTime > 0) {\n await page.waitForTimeout(waitTime);\n }\n }\n}\n","/**\n * Default implementation of IAgentServices\n * Provides standard service methods that can be used by any agent implementation\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { BrowserContext, Page } from 'playwright';\nimport { waitForPageAndFramesLoad } from '../browser/browserUtils';\nimport { replaceVariables } from 'shiplight-types';\nimport logger from '../utils/logger';\nimport { PreloadedKnowledge } from 'shiplight-types';\nimport * as agentWait from './agentWait';\nimport { WebAgentContext } from '../core/types';\nimport { TabManager } from '../browser/tabManager';\n\n/**\n * Test data downloader function type\n * @param filePaths - Array of file names to download\n * @param filesDir - Directory where files should be downloaded to\n */\nexport type TestDataDownloader = (filePaths: string[], filesDir: string) => Promise<void>;\n\n/**\n * Knowledge retriever function type\n * @param statement - The statement/task to retrieve relevant knowledges for\n * @param threshold - Similarity threshold for filtering results (optional)\n * @param topK - Maximum number of results to return (optional)\n * @param usageScenario - Usage scenario for filtering ('general' or 'login', optional)\n * @returns Array of relevant knowledges\n */\nexport type KnowledgeRetriever = (statement: string, threshold?: number, topK?: number, usageScenario?: 'general' | 'login') => Promise<PreloadedKnowledge[]>;\n\n/**\n * Standard AgentServices implementation\n * Provides utility methods for agent operations\n * Uses AgentContext for state management\n */\nexport class AgentServices {\n private tabManager?: TabManager;\n private testDataDownloader?: TestDataDownloader;\n private testDataFileNames: string[] = [];\n private knowledgeRetriever?: KnowledgeRetriever;\n\n constructor(private context: WebAgentContext) {}\n\n /**\n * Set the test data downloader function\n * Called by sandbox to inject S3 download capability\n * For exported tests, this is not set (files are pre-populated)\n */\n setTestDataDownloader(downloader: TestDataDownloader, fileNames?: string[]): void {\n this.testDataDownloader = downloader;\n this.testDataFileNames = fileNames ?? [];\n }\n\n public setupPageTracking(context: BrowserContext): void {\n this.tabManager = new TabManager(context);\n }\n\n public async switchTab(pageIndex: number): Promise<Page> {\n if (!this.tabManager) {\n throw new Error('Tab manager not initialized');\n }\n\n return await this.tabManager.switchToPage(pageIndex);\n }\n\n public async closeTab(pageIndex: number): Promise<void> {\n if (!this.tabManager) {\n throw new Error('Tab manager not initialized');\n }\n\n await this.tabManager.closePage(pageIndex);\n }\n\n /**\n * Get the current active page from TabManager\n * @returns The current page, or null if TabManager is not initialized or no pages exist\n */\n public getCurrentPage(): Page | null {\n if (!this.tabManager) {\n return null;\n }\n return this.tabManager.getCurrentPage();\n }\n\n /**\n * Validate and return a valid page reference\n * Used by generated code to ensure page variable is updated after tab operations\n *\n * @param page - The page reference to validate\n * @returns A valid page (either the same page if still valid, or the current active page)\n */\n public validatePage(page: Page): Page {\n // 1. Check if TabManager has a different current page\n const currentPage = this.getCurrentPage();\n if (currentPage && currentPage !== page && !currentPage.isClosed()) {\n logger.info(`[validatePage] Page changed, switching to ${currentPage.url()}`);\n return currentPage;\n }\n\n // 2. If passed page is closed, get last remaining page from context\n if (page.isClosed()) {\n const pages = page.context().pages().filter(p => !p.isClosed());\n if (pages.length > 0) {\n const newPage = pages[pages.length - 1];\n logger.info(`[validatePage] Page closed, switching to ${newPage.url()}`);\n return newPage;\n }\n logger.error('[validatePage] No valid pages found');\n }\n\n return page;\n }\n\n /**\n * Replace variable placeholders with actual values from context.\n * Supports: ${varName}, $varName, {{ varName }}, <secret>varName</secret>\n * @see replaceVariables from agentContextUtils for full documentation\n */\n replaceVariables(input: string): string {\n return replaceVariables(input, this.context.variableStore.getAll());\n }\n\n getTestDataFileNames(): string[] {\n return this.testDataFileNames;\n }\n\n /**\n * Get the absolute path to a test data file\n * @param fileName - Name of the file in the test data directory\n * @returns Absolute path to the file\n */\n getTestDataFilePath(fileName: string): string {\n const testDataDir = this.context.testDataDir || process.cwd();\n return path.join(testDataDir, fileName);\n }\n\n /**\n * Download test data files on demand (used by upload_file action)\n * Checks local cache first, only downloads missing files\n * @param filePaths - Array of file paths/names to download\n */\n async downloadTestDataFiles(filePaths: string[]): Promise<void> {\n if (filePaths.length === 0) {\n logger.debug('[AgentServices] No file paths provided for download');\n return;\n }\n\n const filesDir = this.context.testDataDir || process.cwd();\n\n // Check which files are missing locally\n const missingFiles = filePaths.filter(filePath => {\n const fileName = path.basename(filePath);\n const localPath = path.join(filesDir, fileName);\n const exists = fs.existsSync(localPath);\n if (exists) {\n logger.debug(`[AgentServices] File already exists locally: ${fileName}`);\n }\n return !exists;\n });\n\n if (missingFiles.length === 0) {\n logger.debug('[AgentServices] All files exist locally, no download needed');\n return;\n }\n\n if (!this.testDataDownloader) {\n // No downloader configured - files should already exist (exported tests)\n logger.debug('[AgentServices] No test data downloader configured, assuming files are pre-populated');\n return;\n }\n\n logger.info(`[AgentServices] Downloading ${missingFiles.length} test data files: ${missingFiles.join(', ')}`);\n await this.testDataDownloader(missingFiles, filesDir);\n }\n\n /**\n * Wait for a download to complete\n * @param page - Playwright Page object\n * @param timeoutSeconds - Maximum time to wait in seconds\n */\n async waitForDownloadComplete(page: Page, timeoutSeconds: number): Promise<void> {\n return agentWait.waitForDownloadComplete(page, this.context, timeoutSeconds);\n }\n\n /**\n * Get the file path of the most recently downloaded file\n * @returns The file path if download is completed, null otherwise\n */\n getRecentDownloadedFilePath(): string | null {\n if (!this.context.downloadStatus) {\n logger.debug('No download found');\n return null;\n }\n\n if (this.context.downloadStatus.status !== 'completed') {\n logger.debug(`Download is ${this.context.downloadStatus.status}, not completed`);\n return null;\n }\n\n if (!this.context.downloadStatus.filePath) {\n logger.warn('Download completed but file path is missing');\n return null;\n }\n\n logger.info(`Retrieved recent download path: ${this.context.downloadStatus.filePath}`);\n return this.context.downloadStatus.filePath;\n }\n\n addSensitive(name: string, value: any): void {\n this.context.variableStore.set(name, value, true);\n }\n\n /**\n * Save a variable to the context\n * @param name - Variable name (without $ prefix)\n * @param value - Value to save\n */\n saveVariable(name: string, value: any): void {\n // Remove $ prefix if present\n const cleanName = name.startsWith('$') ? name.slice(1) : name;\n this.context.variableStore.set(cleanName, value);\n logger.debug(`Saved variable: ${cleanName} = ${JSON.stringify(value)}`);\n\n // Set agentNote to inform future steps about the saved variable\n this.context.agentNote = `A new value is saved to the variable \"${cleanName}\".`;\n }\n\n /**\n * Add a note to the current step execution\n * This note will be included in the execution history for future steps\n * @param note - Note to add to the execution context\n */\n addNote(note: string): void {\n if (!note || note.trim() === '') {\n return;\n }\n\n // Append to existing agentNote with newline if it already has content\n if (this.context.agentNote) {\n this.context.agentNote += `\\n${note.trim()}`;\n } else {\n this.context.agentNote = note.trim();\n }\n }\n\n /**\n * Read a variable from the context\n * @param name - Variable name (without $ prefix)\n * @returns The variable value, or undefined if not found\n */\n readVariable(name: string): any {\n // Remove $ prefix if present\n const cleanName = name.startsWith('$') ? name.slice(1) : name;\n const value = this.context.variableStore.get(cleanName);\n logger.debug(`Read variable: ${cleanName} = ${JSON.stringify(value)}`);\n return value;\n }\n\n /**\n * Generate 2FA code from secret key\n */\n async generate2faCode(secretKey: string): Promise<string> {\n try {\n // Import TOTP library - we'll use otplib which is commonly used\n const { authenticator } = await import('otplib');\n\n // Generate the TOTP code\n const code = authenticator.generate(secretKey);\n\n logger.info(`Generated 2FA code: ${code}`);\n return code;\n } catch (error: any) {\n logger.error(`Failed to generate 2FA code: ${error.message}`);\n throw new Error(`Failed to generate 2FA code: ${error.message}`);\n }\n }\n\n /**\n * Get DOM text from page\n */\n async getDOMText(page: Page): Promise<string> {\n return await page.evaluate(() => document.body.innerText);\n }\n\n /**\n * Wait for page and frames to fully load\n * Ensures page is fully loaded before continuing by waiting for network to be idle\n * @param page - Playwright Page object\n * @param maxWaitTimeMs - Maximum time to wait in milliseconds (default: 30000ms)\n */\n async waitUntilStable(page: Page, maxWaitTimeMs?: number): Promise<void> {\n return waitForPageAndFramesLoad(page, maxWaitTimeMs);\n }\n\n /**\n * Update the current page reference\n * Called by actions like switch_tab and go_to_url (with new_tab) when the active page changes\n * @param page - New page instance\n */\n setPage(page: Page): void {\n if (this.context.setPage) {\n logger.info('[AgentServices] Calling setPage callback');\n this.context.setPage(page);\n } else {\n logger.debug('[AgentServices] setPage callback not configured');\n }\n }\n\n /**\n * Get the LLM model configured for this agent\n */\n getModel(): string {\n return this.context.model;\n }\n\n /**\n * Get the variable store for accessing and setting test variables\n */\n get variableStore() {\n return this.context.variableStore;\n }\n\n /**\n * Get the download directory configured for this agent\n */\n getDownloadDir(): string | undefined {\n return this.context.downloadDir;\n }\n\n getActionSettings(): Record<string, any> {\n return this.context.organizationSettings?.action_code_conversion_settings || {};\n }\n\n /**\n * Get interactive class names from organization settings\n * @returns Array of interactive class names (empty if not configured)\n */\n getInteractiveClassNames(): string[] {\n return this.context.organizationSettings?.agent_settings?.interactive_class_names || [];\n }\n\n /**\n * Get whether to use clean screenshot for assertion\n * @returns True if clean screenshot should be used, false otherwise\n */\n isUseCleanScreenshotForAssertion(): boolean {\n return this.context.organizationSettings?.use_clean_screenshot_for_assertion || false;\n }\n\n /**\n * Get whether knowledge images are enabled for this organization\n * @returns True if knowledge images should be included in LLM prompts\n */\n isKnowledgeImagesEnabled(): boolean {\n return this.context.organizationSettings?.agent_settings?.enable_knowledge_images || false;\n }\n\n /**\n * Get whether sliced screenshots are enabled for this organization\n * Sliced screenshots split the image into 3 parts (left/middle/right) to maximize token usage\n *\n * Can be overridden via USE_SLICED_SCREENSHOTS env var in sandbox for local testing:\n * USE_SLICED_SCREENSHOTS=true pnpm dev\n *\n * @returns True if sliced screenshots should be used\n */\n isSlicedScreenshotsEnabled(): boolean {\n return this.context.organizationSettings?.agent_settings?.use_sliced_screenshots || false;\n }\n\n /**\n * Get whether sliced screenshots should be resized to 768x768\n * @returns True if sliced screenshots should be resized\n */\n isResizeSlicedScreenshotsEnabled(): boolean {\n return this.context.organizationSettings?.agent_settings?.resize_sliced_screenshots || false;\n }\n\n /**\n * Get whether accessibility tree should be used for element detection (experimental)\n * Uses Chrome DevTools Protocol Accessibility API for authoritative interactive element detection\n *\n * Can be overridden via USE_ACCESSIBILITY_TREE env var in sandbox for local testing:\n * USE_ACCESSIBILITY_TREE=true pnpm dev\n *\n * @returns True if accessibility tree should be used\n */\n isAccessibilityTreeEnabled(): boolean {\n return this.context.organizationSettings?.agent_settings?.use_accessibility_tree || false;\n }\n\n /**\n * Get whether action intent filtering is enabled.\n * When enabled, DOM extraction filters elements based on the intended action type\n * (click, input, scroll) to reduce noise and improve action generation accuracy.\n *\n * @returns True if action intent filtering should be used\n */\n isActionIntentFilteringEnabled(): boolean {\n return this.context.organizationSettings?.agent_settings?.use_action_intent_filtering || false;\n }\n\n /**\n * Get whether to use the TypeScript DOM tree implementation\n * The TypeScript version is compiled from index.ts and provides the same functionality\n * with better type safety for future enhancements.\n *\n * Can be overridden via USE_DOM_TREE_TS env var for local testing:\n * USE_DOM_TREE_TS=true pnpm dev\n *\n * @returns True if TypeScript DOM tree should be used\n */\n isDomTreeTsEnabled(): boolean {\n // Environment variable override for local testing\n const envOverride = process.env.USE_DOM_TREE_TS;\n if (envOverride !== undefined) {\n return envOverride === 'true' || envOverride === '1';\n }\n return this.context.organizationSettings?.agent_settings?.use_dom_tree_ts || false;\n }\n\n /**\n * Get DomService options based on organization settings\n * This is a convenience method to pass to new DomService(page, options)\n *\n * @returns DomServiceOptions with useDomTreeTs flag\n */\n getDomServiceOptions(): { useDomTreeTs: boolean } {\n return {\n useDomTreeTs: this.isDomTreeTsEnabled(),\n };\n }\n\n /**\n * Set the knowledge retriever function\n * Called by sandbox/test-fixtures to inject knowledge retrieval capability\n *\n * - Sandbox: uses core-api to retrieve knowledges per statement\n * - Test-fixtures (runner mode): uses core-api to retrieve knowledges per statement\n * - Test-fixtures (offline mode): returns all preloaded knowledges\n *\n * @param retriever - Function that retrieves knowledges for a given statement\n */\n setKnowledgeRetriever(retriever: KnowledgeRetriever): void {\n this.knowledgeRetriever = retriever;\n }\n\n /**\n * Check if knowledge retriever is configured\n * Used to determine if knowledge retrieval functionality is available\n *\n * @returns True if knowledge retriever is configured, false otherwise\n */\n hasKnowledgeRetriever(): boolean {\n return this.knowledgeRetriever !== undefined;\n }\n\n /**\n * Retrieve relevant knowledges for a statement\n * Used by action generation and evaluation to get context-relevant knowledge\n *\n * @param statement - The statement/task to retrieve relevant knowledges for\n * @param threshold - Similarity threshold for filtering results (optional)\n * @param topK - Maximum number of results to return (optional)\n * @param usageScenario - Usage scenario for filtering ('general' or 'login', optional)\n * @returns Array of relevant knowledges (empty if no retriever configured)\n */\n async retrieveKnowledges(statement: string, threshold?: number, topK?: number, usageScenario?: 'general' | 'login'): Promise<PreloadedKnowledge[]> {\n if (!this.knowledgeRetriever) {\n logger.debug('[AgentServices] No knowledge retriever configured');\n return [];\n }\n\n try {\n const knowledges = await this.knowledgeRetriever(statement, threshold, topK, usageScenario);\n logger.debug(`[AgentServices] Retrieved ${knowledges.length} knowledges for statement`);\n return knowledges;\n } catch (error: any) {\n logger.warn(`[AgentServices] Failed to retrieve knowledges: ${error.message}`);\n return [];\n }\n }\n}\n","/**\n * WebAgent - Core AI-powered browser automation\n * Depends on playwright (not @playwright/test) so it can be used outside of test context\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { Page } from 'playwright';\nimport type ActionHandler from '../actions/handler';\nimport { ActionEntity, IAgent } from '../actions/types';\nimport { DomService } from '../dom';\nimport { agentLogger } from '../utils/agentLogger';\nimport logger from '../utils/logger';\nimport * as agentFile from './agentFile';\nimport { evaluateStatement, executeStep, generateActionStep, runTask } from './agentHelpers';\nimport type { LoginCache, LoginConfig, LoginResult, OAuth2Account } from 'shiplight-types';\nimport { generateAndValidateLoginLocators, validateLogin } from './agentLogin';\nimport { AgentServices } from './agentServices';\nimport * as agentWait from './agentWait';\nimport { ActionGenerationDebugInfo } from 'shiplight-types';\nimport { AgentStepResult, StepExecutionResult, WebAgentContext } from './types';\n\n/**\n * Execution strategy for the execute() method\n * - 'single': Single-step execution using executeStep (default, fast, single LLM call)\n * - 'multi': Multi-step autonomous execution using runTask (handles complex statements, multiple LLM calls)\n */\nexport type ExecutionStrategy = 'single' | 'multi';\n\n/** Maximum steps for self-healing recovery attempts */\nconst MAX_SELF_HEALING_STEPS = 3;\n\n/** Maximum steps for dismissing intrusive popups (cookie consent, etc.) */\nconst MAX_MODAL_DISMISSAL_STEPS = 3;\n\n/** Default maximum steps for multi-step execution */\nconst DEFAULT_MULTI_STEP_MAX = 40;\n\n/**\n * Mask sensitive variable values for logging\n * @param variables - All variables\n * @param sensitiveKeys - Set of keys that are sensitive\n * @returns New object with sensitive values replaced by '*****'\n */\nfunction maskSensitiveVariables(\n variables: Record<string, any>,\n sensitiveKeys: Set<string>\n): Record<string, any> {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(variables)) {\n result[key] = sensitiveKeys.has(key) ? '*****' : value;\n }\n return result;\n}\n\n/**\n * Options for the execute() method\n */\nexport interface ExecuteOptions {\n /** Execution strategy: 'single' for single-step, 'multi' for multi-step autonomous execution */\n strategy?: ExecutionStrategy;\n /** Maximum number of steps for multi-step execution (only used when strategy is 'multi') */\n maxSteps?: number;\n}\n\nexport class WebAgent implements IAgent {\n public readonly agentServices: AgentServices;\n private _actionHandler?: ActionHandler;\n\n // Tracks newly generated action entities (from self-healing or AI fallback)\n // Key: stmtUid, Value: the generated ActionEntity\n private _newActionEntities: Map<string, ActionEntity> = new Map();\n\n constructor(private context: WebAgentContext) {\n this.agentServices = new AgentServices(context);\n // Initialize token usages array if not present\n if (!this.context.tokenUsages) {\n this.context.tokenUsages = [];\n }\n }\n\n /**\n * Get all newly generated action entities from this test run.\n * Called by test runner after test success to persist cache updates.\n * @returns Map of stmtUid -> ActionEntity\n */\n getNewActionEntities(): Map<string, ActionEntity> {\n return this._newActionEntities;\n }\n\n /**\n * Lazy-loaded ActionHandler instance to avoid circular dependencies.\n * Uses ESM dynamic import for lazy loading.\n */\n private async getActionHandler(): Promise<ActionHandler> {\n if (!this._actionHandler) {\n // Dynamic import to avoid circular dependencies at module load time\n const { default: ActionHandlerClass } = await import('../actions/handler.js');\n this._actionHandler = new ActionHandlerClass();\n }\n return this._actionHandler!;\n }\n\n /**\n * Execute a structured action by name.\n *\n * This is a low-level method that executes a specific action with given parameters.\n * For natural language instructions, use the public SDK's `act()` method instead.\n *\n * @param actionName - The action name (e.g., \"click\", \"input_text\", \"hover\")\n * @param page - Playwright Page object\n * @param entity - Action entity with locator and other fields\n *\n * @example\n * ```typescript\n * await agent.execAction(\"click\", page, { locator: \"getByRole('button', { name: 'Submit' })\" });\n * await agent.execAction(\"input_text\", page, {\n * locator: \"getByRole('textbox')\",\n * action_data: { kwargs: { text: \"Hello\" } }\n * });\n * ```\n */\n async execAction(actionName: string, page: Page, entity: Partial<ActionEntity>): Promise<void> {\n const actionHandler = await this.getActionHandler();\n const action = actionHandler.getAction(actionName);\n if (!action) {\n throw new Error(`Unknown action: ${actionName}`);\n }\n await action.execute(page, entity as ActionEntity, this.agentServices);\n }\n\n /**\n * Attempt to dismiss any intrusive popup modal using AI.\n * Returns whether a modal was actually dismissed.\n *\n * @param page - Playwright Page object\n * @param currentTaskDescription - Optional description of the current task (to avoid dismissing related dialogs)\n * @returns Object with success status, whether a modal was dismissed, and details\n *\n * @example\n * ```typescript\n * const result = await agent.dismissModalIfPresent(page, \"Click the submit button\");\n * if (result.modalDismissed) {\n * console.log(\"Modal was dismissed:\", result.details);\n * }\n * ```\n */\n async dismissModalIfPresent(\n page: Page,\n currentTaskDescription?: string\n ): Promise<{ success: boolean; modalDismissed: boolean; details: string }> {\n try {\n const result = await this.execute(\n page,\n `TASK: Check if there is an INTRUSIVE POPUP blocking the page, and dismiss it if present.\n\nONLY dismiss intrusive popups such as:\n- Cookie/GDPR consent banners\n- Newsletter or email signup popups\n- Promotional discount or upsell popups\n- \"Rate our app\" or feedback request popups\n- Push notification permission requests\n- Age verification popups\n- Or similar unwanted popups that interrupt the user experience\n\nDO NOT dismiss or interact with:\n- Normal UI elements, buttons, or controls on the page\n- Dialogs that are part of the user's workflow (confirmations, form submissions, etc.)\n- Dropdown menus, filters, or date pickers\n${currentTaskDescription ? `- Anything related to: \"${currentTaskDescription}\"` : ''}\n\nIf you find an intrusive popup, dismiss it by clicking its close/X button or \"No thanks\"/\"Decline\" button.\n\nIMPORTANT: Only act with HIGH CONFIDENCE. If unsure whether something is an intrusive popup, do nothing.\nIt's better to miss a popup than to accidentally interact with normal page elements.\n\nIf NO intrusive popup is present, do nothing and report that no intrusive popup was found.`,\n undefined, // no stepId - not tracked\n false, // usePureVision\n { strategy: 'multi', maxSteps: MAX_MODAL_DISMISSAL_STEPS }\n );\n\n const modalDismissed = !!(result.actions && result.actions.length > 0);\n return {\n success: result.success,\n modalDismissed,\n details: result.details || (modalDismissed ? 'Modal dismissed' : 'No modal found'),\n };\n } catch (error: any) {\n return {\n success: false,\n modalDismissed: false,\n details: error.message || 'Modal dismissal failed',\n };\n }\n }\n\n /**\n * Internal method for test fixtures to access context\n * @internal - Not part of the public API\n */\n _getContext(): WebAgentContext {\n return this.context;\n }\n\n /**\n * Set up download tracking for a page\n * Listens to download events and updates context.downloadStatus\n * Call this method for each page where you want to track downloads\n * @param page - The Playwright page to track downloads on\n */\n public setupDownloadTracking(page: Page): void {\n logger.info(`[Download Tracking] Setting up download tracking for page: ${page.url()}`);\n\n page.on('download', async (download) => {\n const filename = download.suggestedFilename();\n logger.info(`[Download Tracking] Download event detected! File: ${filename}`);\n\n // Track download start\n this.context.downloadStatus = {\n filename,\n status: 'inProgress',\n startTime: Date.now(),\n };\n logger.info(\n `[Download Tracking] Download status set to inProgress: ${JSON.stringify(this.context.downloadStatus)}`,\n );\n\n try {\n // Determine download directory\n const downloadDir = this.context.downloadDir || path.join(process.cwd(), 'downloads');\n\n // Ensure download directory exists\n if (!fs.existsSync(downloadDir)) {\n fs.mkdirSync(downloadDir, { recursive: true });\n }\n\n // Save the download\n const downloadPath = path.join(downloadDir, filename);\n logger.info(`[Download Tracking] Downloading file to: ${downloadPath}`);\n await download.saveAs(downloadPath);\n\n // Track successful completion\n this.context.downloadStatus = {\n filename,\n status: 'completed',\n startTime: this.context.downloadStatus.startTime,\n filePath: downloadPath,\n timestamp: Date.now(),\n };\n\n logger.info(`[Download Tracking] Download completed: ${downloadPath}`);\n logger.info(\n `[Download Tracking] Download status set to completed: ${JSON.stringify(this.context.downloadStatus)}`,\n );\n } catch (error: any) {\n // Track failure\n this.context.downloadStatus = {\n filename,\n status: 'failed',\n startTime: this.context.downloadStatus.startTime,\n error: error.message,\n timestamp: Date.now(),\n };\n\n logger.error(`[Download Tracking] Download failed for ${filename}: ${error.message}`);\n }\n });\n }\n\n /**\n * Collect token usages into the context\n * Accepts either debugInfo (with tokenUsages inside) or raw tokenUsages array\n * @internal\n */\n private collectTokenUsages(\n debugInfoOrTokenUsages?:\n | ActionGenerationDebugInfo\n | Array<{\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n estimated_cost_usd?: number;\n model?: string;\n }>,\n ): void {\n // Extract tokenUsages from either debugInfo or direct array\n let tokenUsages:\n | Array<{\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n estimated_cost_usd?: number;\n model?: string;\n }>\n | undefined;\n\n if (Array.isArray(debugInfoOrTokenUsages)) {\n // Direct token usages array (from TaskResult)\n tokenUsages = debugInfoOrTokenUsages;\n } else if (debugInfoOrTokenUsages?.tokenUsages) {\n // From debugInfo (from ActionStepResult)\n tokenUsages = debugInfoOrTokenUsages.tokenUsages;\n }\n\n if (tokenUsages && tokenUsages.length > 0) {\n if (!this.context.tokenUsages) {\n this.context.tokenUsages = [];\n }\n this.context.tokenUsages.push(...tokenUsages);\n logger.debug(\n `[collectTokenUsages] Added ${tokenUsages.length} token usage(s), total: ${this.context.tokenUsages.length}`,\n );\n } else {\n logger.debug(`[collectTokenUsages] No token usages to collect (undefined or empty)`);\n }\n }\n\n /**\n * Track an AI action (LLM API call) for a step\n * @internal\n */\n private trackAIAction(\n stepId: string,\n actionType: 'execute' | 'generate' | 'assert' | 'evaluate' | 'run',\n debugInfo?: ActionGenerationDebugInfo,\n count: number = 1,\n statement?: string,\n explanation?: string,\n ): void {\n if (!stepId) return;\n\n const tokenUsages = debugInfo?.tokenUsages || [];\n\n // Initialize array if needed\n if (!this.context.aiActionDetails) {\n this.context.aiActionDetails = [];\n }\n\n // Find existing entry for this stepId and actionType\n let entry = this.context.aiActionDetails.find(\n (detail) => detail.stepId === stepId && detail.actionType === actionType,\n );\n\n if (entry) {\n // Update existing entry\n entry.count += count;\n if (tokenUsages.length > 0) {\n entry.tokenUsages.push(...tokenUsages);\n }\n // Update with latest debug info (for retries, keep last values)\n if (debugInfo?.userPrompt) entry.userPrompt = debugInfo.userPrompt;\n if (debugInfo?.rawLlmResponse) entry.rawLlmResponse = debugInfo.rawLlmResponse;\n if (debugInfo?.reasoningContent) entry.reasoningContent = debugInfo.reasoningContent;\n if (debugInfo?.elementTree) entry.elementTree = debugInfo.elementTree;\n if (debugInfo?.screenshotWithSom) entry.screenshotWithSom = debugInfo.screenshotWithSom;\n if (explanation) entry.explanation = explanation;\n } else {\n // Create new entry with all available debug info\n this.context.aiActionDetails.push({\n stepId,\n actionType,\n count,\n tokenUsages: [...tokenUsages],\n statement,\n userPrompt: debugInfo?.userPrompt,\n rawLlmResponse: debugInfo?.rawLlmResponse,\n reasoningContent: debugInfo?.reasoningContent,\n explanation,\n elementTree: debugInfo?.elementTree,\n screenshotWithSom: debugInfo?.screenshotWithSom,\n });\n }\n\n logger.debug(`[trackAIAction] Tracked ${actionType} for step ${stepId}, count: ${count}`);\n }\n\n /**\n * Make an AI-powered assertion\n */\n async assert(page: Page, statement: string, stepId?: string): Promise<boolean> {\n logger.info(`Asserting statement: ${statement}`);\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, statement);\n }\n\n await this.agentServices.waitUntilStable(page);\n\n try {\n const executionHistory = this.getCompletedExecutionHistory();\n\n const result = await evaluateStatement(statement, page, this.agentServices, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n });\n\n // Collect token usages and track intelligent action\n this.collectTokenUsages(result.debugInfo);\n\n // Store debugInfo in context for retrieval after VM execution\n this.context.lastActionDebugInfo = result.debugInfo;\n\n const explanation = result.explanation || result.error || 'No explanation';\n if (stepId) {\n this.trackAIAction(stepId, 'assert', result.debugInfo, 1, statement, explanation);\n }\n\n // Save explanation to agentNote so it can be passed to front-end\n this.context.agentNote = explanation;\n\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(\n stepId,\n result.success ? 'success' : 'failure',\n explanation,\n undefined,\n result.debugInfo,\n );\n }\n\n if (!result.success) {\n this.addToExecutionHistory(`Assert: \"${statement}\"`, `Failed: ${explanation}`);\n throw new Error(`Assertion failed: ${explanation}`);\n }\n\n this.addToExecutionHistory(`Assert: \"${statement}\"`, `Passed: ${explanation}`);\n return true;\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n throw error;\n }\n }\n\n /**\n * Evaluate a condition using AI (returns boolean, doesn't throw)\n */\n async evaluate(page: Page, statement: string, stepId?: string): Promise<boolean> {\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, statement);\n }\n\n await this.agentServices.waitUntilStable(page);\n\n try {\n const executionHistory = this.getCompletedExecutionHistory();\n\n const result = await evaluateStatement(statement, page, this.agentServices, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n });\n\n // Collect token usages and track intelligent action\n this.collectTokenUsages(result.debugInfo);\n\n // Store debugInfo in context for retrieval after VM execution\n this.context.lastActionDebugInfo = result.debugInfo;\n\n const explanation = result.explanation || result.error || 'No explanation';\n if (stepId) {\n this.trackAIAction(stepId, 'evaluate', result.debugInfo, 1, statement, explanation);\n }\n\n // Save explanation to agentNote so it can be passed to front-end\n this.context.agentNote = explanation;\n\n if (stepId && this.context.stepTracking) {\n // For evaluate() used in IF/WHILE conditions, the status reflects whether\n // the evaluation itself succeeded, not whether the condition was true/false.\n // The condition result (true/false) is captured in the explanation.\n await this.updateStepResult(\n stepId,\n 'success',\n explanation,\n undefined,\n result.debugInfo,\n );\n }\n\n // For evaluate(), 'unknown' means we couldn't determine the answer\n // Return false instead of throwing, so if/while statements can proceed\n if (!result.success) {\n logger.warn(`AI evaluation returned false/unknown: ${explanation}`);\n this.addToExecutionHistory(`Evaluate: \"${statement}\"`, `Unknown/False: ${explanation}`);\n return false;\n }\n\n this.addToExecutionHistory(`Evaluate: \"${statement}\"`, `Result: true - ${explanation}`);\n return true;\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n throw error;\n }\n }\n\n /**\n * Perform a single action on the page.\n *\n * Directly calls executeStep for precise single-action execution.\n * Use this for discrete actions like clicking, filling, or selecting.\n *\n * @param page - Playwright Page object\n * @param instruction - Natural language instruction for a single action\n * @returns Result with success status and details\n *\n * @example\n * ```typescript\n * await agent.performAction(page, 'Click the submit button');\n * await agent.performAction(page, 'Fill the email field with test@example.com');\n * ```\n */\n async performAction(page: Page, instruction: string): Promise<AgentStepResult> {\n logger.info(`Act: ${instruction}`);\n\n const executionHistory = this.getCompletedExecutionHistory();\n\n const result = await executeStep(instruction, page, this.agentServices, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n });\n\n // Collect token usages\n this.collectTokenUsages(result.debugInfo);\n\n if (result.status !== 'success' || result.actionEntities.length === 0) {\n const explanation = result.explanation || result.error || 'Action failed';\n return {\n success: false,\n details: explanation,\n };\n }\n\n // Add to execution history\n const actionDescription = result.actionEntities[0]?.action_description || instruction;\n this.addToExecutionHistory(instruction, actionDescription);\n\n return {\n success: true,\n details: result.explanation,\n };\n }\n\n /**\n * Execute an AI-powered action\n *\n * @param page - Playwright Page object\n * @param statement - Natural language instruction to execute\n * @param stepId - Optional step ID for tracking\n * @param usePureVision - Optional flag to use pure vision mode\n * @param options - Optional execution options\n * @param options.strategy - Execution strategy: 'single' (default) or 'multi'\n * @param options.maxSteps - Maximum steps for multi-step execution (only used when strategy is 'multi')\n *\n * @example\n * ```typescript\n * // Default behavior (single-step, fast)\n * await agent.execute(page, \"Click the submit button\");\n *\n * // Explicit single-step\n * await agent.execute(page, \"Click the submit button\", stepId, false, { strategy: 'single' });\n *\n * // Multi-step for complex statements\n * await agent.execute(page, \"Fill out the entire form with test data and submit\", stepId, false, {\n * strategy: 'multi',\n * maxSteps: 10\n * });\n * ```\n */\n async execute(\n page: Page,\n statement: string,\n stepId?: string,\n usePureVision?: boolean,\n options?: ExecuteOptions,\n ): Promise<AgentStepResult> {\n const strategy = options?.strategy || 'single';\n logger.info(`Executing statement: ${statement} (strategy: ${strategy})`);\n\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, statement);\n }\n\n try {\n const executionHistory = this.getCompletedExecutionHistory();\n\n // Clear agent note before execution to capture fresh notes\n this.context.agentNote = '';\n\n let result;\n let completesInstruction: boolean;\n\n if (strategy === 'multi') {\n // Create onEvent callback to capture debugInfo from each step and save to artifacts\n const onEvent = stepId && this.context.stepTracking?.artifactsDir\n ? (event: any) => {\n if (event.type === 'action' && event.debugInfo) {\n // Save debugInfo for each action in multi-step execution\n const stepSubId = `${stepId}-step${event.step}`;\n this.saveDebugInfoToFiles(stepSubId, event.debugInfo, this.context.model);\n }\n }\n : undefined;\n\n // Use runTask for complex multi-step execution\n result = await runTask(statement, page, this.agentServices, onEvent, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n maxSteps: options?.maxSteps ?? DEFAULT_MULTI_STEP_MAX,\n });\n\n // For multi-step, require goal completion\n completesInstruction = result.completed;\n\n // For multi-step execution, collect token usages from the aggregated result\n this.collectTokenUsages(result.tokenUsages);\n\n // Track intelligent action for multi-step execution\n if (stepId && result.tokenUsages) {\n const debugInfoForTracking = { tokenUsages: result.tokenUsages };\n this.trackAIAction(\n stepId,\n 'execute',\n debugInfoForTracking,\n result.actionEntities?.length || 1,\n statement,\n result.explanation || 'Multi-step execution completed',\n );\n }\n } else {\n // Use executeStep for single-step execution (default)\n result = await executeStep(statement, page, this.agentServices, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n usePureVision: usePureVision,\n });\n\n // For single-step, don't require goal completion (action success is enough)\n completesInstruction = true;\n\n // Collect token usages and track intelligent action for single-step\n this.collectTokenUsages(result.debugInfo);\n\n // Store debugInfo in context for retrieval after VM execution\n this.context.lastActionDebugInfo = result.debugInfo;\n\n if (stepId) {\n const explanation = result.explanation || result.error || 'No explanation';\n this.trackAIAction(stepId, 'execute', result.debugInfo, 1, statement, explanation);\n }\n }\n\n if (result.status !== 'success' || result.actionEntities.length === 0 || !completesInstruction) {\n const explanation = result.explanation || result.error || 'Goal not completed';\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', explanation, undefined, result.debugInfo);\n }\n throw new Error(`Action failed: ${explanation}`);\n }\n\n // Use agentNote if set by action execution, otherwise fall back to result.explanation\n const executionNote = this.context.agentNote || result.explanation || 'Action executed successfully';\n\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'success', executionNote, undefined, result.debugInfo);\n }\n\n this.addToExecutionHistory(`Execute: \"${statement}\"`, executionNote);\n return { success: true, details: executionNote, actions: result.actionEntities, debugInfo: result.debugInfo };\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n this.addToExecutionHistory(`Execute: \"${statement}\"`, `Failed: ${error.message}`);\n throw error;\n }\n }\n\n /**\n * Generate an AI-powered action (without executing it)\n * Similar to execute() but only generates the actionEntity without execution\n * Returns the action with a goal completion flag (success = goalAccomplished)\n */\n async generate(page: Page, statement: string, stepId?: string, usePureVision?: boolean): Promise<AgentStepResult> {\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, statement);\n }\n\n try {\n const executionHistory = this.getCompletedExecutionHistory();\n\n const result = await generateActionStep(statement, page, this.agentServices, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n usePureVision: usePureVision,\n });\n\n // Collect token usages and track intelligent action\n this.collectTokenUsages(result.debugInfo);\n\n if (result.status !== 'success' || result.actionEntities.length === 0) {\n const explanation = result.explanation || result.error || 'No explanation';\n if (stepId) {\n this.trackAIAction(stepId, 'generate', result.debugInfo, 1, statement, explanation);\n }\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', explanation, undefined, result.debugInfo);\n }\n throw new Error(explanation);\n }\n\n // success flag indicates if goal was accomplished (completed field in StepResult)\n const success = result.completed;\n const explanation = result.explanation || 'Action generated successfully';\n\n if (stepId) {\n this.trackAIAction(stepId, 'generate', result.debugInfo, 1, statement, explanation);\n }\n\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'success', explanation, undefined, result.debugInfo);\n }\n\n // Don't add to execution history since we didn't execute\n logger.info(\n `[generate] Generated action for \"${statement}\": ${explanation}, goalAccomplished: ${result.completed}`,\n );\n\n return {\n success,\n details: explanation,\n actions: result.actionEntities,\n debugInfo: result.debugInfo,\n };\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n throw error;\n }\n }\n\n /**\n * Run a complex AI step (multi-step execution)\n */\n async run(\n page: Page,\n task: string,\n stepId?: string,\n options?: {\n abortSignal?: AbortSignal;\n maxSteps?: number;\n onAction?: (action: ActionEntity) => void;\n },\n ): Promise<AgentStepResult> {\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, task);\n }\n\n try {\n const executionHistory = this.getCompletedExecutionHistory();\n\n // Build onEvent callback to stream actions back to caller and save debugInfo to artifacts\n const shouldSaveArtifacts = stepId && this.context.stepTracking?.artifactsDir;\n const onEvent = (options?.onAction || shouldSaveArtifacts)\n ? (event: any) => {\n if (event.type === 'action' && event.action_entity) {\n // Forward action to caller's callback if provided\n options?.onAction?.(event.action_entity);\n // Save debugInfo to artifacts for each step\n if (shouldSaveArtifacts && event.debugInfo) {\n const stepSubId = `${stepId}-step${event.step}`;\n this.saveDebugInfoToFiles(stepSubId, event.debugInfo, this.context.model);\n }\n }\n }\n : undefined;\n\n const result = await runTask(task, page, this.agentServices, onEvent, {\n executionHistory,\n variables: this.context.variableStore.getAll(),\n sensitiveKeys: this.context.variableStore.getAllSensitiveKeys(),\n abortSignal: options?.abortSignal,\n maxSteps: options?.maxSteps,\n });\n\n agentLogger.log(`Run task result: status=${result.status}, completed=${result.completed}, actions=${result.actionEntities?.length || 0}`);\n\n // Collect token usages from the task execution (multi-step aggregated)\n this.collectTokenUsages(result.tokenUsages);\n\n const success = result.status === 'success' && result.completed;\n const summary = result.explanation || result.error || (success ? 'Step completed' : 'Step failed');\n\n // Track intelligent action for multi-step run (count = number of actions)\n if (stepId && result.tokenUsages) {\n // For run, track using the aggregated token usages\n // Create a synthetic debugInfo with the tokenUsages\n const debugInfoForTracking = { tokenUsages: result.tokenUsages };\n this.trackAIAction(stepId, 'run', debugInfoForTracking, result.actionEntities?.length || 1, task, summary);\n }\n\n if (stepId && this.context.stepTracking) {\n // Multi-step tasks don't have single debugInfo, pass undefined\n await this.updateStepResult(stepId, success ? 'success' : 'failure', summary, undefined, undefined);\n }\n\n this.addToExecutionHistory(`Run: \"${task}\"`, summary);\n\n if (result.status === 'error') {\n throw new Error(summary);\n }\n\n agentLogger.log(`Run result: success=${success}, details=${summary}`);\n return {\n success,\n details: summary,\n actions: result.actionEntities,\n };\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n this.addToExecutionHistory(`Run: \"${task}\"`, `Failed: ${error.message}`);\n throw error;\n }\n }\n\n /**\n * Wrapper function for executing action code with self-healing support\n * Similar to context._internal.step from monots\n *\n * @param page - Playwright page\n * @param fn - Async function containing the action code to execute\n * @param description - Description of the step\n * @param stepId - Step ID for tracking\n * @param stmtUid - Statement UID for tracking new action entities (optional)\n * @param canSelfHeal - Whether to enable self-healing on failure (default: true)\n * @param maxSteps - Maximum steps for self-healing (only used when strategy is 'multi', default: 3)\n * @returns Result of the function execution\n */\n async step(\n page: Page,\n fn: () => Promise<any>,\n description: string,\n stepId: string,\n stmtUid?: string,\n canSelfHeal: boolean = true,\n maxSteps?: number,\n ): Promise<any> {\n const startTime = Date.now();\n\n // Create step result for tracking\n if (this.context.stepTracking) {\n await this.createStepResult(page, stepId, description);\n }\n\n await this.agentServices.waitUntilStable(page);\n\n // Set current step ID for state transitions and console log association\n if (this.context.stepTracking) {\n this.context.stepTracking.currentStepId = stepId;\n }\n\n // Capture state BEFORE execution (for state transitions)\n const urlBefore = page.url();\n const domBefore = await this.captureDOMSnapshot(page);\n const variablesBefore = this.context.stepTracking?.captureVariables\n ? maskSensitiveVariables(\n this.context.variableStore.getAll(),\n this.context.variableStore.getAllSensitiveKeys()\n )\n : {};\n const screenshotBeforePath = this.context.stepTracking?.results[stepId]?.screenshot;\n\n try {\n logger.info(`Executing step ${stepId}: ${description}`);\n\n // Add to execution history with empty feedback initially\n this.addToExecutionHistory(description, '');\n\n // Clear agent note before execution\n this.context.agentNote = '';\n\n // Execute the provided function\n const result = await fn();\n\n // Capture state AFTER execution\n const urlAfter = page.url();\n const domAfter = await this.captureDOMSnapshot(page);\n const variablesAfter = this.context.stepTracking?.captureVariables\n ? maskSensitiveVariables(\n this.context.variableStore.getAll(),\n this.context.variableStore.getAllSensitiveKeys()\n )\n : {};\n\n // Update step result on success\n if (this.context.stepTracking) {\n await this.updateStepResult(stepId, 'success', '');\n }\n\n // Build state transition entries (interlaced format: state → action → state)\n if (this.context.stepTracking?.captureStateTransitions && this.context.stepTracking.stateTransitions) {\n const durationMs = Date.now() - startTime;\n const screenshotAfterPath = this.context.stepTracking.results[stepId]?.screenshot;\n const now = Date.now();\n\n // If this is the first entry, push initial state\n if (this.context.stepTracking.stateTransitions.length === 0) {\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlBefore,\n domSnapshot: domBefore || undefined,\n variables: variablesBefore,\n screenshotPath: screenshotBeforePath,\n timestamp: startTime,\n });\n }\n\n // Push action entry\n this.context.stepTracking.stateTransitions.push({\n type: 'action',\n stepId,\n description,\n action: {\n playwrightCode: [fn.toString()],\n },\n consoleLogs: this.getConsoleLogsForStep(stepId),\n durationMs,\n status: 'success',\n });\n\n // Push resulting state entry\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlAfter,\n domSnapshot: domAfter || undefined,\n variables: variablesAfter,\n screenshotPath: screenshotAfterPath,\n timestamp: now,\n });\n }\n\n // Update execution history with success feedback from agentNote\n let executionNote = this.context.agentNote;\n if (!executionNote || executionNote.trim() === '') {\n executionNote = 'Execution successful';\n }\n if (this.context.executionHistory && this.context.executionHistory.length > 0) {\n this.context.executionHistory[this.context.executionHistory.length - 1][1] = executionNote;\n }\n\n // Clear current step ID\n if (this.context.stepTracking) {\n this.context.stepTracking.currentStepId = undefined;\n }\n\n return result;\n } catch (error: any) {\n const ableToSelfHeal = canSelfHeal && !this.context.isSelfHealing && this.context.selfHealingStrategy !== 'none';\n\n if (ableToSelfHeal) {\n // Save the screenshot to the test result folder on first failure\n if (this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n\n // Update execution history with failure feedback\n if (this.context.executionHistory && this.context.executionHistory.length > 0) {\n this.context.executionHistory[this.context.executionHistory.length - 1][1] = error.message;\n }\n }\n\n if (!description || description.trim() === '') {\n logger.error('No description provided for self-healing');\n // Clear current step ID\n if (this.context.stepTracking) {\n this.context.stepTracking.currentStepId = undefined;\n }\n throw error;\n }\n\n // If in self-healing mode already, don't attempt to heal again\n if (!ableToSelfHeal) {\n logger.info(`Failed to heal at step ${stepId}. ${description}`);\n this.context.isSelfHealing = false;\n\n // Build state transition entries for failure (interlaced format)\n if (this.context.stepTracking?.captureStateTransitions && this.context.stepTracking.stateTransitions) {\n const urlAfter = page.url();\n const domAfter = await this.captureDOMSnapshot(page);\n const variablesAfter = this.context.stepTracking.captureVariables\n ? maskSensitiveVariables(\n this.context.variableStore.getAll(),\n this.context.variableStore.getAllSensitiveKeys()\n )\n : {};\n const durationMs = Date.now() - startTime;\n const now = Date.now();\n\n // If this is the first entry, push initial state\n if (this.context.stepTracking.stateTransitions.length === 0) {\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlBefore,\n domSnapshot: domBefore || undefined,\n variables: variablesBefore,\n screenshotPath: screenshotBeforePath,\n timestamp: startTime,\n });\n }\n\n // Push action entry\n this.context.stepTracking.stateTransitions.push({\n type: 'action',\n stepId,\n description,\n action: {\n playwrightCode: [fn.toString()],\n },\n consoleLogs: this.getConsoleLogsForStep(stepId),\n durationMs,\n status: 'failure',\n errorMessage: error.message,\n });\n\n // Push resulting state entry\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlAfter,\n domSnapshot: domAfter || undefined,\n variables: variablesAfter,\n timestamp: now,\n });\n }\n\n // Update duration even when throwing\n if (this.context.stepTracking) {\n await this.updateStepResult(stepId, undefined, undefined);\n this.context.stepTracking.currentStepId = undefined;\n }\n throw error;\n }\n\n // Enable self-healing mode\n this.context.isSelfHealing = true;\n\n logger.info(`Action failed at step ${stepId}. ${description}`);\n logger.info(`with error: ${error.message}`);\n\n // Try to dismiss modal before self-healing if enabled\n let modalDismissedNote: string | undefined;\n if (this.context.autoDisableModal) {\n logger.info('Attempting modal dismissal before self-healing...');\n const dismissResult = await this.dismissModalIfPresent(page, description);\n logger.info(`Modal dismissal result: ${dismissResult.details}`);\n if (dismissResult.modalDismissed) {\n modalDismissedNote = `[Auto-dismissed modal: ${dismissResult.details}]`;\n\n // Modal was dismissed - retry the original action before falling back to LLM self-healing\n logger.info(`Modal dismissed, retrying original action for step ${stepId}`);\n try {\n const retryResult = await fn();\n this.context.isSelfHealing = false;\n\n // Update execution history with success after modal dismissal\n if (this.context.executionHistory && this.context.executionHistory.length > 0) {\n this.context.executionHistory[this.context.executionHistory.length - 1][1] = modalDismissedNote + ' Retry successful';\n }\n\n if (this.context.stepTracking) {\n await this.updateStepResult(stepId, 'success', modalDismissedNote);\n this.context.stepTracking.currentStepId = undefined;\n }\n\n return retryResult;\n } catch (retryError: any) {\n logger.info(`Retry after modal dismissal failed: ${retryError.message}, falling back to self-healing`);\n }\n }\n }\n\n // Determine strategy based on maxSteps if provided, otherwise use global setting\n // maxSteps <= 0: 'none' (no self-healing)\n // maxSteps === 1: 'single' (one retry)\n // maxSteps > 1: 'multi' (multi-step recovery)\n let strategy: ExecutionStrategy;\n let effectiveMaxSteps: number;\n\n if (maxSteps !== undefined) {\n if (maxSteps <= 0) {\n // No self-healing requested - but we're already in error handler, so just throw\n throw error;\n } else if (maxSteps === 1) {\n strategy = 'single';\n effectiveMaxSteps = 1;\n } else {\n strategy = 'multi';\n effectiveMaxSteps = maxSteps;\n }\n } else {\n strategy = (this.context.selfHealingStrategy ?? 'single') as ExecutionStrategy;\n effectiveMaxSteps = MAX_SELF_HEALING_STEPS;\n }\n\n logger.info(`Calling execute() to self-heal (strategy: ${strategy}${strategy === 'multi' ? `, maxSteps: ${effectiveMaxSteps}` : ''})`);\n\n try {\n // Use execute to self-heal with configured strategy\n const result = await this.execute(page, description, stepId, false, {\n strategy,\n maxSteps: strategy === 'multi' ? effectiveMaxSteps : undefined\n });\n this.context.isSelfHealing = false;\n\n if (!result.success) {\n throw new Error(`Self-healing failed: ${result.details}`);\n }\n\n // Store new action entity if stmtUid provided\n // Use the LAST action (not first) since multi-step may dismiss popups before the actual action\n const lastAction = result.actions?.at(-1);\n if (stmtUid && lastAction) {\n this._newActionEntities.set(stmtUid, lastAction);\n logger.info(`Stored new action entity for stmtUid: ${stmtUid} (last of ${result.actions?.length} actions)`);\n }\n\n // Capture state after self-healing\n const urlAfter = page.url();\n const domAfter = await this.captureDOMSnapshot(page);\n const variablesAfter = this.context.stepTracking?.captureVariables\n ? maskSensitiveVariables(\n this.context.variableStore.getAll(),\n this.context.variableStore.getAllSensitiveKeys()\n )\n : {};\n\n // Update status after successful self-healing, include modal dismissal note if applicable\n if (this.context.stepTracking) {\n const message = modalDismissedNote || '';\n await this.updateStepResult(stepId, 'success', message);\n }\n\n // Build state transition entries for self-healed step (interlaced format)\n if (this.context.stepTracking?.captureStateTransitions && this.context.stepTracking.stateTransitions) {\n const durationMs = Date.now() - startTime;\n const screenshotAfterPath = this.context.stepTracking.results[stepId]?.screenshot;\n const now = Date.now();\n\n // If this is the first entry, push initial state\n if (this.context.stepTracking.stateTransitions.length === 0) {\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlBefore,\n domSnapshot: domBefore || undefined,\n variables: variablesBefore,\n screenshotPath: screenshotBeforePath,\n timestamp: startTime,\n });\n }\n\n // Push action entry\n this.context.stepTracking.stateTransitions.push({\n type: 'action',\n stepId,\n description,\n action: {\n actionEntity: lastAction,\n playwrightCode: result.actions?.map(a => a.locator || ''),\n llmPrompt: result.debugInfo?.userPrompt as string,\n llmResponse: result.debugInfo?.rawLlmResponse,\n llmReasoning: result.debugInfo?.reasoningContent,\n },\n consoleLogs: this.getConsoleLogsForStep(stepId),\n durationMs,\n status: 'success',\n });\n\n // Push resulting state entry\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlAfter,\n domSnapshot: domAfter || undefined,\n variables: variablesAfter,\n screenshotPath: screenshotAfterPath,\n timestamp: now,\n });\n }\n\n // Clear current step ID\n if (this.context.stepTracking) {\n this.context.stepTracking.currentStepId = undefined;\n }\n\n return result;\n } catch (healError: any) {\n this.context.isSelfHealing = false;\n\n // Build state transition entries for failed self-healing (interlaced format)\n if (this.context.stepTracking?.captureStateTransitions && this.context.stepTracking.stateTransitions) {\n const urlAfter = page.url();\n const domAfter = await this.captureDOMSnapshot(page);\n const variablesAfter = this.context.stepTracking.captureVariables\n ? maskSensitiveVariables(\n this.context.variableStore.getAll(),\n this.context.variableStore.getAllSensitiveKeys()\n )\n : {};\n const durationMs = Date.now() - startTime;\n const now = Date.now();\n\n // If this is the first entry, push initial state\n if (this.context.stepTracking.stateTransitions.length === 0) {\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlBefore,\n domSnapshot: domBefore || undefined,\n variables: variablesBefore,\n screenshotPath: screenshotBeforePath,\n timestamp: startTime,\n });\n }\n\n // Push action entry\n this.context.stepTracking.stateTransitions.push({\n type: 'action',\n stepId,\n description,\n action: {\n playwrightCode: [fn.toString()],\n },\n consoleLogs: this.getConsoleLogsForStep(stepId),\n durationMs,\n status: 'failure',\n errorMessage: healError.message,\n });\n\n // Push resulting state entry\n this.context.stepTracking.stateTransitions.push({\n type: 'state',\n url: urlAfter,\n domSnapshot: domAfter || undefined,\n variables: variablesAfter,\n timestamp: now,\n });\n }\n\n // Clear current step ID\n if (this.context.stepTracking) {\n this.context.stepTracking.currentStepId = undefined;\n }\n\n throw healError;\n }\n }\n }\n\n /**\n * Extract element data using AI and store in variable\n */\n async extract(page: Page, elementDescription: string, variableName: string, stepId?: string): Promise<void> {\n if (stepId && this.context.stepTracking) {\n await this.createStepResult(page, stepId, `Extract ${elementDescription} into ${variableName}`);\n }\n\n try {\n // Construct the statement for AI to understand what to extract\n const statement = `Extract ${elementDescription} and save to ${variableName}`;\n\n // Use execute to generate the extraction action\n const result = await this.execute(page, statement, stepId);\n\n if (!result.success) {\n throw new Error(`AI extraction failed: ${result.details}`);\n }\n\n // The AI will have generated a save_variable action that stores the value\n // in the context, so we don't need to do anything else here\n\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'success', `Extracted ${elementDescription} to ${variableName}`);\n }\n } catch (error: any) {\n if (stepId && this.context.stepTracking) {\n await this.updateStepResult(stepId, 'failure', error.message);\n }\n throw error;\n }\n }\n\n /**\n * Get DOM text from page\n */\n async getDOMText(page: Page): Promise<string> {\n const domService = new DomService(this.agentServices.getDomServiceOptions());\n const interactiveClassNames = this.agentServices.getInteractiveClassNames();\n const domState = await domService.getClickableElements(page, {\n interactiveClassNames,\n });\n return domState.elementTree.clickableElementsToString();\n }\n\n /**\n * Capture DOM snapshot for state transitions\n * @internal\n */\n private async captureDOMSnapshot(page: Page): Promise<{ elementTreeText: string; elementCount: number; timestamp: number } | null> {\n if (!this.context.stepTracking?.captureStateTransitions || !this.context.stepTracking?.captureDom) {\n return null;\n }\n\n try {\n const domService = new DomService(this.agentServices.getDomServiceOptions());\n const interactiveClassNames = this.agentServices.getInteractiveClassNames();\n const domState = await domService.getClickableElements(page, {\n interactiveClassNames,\n highlightElements: false,\n viewportExpansion: 0,\n });\n return {\n elementTreeText: domState.elementTree.clickableElementsToString(),\n elementCount: domState.selectorMap.size,\n timestamp: Date.now(),\n };\n } catch (e) {\n logger.warn('Failed to capture DOM snapshot:', e);\n return null;\n }\n }\n\n /**\n * Get console logs associated with a specific step\n * @internal\n */\n private getConsoleLogsForStep(stepId: string): Array<{ type: string; message: string; timestamp: number }> {\n if (!this.context.stepTracking?.consoleLogs) {\n return [];\n }\n return this.context.stepTracking.consoleLogs\n .filter(log => log.stepId === stepId)\n .map(log => ({\n type: log.type,\n message: log.message,\n timestamp: log.timestamp,\n }));\n }\n\n /**\n * Wait for page to be stable\n * @param page - Playwright Page object\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 3000)\n * @param minWaitTimeMs - Minimum time to wait before returning (default: 1000)\n */\n async waitUntilStable(page: Page, timeoutMs: number = 3000, minWaitTimeMs: number = 1000): Promise<void> {\n return agentWait.waitUntilStable(page, timeoutMs, minWaitTimeMs);\n }\n\n /**\n * Wait until a condition becomes true using AI evaluation\n * Polls the condition at intervals until it's met or timeout is reached\n *\n * @param page - Playwright Page object\n * @param condition - Natural language condition to evaluate (e.g., \"page shows success message\")\n * @param timeoutSeconds - Maximum time to wait in seconds (default: 60, max: 280)\n * @returns true if condition was met, false if timeout was reached\n *\n * @example\n * ```typescript\n * // Wait for a success message to appear\n * const success = await agent.waitUntilCondition(page, \"page shows success message\", 30);\n * if (!success) {\n * throw new Error(\"Success message did not appear\");\n * }\n * ```\n */\n async waitUntilCondition(page: Page, condition: string, timeoutSeconds: number = 60, stepId: string): Promise<boolean> {\n return agentWait.waitUntilCondition(page, condition, (p, c, s) => this.evaluate(p, c, s), timeoutSeconds, stepId);\n }\n\n /**\n * Upload one or more files to a file input element\n * Supports multiple upload strategies: direct file input, file chooser dialog, or AI-powered upload\n *\n * @param page - Playwright Page object\n * @param filePaths - File path(s) relative to test data directory, or absolute paths\n * @param options - Upload options (selector, target description, etc.)\n * @returns Promise that resolves when upload is complete\n *\n * @example\n * ```typescript\n * // Upload with CSS selector\n * await agent.uploadFile(page, \"document.pdf\", {\n * selector: \"input[type='file']\"\n * });\n *\n * // Upload multiple files with XPath\n * await agent.uploadFile(page, [\"file1.pdf\", \"file2.jpg\"], {\n * selector: \"xpath=//input[@type='file']\",\n * useFileInput: true\n * });\n *\n * // Upload with AI (no selector)\n * await agent.uploadFile(page, \"avatar.png\", {\n * targetDescription: \"profile picture upload area\"\n * });\n * ```\n */\n async uploadFile(\n page: Page,\n filePaths: string | string[],\n options: agentFile.UploadFileOptions = {},\n stepId?: string,\n ): Promise<void> {\n return agentFile.uploadFile(page, filePaths, options, this.context, (p, s, sid) => this.execute(p, s, sid), stepId);\n }\n\n /**\n * Perform login page flow with navigation and AI-powered authentication\n * @param page - The page to login on\n * @param config - Login configuration\n * @param cache - Optional login cache with cached actions and validation expressions\n * @returns LoginResult with success status, page, and storage state\n */\n async loginPage(page: Page, config: LoginConfig, cache?: LoginCache): Promise<LoginResult<Page>> {\n const { LoginType } = await import('./agentLogin');\n\n logger.info(\"Start login\");\n\n agentLogger.section('Login Flow');\n agentLogger.log(`Site URL: ${config.site_url}`);\n agentLogger.log(`Account type: ${config.account.type}`);\n agentLogger.log(`Has cached actions: ${!!cache?.cached_actions?.length}`);\n agentLogger.log(`Has validation exprs: ${!!cache?.validation_exprs?.length}`);\n\n if (config.skip_verification) {\n logger.info('Skipping login verification (skip_verification=true)');\n return { success: true, page };\n }\n\n // Enrich test context with credentials\n if (config.account.type === LoginType.PASSWORD || config.account.type === LoginType.OAUTH2) {\n this.context.variableStore.set('username', config.account.username, true);\n this.context.variableStore.set('password', config.account.password, true);\n }\n if (config.account.two_factor_auth_config?.type === 'totp') {\n this.context.variableStore.set('otp_secret_key', config.account.two_factor_auth_config.data, true);\n }\n\n // Navigate to the site\n logger.info(`Navigating to: ${config.site_url}`);\n await page.goto(config.site_url);\n // Wait for page to be stable, at least 3s\n await this.waitUntilStable(page, 10000, 3000);\n\n // Level 1: Check if already logged in via storage state\n // Storage state is applied at the browser context level before this method is called\n if (cache?.validation_exprs && cache.validation_exprs.length > 0) {\n agentLogger.log('Level 1: Checking if already logged in via storage state...');\n agentLogger.log(`Validation expressions: ${JSON.stringify(cache.validation_exprs)}`);\n const isLoggedIn = await validateLogin(page, cache.validation_exprs);\n if (isLoggedIn) {\n agentLogger.log('Level 1 SUCCESS: Already logged in via storage state');\n logger.info('Login: Already logged in via storage state');\n const storageState = await page.context().storageState();\n return {\n success: true,\n page,\n storage_state: storageState,\n cached_actions: cache.cached_actions,\n validation_exprs: cache.validation_exprs,\n alreadyLoggedIn: true, // Skip update since nothing changed\n };\n }\n agentLogger.log('Level 1 FAILED: Storage state validation failed, trying Level 2');\n }\n\n // Level 2: Try cached login actions (only if we have validation expressions to verify success)\n if (cache?.cached_actions && cache.cached_actions.length > 0 &&\n cache?.validation_exprs && cache.validation_exprs.length > 0) {\n agentLogger.log(`Level 2: Attempting cached login with ${cache.cached_actions.length} actions`);\n try {\n const cachedResult = await this.executeCachedLogin(page, cache.cached_actions, cache.validation_exprs);\n if (cachedResult.success) {\n agentLogger.log('Level 2 SUCCESS: Cached login succeeded');\n logger.info('Login: Cached login succeeded');\n const storageState = await page.context().storageState();\n\n return {\n success: true,\n page,\n storage_state: storageState,\n cached_actions: cache.cached_actions,\n validation_exprs: cache.validation_exprs,\n };\n }\n agentLogger.log('Level 2 FAILED: Cached login failed, trying Level 3');\n } catch (error: any) {\n agentLogger.log(`Level 2 ERROR: ${error.message}, trying Level 3`);\n }\n } else if (cache?.cached_actions && cache.cached_actions.length > 0) {\n agentLogger.log('Level 2 SKIPPED: Cached actions exist but no validation expressions, going to Level 3');\n }\n\n // Build login prompt for agent-based login (Level 3)\n let signInPrompt = `First check if the page is already signed in. If it is, do nothing.\nUse your best judgement to determine if the page is signed in.\n`;\n\n if (config.verification_hint) {\n signInPrompt += `\nSigned in verification hint: ${config.verification_hint}\n`;\n }\n\n if (config.account.type === LoginType.PASSWORD) {\n signInPrompt += `\nIf the page is not signed in, sign in using provided credentials ($username, $password, etc).\n`;\n } else {\n const oauth2Account = config.account as OAuth2Account;\n signInPrompt += `\nIf the page is not signed in, sign in with the OAuth provider \"${oauth2Account.provider_name}\" using provided credentials\n($username, $password), and use $otp_secret_key to generate $otp_code for two-factor authentication. If you can't login with the provided credentials, just stop and report the failure.\n`;\n }\n\n if (config.additional_prompt) {\n signInPrompt += `\nAdditional instructions: ${config.additional_prompt}\n`;\n }\n\n // Try to login with agent\n try {\n agentLogger.log('Level 3: Attempting agent-based login');\n agentLogger.log(`Login prompt:\\n${signInPrompt}`);\n \n // Temporarily switch knowledge retriever to use 'login' scenario\n // Save the original retriever to restore later\n const originalRetriever = (this.agentServices as any).knowledgeRetriever;\n let loginKnowledgeRetrieverSet = false;\n\n if (this.agentServices.hasKnowledgeRetriever() && originalRetriever) {\n agentLogger.log('Setting up login knowledge retriever...');\n // Create a wrapper that calls the original retriever with usageScenario='login'\n this.agentServices.setKnowledgeRetriever(async (statement: string, threshold?: number, topK?: number, usageScenario?: 'general' | 'login') => {\n // Force usageScenario to 'login' for all knowledge retrievals during login flow\n return await originalRetriever(statement, threshold, topK, 'login');\n });\n loginKnowledgeRetrieverSet = true;\n agentLogger.log('Login knowledge retriever configured');\n }\n\n // Declare loginResult in outer scope so it can be accessed after the try-finally block\n let loginResult;\n try {\n // Use run() instead of execute() to complete the full login flow (multiple actions)\n loginResult = await this.run(page, signInPrompt, 'login');\n \n if (!loginResult.success) {\n agentLogger.log('Level 3 FAILED: Agent login failed');\n logger.info('Login: Failed');\n return { success: false, page };\n }\n } finally {\n // Restore original knowledge retriever if we changed it\n if (loginKnowledgeRetrieverSet && originalRetriever) {\n this.agentServices.setKnowledgeRetriever(originalRetriever);\n agentLogger.log('Restored original knowledge retriever');\n }\n }\n\n // Get storage state after successful login\n const storageState = await page.context().storageState();\n\n // Generate validation locators for future login verification\n // This creates locators that distinguish signed-in from signed-out state\n // Skip if num_verification_exprs is explicitly set to 0\n let validationExprs: string[] | undefined;\n if (config.num_verification_exprs !== 0) {\n try {\n agentLogger.log('Generating validation locators for future login verification...');\n const generatedLocators = await generateAndValidateLoginLocators(\n page,\n config.site_url,\n {\n verification_hint: config.verification_hint,\n num_verification_exprs: config.num_verification_exprs,\n },\n // AI step callback - use this.run() which is equivalent to old aiStep\n async (p, prompt) => this.run(p, prompt),\n // DOM text callback\n async (p) => this.getDOMText(p)\n );\n if (generatedLocators) {\n validationExprs = generatedLocators;\n agentLogger.log(`Generated ${validationExprs.length} validation locator(s): ${JSON.stringify(validationExprs)}`);\n } else {\n agentLogger.log('Failed to generate validation locators, login will still succeed');\n }\n } catch (error: any) {\n agentLogger.log(`Error generating validation locators: ${error.message}`);\n // Don't fail login if validation locator generation fails\n }\n } else {\n agentLogger.log('Skipping validation locator generation (num_verification_exprs=0)');\n }\n\n agentLogger.log('Level 3 SUCCESS: Agent login succeeded');\n logger.info('Login: Agent login succeeded');\n\n // Preserve existing cached actions if Level 3 produced no actions (page was already logged in)\n const newActions = loginResult.actions || [];\n const finalCachedActions = newActions.length > 0 ? newActions : cache?.cached_actions;\n\n return {\n success: true,\n page,\n storage_state: storageState,\n cached_actions: finalCachedActions,\n validation_exprs: validationExprs,\n };\n } catch (error: any) {\n agentLogger.error(`Agent login failed: ${error.message}`);\n logger.info('Login: Failed');\n return { success: false, page };\n }\n }\n\n /**\n * Execute cached login actions directly without AI\n * @param page - The page to execute actions on\n * @param actions - Cached action entities from previous successful login\n * @param validationExprs - Optional Playwright locator expressions to validate login success\n * @returns Success status\n */\n private async executeCachedLogin(\n page: Page,\n actions: ActionEntity[],\n validationExprs?: string[],\n ): Promise<{ success: boolean }> {\n // Dynamically import ActionHandler to avoid circular dependencies\n const ActionHandlerClass = (await import('../actions/handler')).default;\n const actionHandler = new ActionHandlerClass();\n\n // Execute each cached action using the action handler (which has legacy aliases)\n for (let i = 0; i < actions.length; i++) {\n const action = actions[i];\n agentLogger.log(\n `Executing cached action ${i + 1}/${actions.length}: ${action.action_description || action.action_data?.action_name}`,\n );\n\n try {\n if (!action.action_data) {\n agentLogger.log(`Action ${i + 1} missing action_data, skipping`);\n continue;\n }\n\n // Use actionHandler.execute which supports legacy action names\n // Pass agentServices (not 'this') since IAction.execute expects AgentServices\n await actionHandler.execute(page, action, this.agentServices);\n\n // Wait for stability after action\n await this.waitUntilStable(page, 5000);\n } catch (error: any) {\n agentLogger.error(`Cached action ${i + 1} failed: ${error.message}`);\n return { success: false };\n }\n }\n\n // Validate login success using validation expressions\n if (validationExprs && validationExprs.length > 0) {\n const isLoggedIn = await validateLogin(page, validationExprs);\n if (!isLoggedIn) {\n return { success: false };\n }\n }\n\n return { success: true };\n }\n\n /**\n * Get completed execution history (excluding current incomplete step)\n */\n private getCompletedExecutionHistory(): Array<[string, string]> {\n if (!this.context.executionHistory || this.context.executionHistory.length === 0) {\n return [];\n }\n\n const lastEntry = this.context.executionHistory[this.context.executionHistory.length - 1];\n if (!lastEntry[1] || lastEntry[1].trim() === '') {\n return this.context.executionHistory.slice(0, -1);\n }\n\n return this.context.executionHistory;\n }\n\n /**\n * Add an entry to execution history\n */\n private addToExecutionHistory(statement: string, feedback: string): void {\n if (!this.context.executionHistory) {\n this.context.executionHistory = [];\n }\n this.context.executionHistory.push([statement, feedback]);\n }\n\n /**\n * Create step result for tracking\n */\n private async createStepResult(page: Page, stepId: string, description: string): Promise<void> {\n if (!this.context.stepTracking) return;\n\n // Don't recreate if already exists\n if (this.context.stepTracking.results[stepId]) {\n return;\n }\n\n // Set current step ID for console log association\n this.context.stepTracking.currentStepId = stepId;\n // Notify listener of step change (for syncing with TestContext)\n if (this.context.stepTracking.onStepChange) {\n this.context.stepTracking.onStepChange(stepId);\n }\n\n page = await this.agentServices.validatePage(page);\n await this.waitUntilStable(page);\n\n const result: StepExecutionResult = {\n description,\n startTime: Date.now(),\n artifacts: [],\n };\n\n // Take screenshot if artifacts directory configured\n if (this.context.stepTracking.artifactsDir) {\n // Create per-step directory\n const stepDir = path.join(this.context.stepTracking.artifactsDir, stepId.replace(/\\./g, '-'));\n fs.mkdirSync(stepDir, { recursive: true });\n const screenshotPath = path.join(stepDir, 'screenshot.png');\n await page.screenshot({ type: 'png', path: screenshotPath });\n result.screenshot = screenshotPath;\n }\n\n this.context.stepTracking.results[stepId] = result;\n }\n\n /**\n * Save debug info to local files and return artifact paths\n * Files are saved in the artifacts subdirectory of the screenshot directory\n * @internal\n */\n private saveDebugInfoToFiles(\n stepId: string,\n debugInfo: ActionGenerationDebugInfo,\n model?: string,\n ): Record<string, string> {\n const artifacts: Record<string, string> = {};\n\n if (!this.context.stepTracking?.artifactsDir) {\n logger.debug(`[saveDebugInfoToFiles] No artifacts directory configured, skipping debug info save`);\n return artifacts;\n }\n\n // Create per-step artifacts directory\n const stepArtifactsDir = path.join(\n this.context.stepTracking.artifactsDir,\n stepId.replace(/\\./g, '-'),\n );\n\n try {\n fs.mkdirSync(stepArtifactsDir, { recursive: true });\n\n // Save system prompt\n if (debugInfo.systemPrompt) {\n const systemPromptPath = path.join(stepArtifactsDir, 'system_prompt.txt');\n fs.writeFileSync(systemPromptPath, debugInfo.systemPrompt);\n artifacts.system_prompt_path = systemPromptPath;\n logger.debug(`[saveDebugInfoToFiles] Saved system prompt to: ${systemPromptPath}`);\n }\n\n // Save user prompt (can be string or structured MessageForLogging[])\n if (debugInfo.userPrompt) {\n if (typeof debugInfo.userPrompt === 'string') {\n // Legacy string format\n const userPromptPath = path.join(stepArtifactsDir, 'user_prompt.txt');\n fs.writeFileSync(userPromptPath, debugInfo.userPrompt);\n artifacts.user_prompt_path = userPromptPath;\n logger.debug(`[saveDebugInfoToFiles] Saved user prompt to: ${userPromptPath}`);\n } else {\n // Structured MessageForLogging[] format - save as JSON\n // Strip base64 image data to reduce file size\n const strippedMessages = debugInfo.userPrompt.map((msg: any) => {\n if (Array.isArray(msg.content)) {\n return {\n ...msg,\n content: msg.content.map((part: any) => {\n if (part.type === 'image' && part.file?.startsWith('data:')) {\n return { ...part, file: '[base64 image data stripped]' };\n }\n return part;\n }),\n };\n }\n return msg;\n });\n const messagesPath = path.join(stepArtifactsDir, 'messages.json');\n const messagesData = {\n system: debugInfo.systemPrompt,\n messages: strippedMessages,\n };\n fs.writeFileSync(messagesPath, JSON.stringify(messagesData, null, 2));\n artifacts.messages_path = messagesPath;\n logger.debug(`[saveDebugInfoToFiles] Saved messages to: ${messagesPath}`);\n }\n }\n\n // Save raw LLM response\n if (debugInfo.rawLlmResponse) {\n const modelPrefix = model ? model.replace(/[\\/\\\\:]/g, '-') : 'llm';\n const responsePath = path.join(stepArtifactsDir, `${modelPrefix}_response.txt`);\n fs.writeFileSync(responsePath, debugInfo.rawLlmResponse);\n artifacts.response_path = responsePath;\n logger.debug(`[saveDebugInfoToFiles] Saved LLM response to: ${responsePath}`);\n }\n\n // Save screenshot with SOM annotations (key is screenshot_path to match expected format)\n if (debugInfo.screenshotWithSom) {\n const screenshotPath = path.join(stepArtifactsDir, 'screenshot.png');\n fs.writeFileSync(screenshotPath, Buffer.from(debugInfo.screenshotWithSom, 'base64'));\n artifacts.screenshot_path = screenshotPath;\n logger.debug(`[saveDebugInfoToFiles] Saved SOM screenshot to: ${screenshotPath}`);\n }\n\n // Save reasoning content if available (for native thinking models)\n if (debugInfo.reasoningContent) {\n const reasoningPath = path.join(stepArtifactsDir, 'reasoning.txt');\n fs.writeFileSync(reasoningPath, debugInfo.reasoningContent);\n artifacts.reasoning_path = reasoningPath;\n logger.debug(`[saveDebugInfoToFiles] Saved reasoning to: ${reasoningPath}`);\n }\n\n logger.debug(`[saveDebugInfoToFiles] Saved ${Object.keys(artifacts).length} artifacts for step ${stepId}`);\n } catch (error) {\n logger.error(`[saveDebugInfoToFiles] Failed to save debug info for step ${stepId}:`, error);\n }\n\n return artifacts;\n }\n\n /**\n * Update step result\n */\n private async updateStepResult(\n stepId: string,\n status?: string,\n message?: any,\n artifacts?: Record<string, string>,\n debugInfo?: ActionGenerationDebugInfo,\n ): Promise<void> {\n if (!this.context.stepTracking) return;\n\n const result = this.context.stepTracking.results[stepId];\n if (!result) return;\n\n if (status) {\n result.status = status as 'success' | 'failure' | 'skipped';\n // Clear current step ID when step completes (has a final status)\n this.context.stepTracking.currentStepId = undefined;\n // Notify listener of step change (for syncing with TestContext)\n if (this.context.stepTracking.onStepChange) {\n this.context.stepTracking.onStepChange(undefined);\n }\n }\n if (message !== undefined) {\n result.message = message;\n }\n if (artifacts) {\n result.artifacts.push(artifacts);\n }\n\n // Save debug info to local files (prompts, responses, screenshots)\n // Note: tokenUsages are NOT stored in step results - they're tracked via intelligentActionDetails\n if (debugInfo) {\n // Save debug artifacts to files\n const model = debugInfo.tokenUsages?.[0]?.model;\n const debugArtifacts = this.saveDebugInfoToFiles(stepId, debugInfo, model);\n if (Object.keys(debugArtifacts).length > 0) {\n result.artifacts.push(debugArtifacts);\n }\n }\n\n // Auto-calculate duration from startTime\n result.duration = Date.now() - result.startTime;\n\n // Call callback if provided\n if (this.context.stepTracking.onStepComplete) {\n this.context.stepTracking.onStepComplete(stepId, result);\n }\n }\n\n /**\n * Write execution results to output directory\n * Writes: test-results.json, token-usages.json, ai-actions.json\n */\n async writeExecutionResults(outputDir: string, options?: { tokenUsages?: boolean }): Promise<void> {\n try {\n // Ensure output directory exists\n await fs.promises.mkdir(outputDir, { recursive: true });\n\n // Write test-results.json (step tracking results)\n if (this.context.stepTracking?.results && Object.keys(this.context.stepTracking.results).length > 0) {\n const testResultsPath = path.join(outputDir, 'test-results.json');\n await fs.promises.writeFile(testResultsPath, JSON.stringify(this.context.stepTracking.results, null, 2));\n logger.debug(`Test results written to: ${testResultsPath}`);\n }\n\n // Write token-usages.json if enabled\n if (options?.tokenUsages && this.context.tokenUsages && this.context.tokenUsages.length > 0) {\n const tokenUsagesPath = path.join(outputDir, 'token-usages.json');\n await fs.promises.writeFile(tokenUsagesPath, JSON.stringify(this.context.tokenUsages, null, 2));\n logger.debug(`Token usages written to: ${tokenUsagesPath}`);\n }\n\n // Write ai-actions.json (per-step LLM call tracking)\n if (this.context.aiActionDetails && this.context.aiActionDetails.length > 0) {\n const aiActionsPath = path.join(outputDir, 'ai-actions.json');\n await fs.promises.writeFile(aiActionsPath, JSON.stringify(this.context.aiActionDetails, null, 2));\n logger.debug(`AI action details written to: ${aiActionsPath}`);\n }\n } catch (error) {\n logger.error('Failed to write execution results:', error);\n throw error;\n }\n }\n}\n","/**\n * Agent types - re-exports shared types and defines agent-specific types\n */\n\nimport type { ActionEntity } from '../actions/types';\nimport { ActionGenerationDebugInfo } from 'shiplight-types';\n\n// Re-export shared types from core\nexport type {\n AIActionDetail,\n ConsoleLogEntry,\n DOMSnapshot,\n DownloadStatus,\n PageState,\n ActionInfo,\n StateEntry,\n ActionEntry,\n RedirectEntry,\n StepTransition, // Legacy alias for ActionEntry\n RedirectTransition, // Legacy alias for RedirectEntry\n StateTransition,\n StateTransitionsOutput,\n StepExecutionResult,\n StepTrackingConfig,\n TestContextData,\n TokenUsage,\n WebAgentContext,\n} from '../core/types';\n\n// Re-export ActionEntity for convenience\nexport type { ActionEntity };\n\n/**\n * Event types from webagent streaming endpoint\n */\nexport enum AgentStepEventTypes {\n Started = 'started',\n Action = 'action',\n Completion = 'completion',\n Error = 'error',\n Aborted = 'aborted',\n Keepalive = 'keepalive',\n}\n\n/**\n * Event structure from webagent streaming\n */\nexport interface AgentStepEvent {\n type: AgentStepEventTypes;\n data?: any;\n}\n\n/**\n * Agent state information\n */\nexport interface AgentState {\n feedback: string;\n message: string;\n next_goal: string;\n testContext?: Record<string, any>;\n}\n\n/**\n * Agent action with state information\n */\nexport interface AgentAction {\n agent_state: AgentState;\n action_entity: ActionEntity;\n debugInfo?: ActionGenerationDebugInfo;\n}\n\n/**\n * Evaluation result from webagent\n */\nexport interface EvaluationResult {\n status: 'success' | 'error';\n conclusion: 'true' | 'false' | 'unknown';\n explanation: string;\n model?: string;\n id?: string;\n other_opinions?: Array<{\n conclusion: 'true' | 'false' | 'unknown';\n explanation: string;\n model?: string;\n }>;\n artifacts?: Record<string, string>;\n}\n\n/**\n * Action result from webagent\n */\nexport interface ActionResult {\n status: 'success' | 'failure';\n action?: ActionEntity;\n explanation: string;\n artifacts?: Record<string, any>;\n}\n\n/**\n * Final result from agent.step()\n */\nexport interface AgentStepResult {\n success: boolean;\n details?: string;\n actions?: ActionEntity[];\n debugInfo?: ActionGenerationDebugInfo;\n}\n","/**\n * AgentContext - Core context for AI agent operations\n * This is framework-agnostic and can be used outside of Playwright tests\n */\n\nimport { WebAgentContext } from './types';\nimport type { VariableStore } from 'shiplight-types';\n\n/**\n * Generate a timestamp string in datetime format (America/Los_Angeles timezone)\n * Format: \"YYYY-MM-DDTHH:MM:SS.mmm±HH:MM\"\n * Example: \"2025-11-03T19:55:37.128-08:00\"\n */\nfunction getCurrentTimeStamp(): string {\n const now = new Date();\n\n // Get time parts in America/Los_Angeles timezone\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/Los_Angeles',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n const parts = formatter.formatToParts(now);\n const year = parts.find(p => p.type === 'year')!.value;\n const month = parts.find(p => p.type === 'month')!.value;\n const day = parts.find(p => p.type === 'day')!.value;\n const hour = parts.find(p => p.type === 'hour')!.value;\n const minute = parts.find(p => p.type === 'minute')!.value;\n const second = parts.find(p => p.type === 'second')!.value;\n const milliseconds = String(now.getMilliseconds()).padStart(3, '0');\n\n // Calculate timezone offset for America/Los_Angeles\n const laTimeStr = `${year}-${month}-${day}T${hour}:${minute}:${second}.${milliseconds}`;\n const utcTimeStr = now.toISOString().slice(0, -1); // Remove 'Z'\n\n const laAsUtc = Date.parse(laTimeStr + 'Z');\n const actualUtc = Date.parse(utcTimeStr + 'Z');\n const offsetMinutes = Math.round((actualUtc - laAsUtc) / (1000 * 60));\n\n const offsetHours = String(Math.floor(Math.abs(offsetMinutes) / 60)).padStart(2, '0');\n const offsetMins = String(Math.abs(offsetMinutes) % 60).padStart(2, '0');\n const offsetSign = offsetMinutes <= 0 ? '+' : '-';\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}.${milliseconds}${offsetSign}${offsetHours}:${offsetMins}`;\n}\n\n/**\n * Options for creating an AgentContext\n */\nexport interface AgentContextOptions {\n model: string; // LLM model to use for AI operations\n variableStore: VariableStore; // Shared variable store (required - must be shared with TestContext)\n organizationId?: string;\n organizationSettings?: Record<string, any>;\n executionHistory?: Array<[string, string]>;\n testDataDir?: string;\n downloadDir?: string;\n useNativeGenerator?: boolean;\n selfHealingStrategy?: 'none' | 'single' | 'multi'; // Self-healing strategy ('none' to disable)\n autoDisableModal?: boolean; // Auto-dismiss modal dialogs (cookie consent, popups) before self-healing\n}\n\n/**\n * Create a new AgentContext\n *\n * WebAgentContext is internal - access variables via context.variableStore\n */\nexport function createAgentContext(options: AgentContextOptions): WebAgentContext {\n const variableStore = options.variableStore;\n\n // Initialize currentTime in the shared variable store if not already set\n if (!variableStore.has('currentTime')) {\n variableStore.set('currentTime', getCurrentTimeStamp());\n }\n\n return {\n // LLM model\n model: options.model,\n\n // Shared variable storage\n variableStore,\n\n // Configuration\n organizationId: options.organizationId,\n organizationSettings: options.organizationSettings,\n executionHistory: options.executionHistory || [],\n testDataDir: options.testDataDir,\n downloadDir: options.downloadDir,\n useNativeGenerator: options.useNativeGenerator,\n selfHealingStrategy: options.selfHealingStrategy,\n autoDisableModal: options.autoDisableModal,\n\n // Initialize execution state\n tokenUsages: [],\n aiActionDetails: [],\n };\n}\n","/**\n * Agent - Main entry point for browser automation with custom actions\n */\n\nimport type { Page } from 'playwright';\nimport { VariableStore, TwoFactorAuthType } from 'shiplight-types';\nimport {\n Agent as WebAgent,\n createAgentContext,\n toolRegistry,\n LoginType,\n type AgentStepResult,\n} from 'sdk-core';\nimport type {\n ICustomAction,\n ActionExecutionContext,\n CreateAgentOptions,\n LoginOptions,\n StepOptions,\n RunOptions,\n} from './types';\n\n/**\n * Browser automation agent with custom action support.\n *\n * @example\n * ```typescript\n * import { createAgent, configureSdk, z } from '@shiplightai/sdk';\n *\n * // Configure SDK with API key (call once at startup)\n * configureSdk({\n * env: { GOOGLE_API_KEY: process.env.GOOGLE_API_KEY },\n * });\n *\n * const agent = createAgent({\n * model: 'gemini-2.5-pro',\n * variables: { username: 'test@example.com' },\n * sensitiveKeys: ['password'],\n * });\n *\n * // Register a custom action\n * agent.registerAction({\n * name: 'extract_email_code',\n * description: 'Extract verification code from email inbox',\n * schema: z.object({\n * email_address: z.string(),\n * }),\n * async execute(args, ctx) {\n * const code = await getEmailCode(args.email_address);\n * ctx.variableStore.set('code', code);\n * return { success: true };\n * },\n * });\n *\n * // Use the agent\n * await agent.act(page, 'Fill email with $username');\n * await agent.act(page, 'Click submit');\n * await agent.act(page, 'Get the verification code from email');\n * await agent.act(page, 'Enter $code in the verification field');\n * await agent.assert(page, 'Dashboard is visible');\n * ```\n */\nexport class Agent {\n private webAgent: WebAgent;\n private variableStore: VariableStore;\n private customActions: Map<string, ICustomAction> = new Map();\n\n constructor(options: CreateAgentOptions) {\n // Check PWDEBUG environment variable - recommended for Playwright locator generation\n if (process.env.PWDEBUG !== 'console') {\n console.warn(\n '⚠️ Warning: PWDEBUG=console is recommended for @shiplightai/sdk.\\n' +\n ' Set it before running your script:\\n\\n' +\n ' export PWDEBUG=console\\n\\n' +\n ' Or prefix your command:\\n\\n' +\n ' PWDEBUG=console pnpm exec tsx your-script.ts\\n'\n );\n }\n\n // Create variable store\n this.variableStore = new VariableStore();\n const sensitiveSet = new Set(options.sensitiveKeys || []);\n\n // Set initial variables\n if (options.variables) {\n for (const [key, value] of Object.entries(options.variables)) {\n this.variableStore.set(key, value, sensitiveSet.has(key));\n }\n }\n\n // Create internal agent context\n const context = createAgentContext({\n model: options.model,\n variableStore: this.variableStore,\n testDataDir: options.testDataDir,\n downloadDir: options.downloadDir,\n selfHealingStrategy: options.selfHealingStrategy ?? 'single',\n });\n\n // Create internal web agent\n this.webAgent = new WebAgent(context);\n }\n\n /**\n * Register a custom action.\n *\n * Custom actions extend the agent's capabilities. The agent will automatically\n * call your action when the task requires it, based on the name and description.\n *\n * @param action - Custom action definition\n * @throws {Error} If action is missing required fields or name is already registered\n *\n * @example\n * ```typescript\n * agent.registerAction({\n * name: 'send_sms',\n * description: 'Send an SMS message to a phone number',\n * schema: z.object({\n * phone: z.string().describe('Phone number with country code'),\n * message: z.string().describe('Message content'),\n * }),\n * async execute(args, ctx) {\n * await twilioClient.send(args.phone, args.message);\n * return { success: true, message: 'SMS sent' };\n * },\n * });\n * ```\n */\n registerAction(action: ICustomAction): void {\n // Validate action\n if (!action.name) {\n throw new Error('CustomAction requires a name');\n }\n if (!action.description) {\n throw new Error('CustomAction requires a description');\n }\n if (!action.schema) {\n throw new Error('CustomAction requires a schema');\n }\n if (!action.execute) {\n throw new Error('CustomAction requires an execute function');\n }\n\n // Check for duplicate\n if (this.customActions.has(action.name)) {\n throw new Error(`CustomAction '${action.name}' is already registered`);\n }\n\n // Store action\n this.customActions.set(action.name, action);\n\n const variableStore = this.variableStore;\n\n // Register with the tool registry so the agent can call it\n toolRegistry.register({\n name: action.name,\n description: action.description,\n schema: action.schema,\n usesElementIndex: false,\n availability: {\n openai: true,\n mcp: true,\n },\n execute: async (args, toolContext) => {\n // Create the execution context for the custom action\n const ctx: ActionExecutionContext = {\n page: toolContext.page,\n variableStore: variableStore,\n };\n\n try {\n // Execute the custom action\n const result = await action.execute(args, ctx);\n\n // Return tool result with action entity for trajectory\n return {\n success: result.success,\n message: result.message,\n actionEntity: {\n action_description: result.message || `Executed ${action.name}`,\n action_data: {\n action_name: action.name,\n kwargs: args,\n },\n },\n };\n } catch (error: any) {\n return {\n success: false,\n error: error.message,\n actionEntity: {\n action_description: `Failed to execute ${action.name}`,\n action_data: {\n action_name: action.name,\n kwargs: args,\n },\n feedback: error.message,\n },\n };\n }\n },\n });\n }\n\n /**\n * Perform a single action on the page.\n *\n * Use this for discrete actions like clicking a button, filling a field,\n * or selecting an option. The agent executes exactly one action.\n *\n * @param page - Playwright page instance\n * @param instruction - Natural language instruction for a single action\n * @returns Result with success status and details\n *\n * @example\n * ```typescript\n * await agent.act(page, 'Click the login button');\n * await agent.act(page, 'Fill the email field with $username');\n * await agent.act(page, 'Select \"Express\" from the shipping dropdown');\n * ```\n */\n async act(page: Page, instruction: string): Promise<AgentStepResult> {\n return this.webAgent.performAction(page, instruction);\n }\n\n /**\n * Run a multi-step instruction until the goal is achieved.\n *\n * Use this for complex tasks that require multiple actions, like\n * \"Complete the checkout process\" or \"Fill out the registration form\".\n * The agent will take multiple steps until the goal is reached.\n *\n * @param page - Playwright page instance\n * @param instruction - Natural language instruction describing the goal\n * @param options - Optional configuration\n * @returns Result with success status and details\n *\n * @example\n * ```typescript\n * // Multi-step tasks\n * await agent.run(page, 'Complete the checkout process');\n * await agent.run(page, 'Fill out the entire registration form');\n *\n * // Limit steps to prevent runaway execution\n * await agent.run(page, 'Add 3 items to cart', { maxSteps: 10 });\n * ```\n */\n async run(page: Page, instruction: string, options?: RunOptions): Promise<AgentStepResult> {\n return this.webAgent.run(page, instruction, undefined, {\n maxSteps: options?.maxSteps,\n });\n }\n\n /**\n * Assert a condition on the page.\n *\n * The agent will analyze the page and determine if the assertion is true.\n * Throws an error if the assertion fails.\n *\n * @param page - Playwright page instance\n * @param statement - Assertion statement (e.g., \"Login button is visible\")\n * @returns true if assertion passes\n * @throws {Error} If assertion fails\n *\n * @example\n * ```typescript\n * await agent.assert(page, 'The dashboard shows welcome message');\n * await agent.assert(page, 'Shopping cart has 3 items');\n * await agent.assert(page, 'Error message is not displayed');\n * ```\n */\n async assert(page: Page, statement: string): Promise<boolean> {\n return this.webAgent.assert(page, statement);\n }\n\n /**\n * Evaluate a condition on the page (returns boolean, doesn't throw).\n *\n * Similar to assert() but returns false instead of throwing on failure.\n * Use this for conditional logic in tests.\n *\n * @param page - Playwright page instance\n * @param statement - Condition to evaluate (e.g., \"User is logged in\")\n * @returns true if condition is met, false otherwise\n *\n * @example\n * ```typescript\n * const isLoggedIn = await agent.evaluate(page, 'User is logged in');\n * if (!isLoggedIn) {\n * await agent.act(page, 'Click the login button');\n * }\n * ```\n */\n async evaluate(page: Page, statement: string): Promise<boolean> {\n return this.webAgent.evaluate(page, statement);\n }\n\n /**\n * Extract data from an element and store in a variable.\n *\n * @param page - Playwright page instance\n * @param elementDescription - Description of element to extract from\n * @param variableName - Name of variable to store the value\n *\n * @example\n * ```typescript\n * await agent.extract(page, 'the order total', 'orderTotal');\n * // Later use: await agent.run(page, 'Verify $orderTotal matches invoice');\n * ```\n */\n async extract(\n page: Page,\n elementDescription: string,\n variableName: string\n ): Promise<void> {\n return this.webAgent.extract(page, elementDescription, variableName);\n }\n\n /**\n * Perform automated login.\n *\n * The agent will navigate to the login URL, find login fields, enter credentials,\n * handle 2FA if configured, and verify successful login.\n *\n * @param page - Playwright page instance\n * @param options - Login URL, credentials, and options\n * @returns true if login was successful\n *\n * @example\n * ```typescript\n * await agent.login(page, {\n * url: 'https://example.com/login',\n * username: 'user@example.com',\n * password: 'secret123',\n * });\n * await agent.assert(page, 'Dashboard is visible');\n * ```\n *\n * @example\n * ```typescript\n * // With 2FA\n * await agent.login(page, {\n * url: 'https://example.com/login',\n * username: 'user@example.com',\n * password: 'secret123',\n * totpSecret: 'JBSWY3DPEHPK3PXP',\n * });\n * ```\n */\n async login(page: Page, options: LoginOptions): Promise<boolean> {\n // Build LoginConfig for the internal loginPage method\n // Set num_verification_exprs to 0 to skip validation expression generation\n // (not needed for simple login without auto-login caching)\n const loginConfig = {\n site_url: options.url,\n num_verification_exprs: 0,\n account: {\n type: LoginType.PASSWORD,\n username: options.username,\n password: options.password,\n ...(options.totpSecret && {\n two_factor_auth_config: {\n type: TwoFactorAuthType.TOTP,\n data: options.totpSecret,\n },\n }),\n },\n };\n\n const result = await this.webAgent.loginPage(page, loginConfig);\n return result.success;\n }\n\n /**\n * Get a variable value from the variable store.\n *\n * Use this to access values that were set via extract() or setVariable().\n *\n * @param name - Variable name\n * @returns Variable value, or undefined if not set\n *\n * @example\n * ```typescript\n * await agent.extract(page, 'the order total', 'orderTotal');\n * const total = agent.getVariable('orderTotal');\n * console.log('Order total:', total);\n * ```\n */\n getVariable(name: string): string | undefined {\n return this.variableStore.get(name);\n }\n\n /**\n * Set a variable value in the variable store.\n *\n * Variables can be referenced in instructions using $variableName syntax.\n *\n * @param name - Variable name\n * @param value - Variable value\n * @param sensitive - If true, value will be masked in logs (default: false)\n *\n * @example\n * ```typescript\n * agent.setVariable('couponCode', 'SAVE20');\n * await agent.run(page, 'Enter $couponCode in the promo field');\n *\n * // Sensitive values are masked in logs\n * agent.setVariable('apiKey', 'secret123', true);\n * ```\n */\n setVariable(name: string, value: string, sensitive: boolean = false): void {\n this.variableStore.set(name, value, sensitive);\n }\n\n /**\n * Wait until a condition becomes true.\n *\n * Polls the page state and evaluates whether the condition is met.\n * Useful for waiting on dynamic content, animations, or async operations.\n *\n * @param page - Playwright page instance\n * @param condition - Natural language condition to wait for\n * @param timeoutSeconds - Maximum wait time in seconds (default: 60)\n * @returns true if condition was met, false if timeout\n *\n * @example\n * ```typescript\n * // Wait for loading to complete\n * await agent.waitUntil(page, 'Loading spinner is no longer visible');\n *\n * // Wait for data to appear\n * const appeared = await agent.waitUntil(page, 'Table shows at least 5 rows', 30);\n * if (!appeared) {\n * throw new Error('Data did not load in time');\n * }\n *\n * // Wait for modal to close\n * await agent.waitUntil(page, 'Confirmation modal is closed');\n * ```\n */\n async waitUntil(\n page: Page,\n condition: string,\n timeoutSeconds: number = 60\n ): Promise<boolean> {\n const stepId = `waitUntil_${Date.now()}`;\n return this.webAgent.waitUntilCondition(page, condition, timeoutSeconds, stepId);\n }\n\n /**\n * Execute Playwright code with self-healing.\n *\n * Wraps Playwright code with automatic recovery. If the code throws\n * an exception, the agent will analyze the page and attempt to accomplish\n * the goal described in `description`.\n *\n * The `description` parameter is crucial - it tells the agent what you're trying\n * to achieve, so it can find alternative ways to accomplish the goal when\n * the original code fails (e.g., due to changed selectors or page structure).\n *\n * Self-healing behavior is controlled by:\n * - Global `selfHealingStrategy` (set in createAgent options, default: 'single')\n * - Per-call `maxSteps` overrides global strategy: 0=none, 1=single, >1=multi\n *\n * @param page - Playwright page instance\n * @param action - Async function containing Playwright code to execute\n * @param description - Intent description - what the agent should accomplish if action fails\n * @param options - Optional configuration for this call\n * @returns Result with success status and action details\n *\n * @example\n * ```typescript\n * // Single action with self-healing\n * await agent.step(\n * page,\n * async () => await page.click('#submit-btn'),\n * 'Click the submit button'\n * );\n *\n * // Code block with multiple actions\n * await agent.step(\n * page,\n * async () => {\n * await page.fill('#email', 'user@example.com');\n * await page.fill('#password', 'secret');\n * await page.click('#login');\n * },\n * 'Fill login form and submit'\n * );\n *\n * // With maxSteps for multi-step recovery\n * await agent.step(\n * page,\n * async () => await page.click('.dynamic-button'),\n * 'Click the dynamic button that appears after loading',\n * { maxSteps: 5 }\n * );\n * ```\n */\n async step(\n page: Page,\n action: () => Promise<void>,\n description: string,\n options?: StepOptions\n ): Promise<AgentStepResult> {\n // Generate a unique step ID\n const stepId = `step_${Date.now()}`;\n\n try {\n // Use the internal step function which handles self-healing\n const result = await this.webAgent.step(\n page,\n action,\n description,\n stepId,\n undefined, // stmtUid - not needed for public API\n true, // canSelfHeal\n options?.maxSteps\n );\n\n // Normalize return value - internal step may return undefined on direct success\n // or AgentStepResult on self-healing success\n if (result && typeof result === 'object' && 'success' in result) {\n return result as AgentStepResult;\n }\n return { success: true };\n } catch (error: any) {\n // Return failure result instead of throwing\n return {\n success: false,\n details: error.message,\n };\n }\n }\n}\n\n/**\n * Create a browser automation agent.\n *\n * This is the main entry point for the SDK. Creates an agent that can\n * execute natural language instructions and supports custom actions.\n *\n * @param options - Agent configuration options\n * @returns Configured Agent instance\n *\n * @example\n * ```typescript\n * import { createAgent, configureSdk, z } from '@shiplightai/sdk';\n *\n * // Configure SDK with API key (call once at startup)\n * configureSdk({\n * env: { GOOGLE_API_KEY: process.env.GOOGLE_API_KEY },\n * });\n *\n * const agent = createAgent({\n * model: 'gemini-2.5-pro',\n * variables: {\n * username: 'test@example.com',\n * password: 'secret123',\n * },\n * sensitiveKeys: ['password'],\n * });\n *\n * // Register custom actions\n * agent.registerAction({\n * name: 'get_otp',\n * description: 'Get OTP code from authenticator',\n * schema: z.object({}),\n * async execute(args, ctx) {\n * const code = await generateOTP();\n * ctx.variableStore.set('otp', code);\n * return { success: true };\n * },\n * });\n *\n * // Run automation\n * await agent.act(page, 'Fill username with $username');\n * await agent.act(page, 'Fill password with $password');\n * await agent.act(page, 'Click login');\n * await agent.act(page, 'Enter the OTP code');\n * await agent.assert(page, 'Dashboard is visible');\n * ```\n */\nexport function createAgent(options: CreateAgentOptions): Agent {\n return new Agent(options);\n}\n","/**\n * @shiplightai/sdk - AI-powered browser automation with custom actions\n *\n * A clean, minimal SDK for building browser automation with pluggable custom actions.\n *\n * @example\n * ```typescript\n * import { createAgent, z } from '@shiplightai/sdk';\n *\n * const agent = createAgent({\n * model: 'gemini-2.5-pro',\n * variables: { username: 'test@example.com' },\n * sensitiveKeys: ['password'],\n * });\n *\n * // Register a custom action\n * agent.registerAction({\n * name: 'extract_email_code',\n * description: 'Extract verification code from email',\n * schema: z.object({\n * email: z.string().describe('Email address to check'),\n * }),\n * async execute(args, ctx) {\n * const code = await getEmailCode(args.email);\n * ctx.variableStore.set('code', code);\n * return { success: true };\n * },\n * });\n *\n * // Use the agent\n * await agent.run(page, 'Login and get verification code');\n * await agent.assert(page, 'Dashboard is visible');\n * ```\n *\n * @packageDocumentation\n */\n\n// Main entry points\nexport { createAgent, Agent } from './agent';\n\n// Public types - re-export from sdk-core for consistency\nexport type {\n ICustomAction,\n ActionExecutionContext,\n CustomActionResult,\n} from 'sdk-core';\n\n// Local types specific to sdk-public\nexport type { CreateAgentOptions, LoginOptions, StepOptions, RunOptions } from './types';\n\n// Re-export VariableStore for type annotations\nexport { VariableStore } from 'shiplight-types';\n\n// Re-export zod for schema definitions\nexport { z } from 'zod';\n\n// SDK configuration\nexport { configureSdk, getSdkConfig, LogLevel } from 'sdk-core';\nexport type { SdkConfig } from 'sdk-core';\n"],"mappings":"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AM,yBACN,YAAa,iBACb,UAAW,wIACX,OAAQ,CACN,MAAO,KACP,OAAQ,IACV,EACA,SAAU,CACR,MAAO,KACP,OAAQ,IACV,EACA,kBAAmB,EACnB,SAAU,GACV,SAAU,GACV,mBAAoB,WACpB,QAAS,QACX,EACA,2CAA4C,CAC1C,KAAM,2CACN,YAAa,iBACb,UAAW,wIACX,OAAQ,CACN,MAAO,KACP,OAAQ,GACV,EACA,SAAU,CACR,MAAO,KACP,OAAQ,GACV,EACA,kBAAmB,EACnB,SAAU,GACV,SAAU,GACV,mBAAoB,WACpB,QAAS,QACX,EACA,kBAAmB,CACjB,KAAM,kBACN,UAAW,uFACX,OAAQ,CACN,MAAO,KACP,OAAQ,IACV,EACA,SAAU,CACR,MAAO,KACP,OAAQ,GACV,EACA,kBAAmB,EACnB,SAAU,GACV,SAAU,GACV,mBAAoB,SACtB,CACF,EAMA,IAAIC,GAAoB,CACrB,QAA0B,CACzB,iBACA,mCACA,2BACA,6CACA,yBACA,2CACA,gBAMF,EAeC,OAAwB,CACvB,oBACA,gBACA,iBACA,YACA,oBACA,gBACA,iBACA,YACA,oBACA,gBACA,YACA,iBACA,oBACA,gBACA,YACA,iBACA,oBACA,gBACA,YACA,YACA,WACA,sBACA,YACA,gBACA,WACA,gBACA,WACA,gBACA,WACA,aACA,aACA,aACA,YACA,YACA,gBACA,iBACA,eACA,UACA,UACA,gBACA,UACA,UACA,aACA,UACA,WACA,UACA,WACA,UACA,UACA,UACA,iBACA,sBACA,sBACA,kBACA,WACA,gBACF,CACF,EACIC,EAAuB,CAACC,EAAUC,EAAgB,KAAU,CAC9D,IAAMC,EAAsB,CAAC,UAAyB,EACtD,OAAID,GACFC,EAAoB,KAAK,QAAqB,EAEzCJ,GAAkBE,CAAQ,EAAE,IAAKG,GAAeC,GAAmBD,CAAU,CAAC,EAAE,OAAQE,GAAWA,EAAO,oBAAsBH,EAAoB,SAASG,EAAO,kBAAkB,CAAC,CAChM,EAOA,IAAIC,GAAuB,CACzB,QAAS,CACP,MAAO,UACP,KAAM,UACN,QAASC,EAAqB,SAAuB,CACvD,EAMA,O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","names":["ADDRESS_BAR_HEIGHT","VIEWPORT_HEIGHT","ADDRESS_BAR_HEIGHT","PLAYWRIGHT_DEVICES","DEVICE_CATEGORIES","getDevicesByCategory","category","includeWebkit","allowedBrowserTypes","deviceName","PLAYWRIGHT_DEVICES","device","UI_DEVICE_CATEGORIES","getDevicesByCategory","UI_DEVICE_CATEGORIES_ELECTRON","TwoFactorAuthType","TwoFactorAuthType2","z","ConditionTypeSchema","ConditionSchema","StatementTypeSchema","BaseStatementSchema","ActionEntitySchema","DraftSchema","ActionSchema","StatementSchema","TestFlowSchema","import_yaml","uuidv4","MAX_YAML_SIZE","VariableStore","_VariableStore","__publicField","key","value","sensitive","other","json","store","sensitiveSet","z","PerformAccurateOperationToolSchema","registerPerformAccurateOperationTool","registry","args","ctx","instruction","taskContext","generatedAction","generateAction","actionEntity","executionResult","executeAction","cachedActionHandler","getActionHandler","ActionHandlerClass","registerMouseTools","registry","handler","clickAction","hoverAction","rightClickAction","doubleClickAction","registerClickTool","registerHoverTool","registerRightClickTool","registerDoubleClickTool","registerNavigationTools","goToUrlAction","goBackAction","reloadPageAction","registerGoToUrlTool","registerGoBackTool","registerReloadPageTool","registerInputTools","clearInputAction","inputTextAction","pressAction","registerClearInputTool","registerInputTextTool","registerPressTool","registerScrollTools","scrollOnElementAction","scrollToTextAction","scrollAction","registerScrollOnElementTool","registerScrollToTextTool","registerScrollTool","registerTabTools","closeTabAction","switchTabAction","registerCloseTabTool","registerSwitchTabTool","registerFileTools","uploadFileAction","waitForDownloadCompleteAction","registerUploadFileTool","registerWaitForDownloadCompleteTool","registerFormTools","getDropdownOptionsAction","selectDropdownAction","setDateForNativeDatePickerAction","registerGetDropdownOptionsTool","registerSelectDropdownOptionTool","registerSetDateForNativeDatePickerTool","registerUtilityTools","waitAction","saveVariableAction","doneAction","registerWaitTool","registerSaveVariableTool","registerDoneTool","registerPerformAccurateOperationTool","registerAuthTools","generate2faCodeAction","registerGenerate2faCodeTool","registerAITools","verifyAction","aiExtractAction","aiWaitUntilAction","registerAiAssertTool","registerAiExtractTool","registerAiWaitUntilTool","toolRegistrationPromises","registerMouseTools","toolRegistry","registerNavigationTools","registerInputTools","registerScrollTools","registerTabTools","registerFileTools","registerFormTools","registerUtilityTools","registerAITools","registerAuthTools","allToolsRegistered","chromium","firefox","webkit","DevicePlatform","TabManager","context","page","existingPages","index","closedPage","pages","firstNonClosedPage","pageChanged","targetPage","waitUntilStable","page","timeoutMs","minWaitTimeMs","waitForPageAndFramesLoad","waitForDownloadComplete","context","timeoutSeconds","downloadCheckInterval","startTime","errorMsg","logger_default","waitUntilCondition","condition","evaluateCondition","stepId","maxWaitSeconds","checkInterval","endTime","lastCheckTime","error","waitTime","fs","path","AgentServices","context","downloader","fileNames","TabManager","pageIndex","page","currentPage","logger_default","pages","p","newPage","input","replaceVariables","fileName","testDataDir","filePaths","filesDir","missingFiles","filePath","localPath","exists","timeoutSeconds","waitForDownloadComplete","name","value","cleanName","note","secretKey","authenticator","code","error","maxWaitTimeMs","waitForPageAndFramesLoad","envOverride","retriever","statement","threshold","topK","usageScenario","knowledges","fs","path","MAX_SELF_HEALING_STEPS","MAX_MODAL_DISMISSAL_STEPS","DEFAULT_MULTI_STEP_MAX","maskSensitiveVariables","variables","sensitiveKeys","result","key","value","WebAgent","context","AgentServices","ActionHandlerClass","actionName","page","entity","action","currentTaskDescription","modalDismissed","error","logger_default","download","filename","downloadDir","downloadPath","debugInfoOrTokenUsages","tokenUsages","stepId","actionType","debugInfo","count","statement","explanation","entry","detail","executionHistory","evaluateStatement","instruction","executeStep","actionDescription","usePureVision","options","strategy","completesInstruction","onEvent","event","stepSubId","runTask","debugInfoForTracking","executionNote","generateActionStep","success","task","shouldSaveArtifacts","agentLogger","summary","fn","description","stmtUid","canSelfHeal","maxSteps","startTime","urlBefore","domBefore","variablesBefore","screenshotBeforePath","urlAfter","domAfter","variablesAfter","durationMs","screenshotAfterPath","now","ableToSelfHeal","modalDismissedNote","dismissResult","retryResult","retryError","effectiveMaxSteps","lastAction","message","a","healError","elementDescription","variableName","domService","DomService","interactiveClassNames","domState","log","timeoutMs","minWaitTimeMs","waitUntilStable","condition","timeoutSeconds","waitUntilCondition","p","c","s","filePaths","uploadFile","sid","config","cache","LoginType","validateLogin","storageState","signInPrompt","oauth2Account","originalRetriever","loginKnowledgeRetrieverSet","threshold","topK","usageScenario","loginResult","validationExprs","generatedLocators","generateAndValidateLoginLocators","prompt","newActions","finalCachedActions","actions","actionHandler","i","lastEntry","feedback","stepDir","screenshotPath","model","artifacts","stepArtifactsDir","systemPromptPath","userPromptPath","strippedMessages","msg","part","messagesPath","messagesData","modelPrefix","responsePath","reasoningPath","status","debugArtifacts","outputDir","testResultsPath","tokenUsagesPath","aiActionsPath","AgentStepEventTypes","getCurrentTimeStamp","now","parts","year","p","month","day","hour","minute","second","milliseconds","laTimeStr","utcTimeStr","laAsUtc","actualUtc","offsetMinutes","offsetHours","offsetMins","offsetSign","createAgentContext","options","variableStore","Agent","options","VariableStore","sensitiveSet","key","value","context","h","K","action","variableStore","u","args","toolContext","ctx","result","error","page","instruction","statement","elementDescription","variableName","loginConfig","L","TwoFactorAuthType","name","sensitive","condition","timeoutSeconds","stepId","description","createAgent","z"]}