misoai-web 1.5.9 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/es/agent.js +158 -54
  2. package/dist/es/agent.js.map +1 -1
  3. package/dist/es/bridge-mode-browser.js +3 -3
  4. package/dist/es/bridge-mode-browser.js.map +1 -1
  5. package/dist/es/bridge-mode.js +160 -56
  6. package/dist/es/bridge-mode.js.map +1 -1
  7. package/dist/es/chrome-extension.js +159 -55
  8. package/dist/es/chrome-extension.js.map +1 -1
  9. package/dist/es/index.js +159 -55
  10. package/dist/es/index.js.map +1 -1
  11. package/dist/es/midscene-playground.js +161 -57
  12. package/dist/es/midscene-playground.js.map +1 -1
  13. package/dist/es/midscene-server.js +4 -4
  14. package/dist/es/midscene-server.js.map +1 -1
  15. package/dist/es/playground.js +158 -54
  16. package/dist/es/playground.js.map +1 -1
  17. package/dist/es/playwright-report.js +1 -1
  18. package/dist/es/playwright-report.js.map +1 -1
  19. package/dist/es/playwright.js +159 -55
  20. package/dist/es/playwright.js.map +1 -1
  21. package/dist/es/puppeteer-agent-launcher.js +158 -54
  22. package/dist/es/puppeteer-agent-launcher.js.map +1 -1
  23. package/dist/es/puppeteer.js +158 -54
  24. package/dist/es/puppeteer.js.map +1 -1
  25. package/dist/es/utils.js +1 -1
  26. package/dist/es/utils.js.map +1 -1
  27. package/dist/es/yaml.js +1 -1
  28. package/dist/es/yaml.js.map +1 -1
  29. package/dist/lib/agent.js +158 -54
  30. package/dist/lib/agent.js.map +1 -1
  31. package/dist/lib/bridge-mode-browser.js +3 -3
  32. package/dist/lib/bridge-mode-browser.js.map +1 -1
  33. package/dist/lib/bridge-mode.js +160 -56
  34. package/dist/lib/bridge-mode.js.map +1 -1
  35. package/dist/lib/chrome-extension.js +159 -55
  36. package/dist/lib/chrome-extension.js.map +1 -1
  37. package/dist/lib/index.js +159 -55
  38. package/dist/lib/index.js.map +1 -1
  39. package/dist/lib/midscene-playground.js +161 -57
  40. package/dist/lib/midscene-playground.js.map +1 -1
  41. package/dist/lib/midscene-server.js +4 -4
  42. package/dist/lib/midscene-server.js.map +1 -1
  43. package/dist/lib/playground.js +158 -54
  44. package/dist/lib/playground.js.map +1 -1
  45. package/dist/lib/playwright-report.js +1 -1
  46. package/dist/lib/playwright-report.js.map +1 -1
  47. package/dist/lib/playwright.js +159 -55
  48. package/dist/lib/playwright.js.map +1 -1
  49. package/dist/lib/puppeteer-agent-launcher.js +158 -54
  50. package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
  51. package/dist/lib/puppeteer.js +158 -54
  52. package/dist/lib/puppeteer.js.map +1 -1
  53. package/dist/lib/utils.js +1 -1
  54. package/dist/lib/utils.js.map +1 -1
  55. package/dist/lib/yaml.js +1 -1
  56. package/dist/lib/yaml.js.map +1 -1
  57. package/dist/types/agent.d.ts +8 -1
  58. package/dist/types/index.d.ts +1 -1
  59. package/dist/types/playwright.d.ts +1 -1
  60. package/package.json +18 -54
@@ -1,4 +1,5 @@
1
1
  // src/common/utils.ts
2
+ import dayjs from "dayjs";
2
3
  import { elementByPositionWithElementInfo } from "misoai-core/ai-model";
3
4
  import { uploadTestInfoToServer } from "misoai-core/utils";
4
5
  import { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from "misoai-shared/env";
@@ -10,7 +11,6 @@ import {
10
11
  } from "misoai-shared/extractor";
11
12
  import { resizeImgBase64 } from "misoai-shared/img";
12
13
  import { assert, logMsg, uuid } from "misoai-shared/utils";
13
- import dayjs from "dayjs";
14
14
  function reportFileName(tag = "web") {
15
15
  const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);
16
16
  const dateTimeInFileName = dayjs().format("YYYY-MM-DD_HH-mm-ss");
@@ -1 +1 @@
1
- {"version":3,"mappings":";AAQA,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,0BAA0B,mBAAmB;AAEtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAEhC,SAAS,QAAQ,QAAQ,YAAY;AACrC,OAAO,WAAW;AAsEX,SAAS,eAAe,MAAM,OAAO;AAC1C,QAAM,gBAAgB,YAAY,wBAAwB;AAC1D,QAAM,qBAAqB,MAAM,EAAE,OAAO,qBAAqB;AAE/D,QAAM,WAAW,KAAK,EAAE,UAAU,GAAG,CAAC;AACtC,SAAO,GAAG,iBAAiB,GAAG,IAAI,kBAAkB,IAAI,QAAQ;AAClE;AAEO,SAAS,eAAe,UAAkB;AAC/C,SAAO,mCAAmC,QAAQ,EAAE;AACtD;AA0DO,SAAS,gCAAgC,KAAa;AAE3D,SAAO,IAAI,QAAQ,eAAe,GAAG;AACvC;;;AC5JA,SAAS,uBAAuB;AAUhC,SAAS,UAAU,SAAgB;AACjC,MAAI,QAAQ,IAAI,UAAU,QAAQ;AAChC,YAAQ,IAAI,wBAAwB,GAAG,OAAO;AAAA,EAChD;AACF;AAEA,IAAM,eAAgD,CAAC;AACvD,IAAI;AACJ,IAAM,sBAA2C,oBAAI,IAAI;AAEzD,SAAS,kBAAkB,WAA2B;AACpD,MAAI,CAAC,oBAAoB,IAAI,SAAS,GAAG;AAGvC,UAAM,UAAU,cAAc,gCAAgC,SAAS,CAAC;AACxE,UAAM,oBAAoB,eAAe,OAAO;AAChD,wBAAoB,IAAI,WAAW,iBAAiB;AAAA,EACtD;AACA,SAAO,oBAAoB,IAAI,SAAS;AAC1C;AAEA,SAAS,aAAa,MAA6B,QAAiB;AAClE,MAAI,SAAS,YAAY;AAEvB,UAAM,WAAW,aAAa;AAAA,MAC5B,CAAC,SAAS,KAAK,YAAY,uBAAuB;AAAA,IACpD;AAEA,QAAI,UAAU;AAEZ,YAAM,iBAAiB;AAAA,QACrB,SAAS,YAAY;AAAA,MACvB;AAEA,YAAM,aAAa,gBAAgB,gBAAgB,CAAC,QAAQ,CAAC;AAC7D,oBAAc,eAAe,UAAU;AAAA,IACzC;AAAA,EACF,WAAW,SAAS,UAAU;AAE5B,QAAI,CAAC,gBAAgB;AACnB,uBAAiB,eAAe,mBAAmB;AAAA,IACrD;AAEA,UAAM,aAAa,gBAAgB,gBAAgB,YAAY;AAC/D,kBAAc,eAAe,UAAU;AAAA,EACzC,OAAO;AACL,UAAM,IAAI;AAAA,MACR,+CAA+C,IAAI;AAAA,IACrD;AAAA,EACF;AACF;AAEA,SAAS,QAAQ,cAAsB;AACrC,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,YAAY,iBAAiB,YAAY;AAC5D,UAAM,IAAI;AAAA,MACR,+CAA+C,YAAY;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,mBAAN,MAA2C;AAAA,EAGzC,MAAM,QAAQ,QAAoB,OAAc;AAC9C,UAAM,eAAe,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG;AAEhD,SAAK,OAAO,QAAQ,YAAY;AAAA,EAIlC;AAAA,EAEA,YAAY,MAAgB,SAAqB;AAAA,EAEjD;AAAA,EAEA,UAAU,MAAgB,QAAoB;AAC5C,UAAM,iBAAiB,KAAK,YAAY,KAAK,CAAC,eAAe;AAC3D,aAAO,WAAW,SAAS;AAAA,IAC7B,CAAC;AACD,QAAI,CAAC,gBAAgB;AAAa;AAClC,UAAM,QAAQ,OAAO,QAAQ,WAAW,OAAO,KAAK,MAAM;AAC1D,UAAM,SAAS,GAAG,KAAK,EAAE,GAAG,KAAK;AACjC,UAAM,WAAqC;AAAA,MACzC,YAAY,eAAe;AAAA,MAC3B,YAAY;AAAA,QACV,oBAAoB;AAAA,QACpB,uBAAuB,GAAG,KAAK,KAAK,GAAG,KAAK;AAAA,QAC5C,wBAAwB,OAAO;AAAA,QAC/B,0BAA0B,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,iBAAa,KAAK,QAAQ;AAE1B,iBAAa,KAAK,MAAO,MAAM;AAE/B,SAAK,cAAc,KAAK,YAAY;AAAA,MAClC,CAAC,eAAe,WAAW,SAAS;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAM,QAAoB;AACxB,iBAAa,KAAK,IAAK;AAEvB,WAAO,qBAAqB,OAAO,MAAM,EAAE;AAAA,EAC7C;AACF;AAEA,IAAO,mBAAQ","names":[],"ignoreList":[],"sources":["../../src/common/utils.ts","../../src/playwright/reporter/index.ts"],"sourcesContent":["import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId, isVisible } =\n elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n isVisible,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n // Only replace characters that are illegal in filenames, but preserve path separators\n return str.replace(/[:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n","import {\n printReportMsg,\n replaceIllegalPathCharsAndSpace,\n reportFileName,\n} from '@/common/utils';\nimport type { ReportDumpWithAttributes } from 'misoai-core';\nimport { writeDumpReport } from 'misoai-core/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\nfunction logger(...message: any[]) {\n if (process.env.DEBUG === 'true') {\n console.log('Midscene e2e report:', ...message);\n }\n}\n\nconst testDataList: Array<ReportDumpWithAttributes> = [];\nlet mergedFilename: string;\nconst testTitleToFilename: Map<string, string> = new Map();\n\nfunction getStableFilename(testTitle: string): string {\n if (!testTitleToFilename.has(testTitle)) {\n // use reportFileName to generate the base filename\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = reportFileName(baseTag);\n testTitleToFilename.set(testTitle, generatedFilename);\n }\n return testTitleToFilename.get(testTitle)!;\n}\n\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\n if (mode === 'separate') {\n // in separate mode, find the data for the corresponding testID and generate a separate report\n const testData = testDataList.find(\n (data) => data.attributes?.playwright_test_id === testId,\n );\n\n if (testData) {\n // use the stable filename\n const stableFilename = getStableFilename(\n testData.attributes?.playwright_test_title,\n );\n\n const reportPath = writeDumpReport(stableFilename, [testData]);\n reportPath && printReportMsg(reportPath);\n }\n } else if (mode === 'merged') {\n // in merged mode, write all test data into one file\n if (!mergedFilename) {\n mergedFilename = reportFileName('playwright-merged');\n }\n\n const reportPath = writeDumpReport(mergedFilename, testDataList);\n reportPath && printReportMsg(reportPath);\n } else {\n throw new Error(\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\n );\n }\n}\n\nfunction getMode(reporterType: string) {\n if (!reporterType) {\n return 'merged';\n }\n\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n\n return reporterType;\n}\n\nclass MidsceneReporter implements Reporter {\n mode?: 'merged' | 'separate';\n\n async onBegin(config: FullConfig, suite: Suite) {\n const reporterType = config.reporter?.[1]?.[1]?.type;\n\n this.mode = getMode(reporterType);\n\n // const suites = suite.allTests();\n // logger(`Starting the run with ${suites.length} tests`);\n }\n\n onTestBegin(test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString: dumpAnnotation.description,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n testDataList.push(testData);\n\n updateReport(this.mode!, testId);\n\n test.annotations = test.annotations.filter(\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\n );\n }\n\n onEnd(result: FullResult) {\n updateReport(this.mode!);\n\n logger(`Finished the run: ${result.status}`);\n }\n}\n\nexport default MidsceneReporter;\n"]}
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/common/utils.ts","../../src/playwright/reporter/index.ts"],"sourcesContent":["import type { StaticPage } from '@/playground';\nimport dayjs from 'dayjs';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId, isVisible } =\n elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n isVisible,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n // Only replace characters that are illegal in filenames, but preserve path separators\n return str.replace(/[:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n","import {\n printReportMsg,\n replaceIllegalPathCharsAndSpace,\n reportFileName,\n} from '@/common/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\nimport type { ReportDumpWithAttributes } from 'misoai-core';\nimport { writeDumpReport } from 'misoai-core/utils';\n\nfunction logger(...message: any[]) {\n if (process.env.DEBUG === 'true') {\n console.log('Midscene e2e report:', ...message);\n }\n}\n\nconst testDataList: Array<ReportDumpWithAttributes> = [];\nlet mergedFilename: string;\nconst testTitleToFilename: Map<string, string> = new Map();\n\nfunction getStableFilename(testTitle: string): string {\n if (!testTitleToFilename.has(testTitle)) {\n // use reportFileName to generate the base filename\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = reportFileName(baseTag);\n testTitleToFilename.set(testTitle, generatedFilename);\n }\n return testTitleToFilename.get(testTitle)!;\n}\n\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\n if (mode === 'separate') {\n // in separate mode, find the data for the corresponding testID and generate a separate report\n const testData = testDataList.find(\n (data) => data.attributes?.playwright_test_id === testId,\n );\n\n if (testData) {\n // use the stable filename\n const stableFilename = getStableFilename(\n testData.attributes?.playwright_test_title,\n );\n\n const reportPath = writeDumpReport(stableFilename, [testData]);\n reportPath && printReportMsg(reportPath);\n }\n } else if (mode === 'merged') {\n // in merged mode, write all test data into one file\n if (!mergedFilename) {\n mergedFilename = reportFileName('playwright-merged');\n }\n\n const reportPath = writeDumpReport(mergedFilename, testDataList);\n reportPath && printReportMsg(reportPath);\n } else {\n throw new Error(\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\n );\n }\n}\n\nfunction getMode(reporterType: string) {\n if (!reporterType) {\n return 'merged';\n }\n\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n\n return reporterType;\n}\n\nclass MidsceneReporter implements Reporter {\n mode?: 'merged' | 'separate';\n\n async onBegin(config: FullConfig, suite: Suite) {\n const reporterType = config.reporter?.[1]?.[1]?.type;\n\n this.mode = getMode(reporterType);\n\n // const suites = suite.allTests();\n // logger(`Starting the run with ${suites.length} tests`);\n }\n\n onTestBegin(test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString: dumpAnnotation.description,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n testDataList.push(testData);\n\n updateReport(this.mode!, testId);\n\n test.annotations = test.annotations.filter(\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\n );\n }\n\n onEnd(result: FullResult) {\n updateReport(this.mode!);\n\n logger(`Finished the run: ${result.status}`);\n }\n}\n\nexport default MidsceneReporter;\n"]}
@@ -306,8 +306,8 @@ var ScriptPlayer = class {
306
306
  import yaml from "js-yaml";
307
307
 
308
308
  // src/yaml/utils.ts
309
- import { assert as assert2 } from "misoai-shared/utils";
310
309
  import yaml2 from "js-yaml";
310
+ import { assert as assert2 } from "misoai-shared/utils";
311
311
  function interpolateEnvVars(content) {
312
312
  return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
313
313
  const value = process.env[envVar.trim()];
@@ -468,6 +468,7 @@ function paramStr(task) {
468
468
  }
469
469
 
470
470
  // src/common/utils.ts
471
+ import dayjs from "dayjs";
471
472
  import { elementByPositionWithElementInfo } from "misoai-core/ai-model";
472
473
  import { uploadTestInfoToServer } from "misoai-core/utils";
473
474
  import { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from "misoai-shared/env";
@@ -479,7 +480,6 @@ import {
479
480
  } from "misoai-shared/extractor";
480
481
  import { resizeImgBase64 } from "misoai-shared/img";
481
482
  import { assert as assert3, logMsg, uuid } from "misoai-shared/utils";
482
- import dayjs from "dayjs";
483
483
 
484
484
  // src/web-element.ts
485
485
  var WebElementInfo = class {
@@ -648,8 +648,12 @@ var WorkflowMemory = class {
648
648
  const workflow = this.workflows.get(workflowId) || this.createEmptyWorkflowData(workflowId);
649
649
  workflow.memory = [...memory];
650
650
  workflow.metadata.totalSteps = workflow.steps.length;
651
- workflow.metadata.completedSteps = workflow.steps.filter((s) => s.status === "completed").length;
652
- workflow.metadata.failedSteps = workflow.steps.filter((s) => s.status === "failed").length;
651
+ workflow.metadata.completedSteps = workflow.steps.filter(
652
+ (s) => s.status === "completed"
653
+ ).length;
654
+ workflow.metadata.failedSteps = workflow.steps.filter(
655
+ (s) => s.status === "failed"
656
+ ).length;
653
657
  this.workflows.set(workflowId, workflow);
654
658
  this.enforceRetentionPolicy();
655
659
  }
@@ -660,7 +664,9 @@ var WorkflowMemory = class {
660
664
  const workflow = this.workflows.get(workflowId) || this.createEmptyWorkflowData(workflowId);
661
665
  workflow.context = { ...workflow.context, ...context };
662
666
  if (context.currentStep) {
663
- const existingStep = workflow.steps.find((s) => s.stepName === context.currentStep);
667
+ const existingStep = workflow.steps.find(
668
+ (s) => s.stepName === context.currentStep
669
+ );
664
670
  if (!existingStep) {
665
671
  workflow.steps.push({
666
672
  stepId: `step_${workflow.steps.length + 1}`,
@@ -705,7 +711,9 @@ var WorkflowMemory = class {
705
711
  enforceRetentionPolicy() {
706
712
  const maxWorkflows = 10;
707
713
  if (this.workflows.size > maxWorkflows) {
708
- const sortedWorkflows = Array.from(this.workflows.entries()).sort(([, a], [, b]) => (b.metadata.endTime || b.metadata.startTime) - (a.metadata.endTime || a.metadata.startTime));
714
+ const sortedWorkflows = Array.from(this.workflows.entries()).sort(
715
+ ([, a], [, b]) => (b.metadata.endTime || b.metadata.startTime) - (a.metadata.endTime || a.metadata.startTime)
716
+ );
709
717
  const toDelete = sortedWorkflows.slice(maxWorkflows);
710
718
  toDelete.forEach(([workflowId]) => this.workflows.delete(workflowId));
711
719
  }
@@ -1444,7 +1452,9 @@ var PageTaskExecutor = class {
1444
1452
  */
1445
1453
  getPersistentExecutor() {
1446
1454
  if (!this.persistentExecutor || this.persistentExecutor.status === "error") {
1447
- const previousMemory = this.workflowMemory.getWorkflowMemory(this.sessionContext.workflowId);
1455
+ const previousMemory = this.workflowMemory.getWorkflowMemory(
1456
+ this.sessionContext.workflowId
1457
+ );
1448
1458
  this.persistentExecutor = new Executor("Persistent Task Executor", {
1449
1459
  onTaskStart: this.onTaskStartCallback,
1450
1460
  initialMemory: previousMemory
@@ -1473,7 +1483,9 @@ var PageTaskExecutor = class {
1473
1483
  if (this.persistentExecutor) {
1474
1484
  this.persistentExecutor.clearMemory();
1475
1485
  }
1476
- this.workflowMemory.clearWorkflow(this.sessionContext.workflowId || "default");
1486
+ this.workflowMemory.clearWorkflow(
1487
+ this.sessionContext.workflowId || "default"
1488
+ );
1477
1489
  }
1478
1490
  /**
1479
1491
  * Mevcut hafızayı döndürür
@@ -1485,7 +1497,9 @@ var PageTaskExecutor = class {
1485
1497
  * İş akışı hafızasını döndürür
1486
1498
  */
1487
1499
  getWorkflowMemory() {
1488
- return this.workflowMemory.getWorkflowData(this.sessionContext.workflowId || "default");
1500
+ return this.workflowMemory.getWorkflowData(
1501
+ this.sessionContext.workflowId || "default"
1502
+ );
1489
1503
  }
1490
1504
  /**
1491
1505
  * Hafıza istatistiklerini döndürür
@@ -1493,7 +1507,13 @@ var PageTaskExecutor = class {
1493
1507
  getMemoryStats() {
1494
1508
  return this.persistentExecutor?.getMemoryStats() || {
1495
1509
  totalItems: 0,
1496
- analytics: { totalTasks: 0, memoryHits: 0, memoryMisses: 0, averageMemorySize: 0, memoryEffectiveness: 0 },
1510
+ analytics: {
1511
+ totalTasks: 0,
1512
+ memoryHits: 0,
1513
+ memoryMisses: 0,
1514
+ averageMemorySize: 0,
1515
+ memoryEffectiveness: 0
1516
+ },
1497
1517
  config: this.memoryConfig
1498
1518
  };
1499
1519
  }
@@ -1529,11 +1549,14 @@ var PageTaskExecutor = class {
1529
1549
  let taskExecutor;
1530
1550
  if (useMemory) {
1531
1551
  taskExecutor = this.getPersistentExecutor();
1532
- this.workflowMemory.updateWorkflowContext({
1533
- currentStep: title,
1534
- pageInfo: this.sessionContext.pageInfo,
1535
- timestamp: Date.now()
1536
- }, this.sessionContext.workflowId || "default");
1552
+ this.workflowMemory.updateWorkflowContext(
1553
+ {
1554
+ currentStep: title,
1555
+ pageInfo: this.sessionContext.pageInfo,
1556
+ timestamp: Date.now()
1557
+ },
1558
+ this.sessionContext.workflowId || "default"
1559
+ );
1537
1560
  } else {
1538
1561
  taskExecutor = new Executor(title, {
1539
1562
  onTaskStart: this.onTaskStartCallback
@@ -1831,9 +1854,15 @@ var PageTaskExecutor = class {
1831
1854
  }
1832
1855
  async waitFor(assertion, opt) {
1833
1856
  const description = `waitFor: ${assertion}`;
1834
- const taskExecutor = new Executor(taskTitleStr("WaitFor", description), {
1835
- onTaskStart: this.onTaskStartCallback
1836
- });
1857
+ const useMemory = true;
1858
+ let taskExecutor;
1859
+ if (useMemory) {
1860
+ taskExecutor = this.getPersistentExecutor();
1861
+ } else {
1862
+ taskExecutor = new Executor(taskTitleStr("WaitFor", description), {
1863
+ onTaskStart: this.onTaskStartCallback
1864
+ });
1865
+ }
1837
1866
  const { timeoutMs, checkIntervalMs } = opt;
1838
1867
  assert4(assertion, "No assertion for waitFor");
1839
1868
  assert4(timeoutMs, "No timeoutMs for waitFor");
@@ -1888,6 +1917,25 @@ var PageTaskExecutor = class {
1888
1917
  `waitFor timeout: ${errorThought}`
1889
1918
  );
1890
1919
  }
1920
+ /**
1921
+ * Hafızaya yeni bir öğe ekler
1922
+ */
1923
+ addToMemory(memoryItem) {
1924
+ if (!this.persistentExecutor || this.persistentExecutor.status === "error") {
1925
+ const previousMemory = this.workflowMemory.getWorkflowMemory(
1926
+ this.sessionContext.workflowId
1927
+ );
1928
+ this.persistentExecutor = new Executor("Persistent Task Executor", {
1929
+ onTaskStart: this.onTaskStartCallback,
1930
+ initialMemory: previousMemory
1931
+ });
1932
+ }
1933
+ this.persistentExecutor.memoryStore?.add(memoryItem);
1934
+ this.persistentExecutor.memoryAnalytics?.recordMemoryOperation(
1935
+ "add",
1936
+ memoryItem
1937
+ );
1938
+ }
1891
1939
  };
1892
1940
 
1893
1941
  // src/common/plan-builder.ts
@@ -1975,14 +2023,14 @@ function buildPlans(type, locateParam, param) {
1975
2023
  import assert6 from "assert";
1976
2024
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
1977
2025
  import { dirname as dirname2, join as join2 } from "path";
2026
+ import yaml3 from "js-yaml";
1978
2027
  import { getMidsceneRunSubDir as getMidsceneRunSubDir2 } from "misoai-shared/common";
1979
2028
  import { getDebug as getDebug3 } from "misoai-shared/logger";
1980
2029
  import { ifInBrowser as ifInBrowser2 } from "misoai-shared/utils";
1981
- import yaml3 from "js-yaml";
1982
2030
  import semver from "semver";
1983
2031
 
1984
2032
  // package.json
1985
- var version = "1.5.8";
2033
+ var version = "1.6.1";
1986
2034
 
1987
2035
  // src/common/task-cache.ts
1988
2036
  var debug3 = getDebug3("cache");
@@ -2193,7 +2241,10 @@ var PageAgent = class {
2193
2241
  }
2194
2242
  this.taskExecutor = new PageTaskExecutor(this.page, this.insight, {
2195
2243
  taskCache: this.taskCache,
2196
- onTaskStart: this.callbackOnTaskStartTip.bind(this)
2244
+ onTaskStart: this.callbackOnTaskStartTip.bind(this),
2245
+ memoryConfig: opts?.memoryConfig,
2246
+ sessionId: opts?.sessionId,
2247
+ workflowId: opts?.workflowId
2197
2248
  });
2198
2249
  this.dump = this.resetDump();
2199
2250
  this.reportFileName = reportFileName(
@@ -2274,22 +2325,28 @@ var PageAgent = class {
2274
2325
  const allThoughts = executor.tasks.filter((task) => task.thought).map((task) => task.thought);
2275
2326
  const allLocates = executor.tasks.filter((task) => task.locate).map((task) => task.locate);
2276
2327
  const allPlans = executor.tasks.filter((task) => task.param?.plans).map((task) => task.param?.plans);
2277
- const planningTasks = executor.tasks.filter((task) => task.type === "Planning");
2278
- const insightTasks = executor.tasks.filter((task) => task.type === "Insight");
2328
+ const planningTasks = executor.tasks.filter(
2329
+ (task) => task.type === "Planning"
2330
+ );
2331
+ const insightTasks = executor.tasks.filter(
2332
+ (task) => task.type === "Insight"
2333
+ );
2279
2334
  const actionTasks = executor.tasks.filter((task) => task.type === "Action");
2280
2335
  const planning = planningTasks.length > 0 ? {
2281
2336
  type: "Planning",
2282
- description: `Planning for task execution`,
2337
+ description: "Planning for task execution",
2283
2338
  steps: planningTasks.map((task) => task.thought || "Planning step")
2284
2339
  } : void 0;
2285
2340
  const insight = insightTasks.length > 0 ? {
2286
2341
  type: "Insight",
2287
- description: `Insight for task execution`,
2288
- elements: insightTasks.map((task) => task.thought || "Insight element")
2342
+ description: "Insight for task execution",
2343
+ elements: insightTasks.map(
2344
+ (task) => task.thought || "Insight element"
2345
+ )
2289
2346
  } : void 0;
2290
2347
  const action = actionTasks.length > 0 ? {
2291
2348
  type: "Action",
2292
- description: `Action for task execution`,
2349
+ description: "Action for task execution",
2293
2350
  result: lastTask?.output
2294
2351
  } : void 0;
2295
2352
  const actionDetails = executor.tasks.map((task) => ({
@@ -2623,7 +2680,10 @@ ${memoryContext}` : void 0;
2623
2680
  }
2624
2681
  const memoryContext = this.getMemoryAsContext();
2625
2682
  const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${assertion}` : assertion;
2626
- const { output, executor } = await this.taskExecutor.assert(assertionWithContext, memoryContext);
2683
+ const { output, executor } = await this.taskExecutor.assert(
2684
+ assertionWithContext,
2685
+ memoryContext
2686
+ );
2627
2687
  const metadata = this.afterTaskRunning(executor, true);
2628
2688
  if (output && opt?.keepRawResponse) {
2629
2689
  return {
@@ -2644,6 +2704,7 @@ ${reasonMsg}`);
2644
2704
  }
2645
2705
  async aiCaptcha(options) {
2646
2706
  const { deepThink = false, autoDetectComplexity = true } = options || {};
2707
+ const memoryContext = this.getMemoryAsContext();
2647
2708
  let shouldUseDeepThink = deepThink;
2648
2709
  if (autoDetectComplexity && !deepThink) {
2649
2710
  const context = await this.getUIContext();
@@ -2661,7 +2722,10 @@ A complex CAPTCHA typically has one or more of these characteristics:
2661
2722
  Return only "complex" or "simple" based on your analysis.
2662
2723
  `;
2663
2724
  const complexityMsgs = [
2664
- { role: "system", content: "You are an AI assistant that analyzes screenshots to determine CAPTCHA complexity." },
2725
+ {
2726
+ role: "system",
2727
+ content: "You are an AI assistant that analyzes screenshots to determine CAPTCHA complexity."
2728
+ },
2665
2729
  {
2666
2730
  role: "user",
2667
2731
  content: [
@@ -2685,7 +2749,12 @@ Return only "complex" or "simple" based on your analysis.
2685
2749
  );
2686
2750
  const responseText = typeof complexityResult.content === "string" ? complexityResult.content.toLowerCase() : JSON.stringify(complexityResult.content).toLowerCase();
2687
2751
  shouldUseDeepThink = responseText.includes("complex");
2688
- debug4("CAPTCHA complexity analysis:", responseText, "Using deep think:", shouldUseDeepThink);
2752
+ debug4(
2753
+ "CAPTCHA complexity analysis:",
2754
+ responseText,
2755
+ "Using deep think:",
2756
+ shouldUseDeepThink
2757
+ );
2689
2758
  } catch (error) {
2690
2759
  debug4("Failed to analyze CAPTCHA complexity:", error);
2691
2760
  }
@@ -2702,7 +2771,9 @@ Return only "complex" or "simple" based on your analysis.
2702
2771
  await this.aiTap(action.target, { deepThink: shouldUseDeepThink });
2703
2772
  } else if (action.type === "input" && action.value) {
2704
2773
  if (action.target) {
2705
- await this.aiInput(action.value, action.target, { deepThink: shouldUseDeepThink });
2774
+ await this.aiInput(action.value, action.target, {
2775
+ deepThink: shouldUseDeepThink
2776
+ });
2706
2777
  }
2707
2778
  } else if (action.type === "verify" && action.target) {
2708
2779
  await this.aiTap(action.target, { deepThink: shouldUseDeepThink });
@@ -2714,7 +2785,9 @@ Return only "complex" or "simple" based on your analysis.
2714
2785
  if (action.coordinates) {
2715
2786
  const x = action.coordinates[0];
2716
2787
  const y = action.coordinates[1];
2717
- await this.aiTap(`element at coordinates (${x}, ${y})`, { deepThink: shouldUseDeepThink });
2788
+ await this.aiTap(`element at coordinates (${x}, ${y})`, {
2789
+ deepThink: shouldUseDeepThink
2790
+ });
2718
2791
  } else if (action.target) {
2719
2792
  await this.aiTap(action.target, { deepThink: shouldUseDeepThink });
2720
2793
  }
@@ -2724,6 +2797,26 @@ Return only "complex" or "simple" based on your analysis.
2724
2797
  }
2725
2798
  }
2726
2799
  await new Promise((resolve2) => setTimeout(resolve2, 3e3));
2800
+ const captchaMemoryItem = {
2801
+ id: `captcha_${Date.now()}`,
2802
+ timestamp: Date.now(),
2803
+ taskType: "Action",
2804
+ summary: `Solved ${captchaResult.captchaType} CAPTCHA: ${captchaResult.thought}`,
2805
+ context: {
2806
+ url: await this.page.url?.() || "",
2807
+ captchaType: captchaResult.captchaType,
2808
+ actions: captchaResult.actions,
2809
+ deepThink: actualDeepThink
2810
+ },
2811
+ metadata: {
2812
+ executionTime: Date.now() - Date.now(),
2813
+ // Will be updated
2814
+ success: true,
2815
+ confidence: 0.9
2816
+ },
2817
+ tags: ["captcha", "action", captchaResult.captchaType]
2818
+ };
2819
+ this.taskExecutor.addToMemory(captchaMemoryItem);
2727
2820
  const metadata = {
2728
2821
  status: "finished",
2729
2822
  usage,
@@ -2740,10 +2833,15 @@ Return only "complex" or "simple" based on your analysis.
2740
2833
  }
2741
2834
  async aiWaitFor(assertion, opt) {
2742
2835
  const startTime = Date.now();
2743
- const { executor } = await this.taskExecutor.waitFor(assertion, {
2836
+ const memoryContext = this.getMemoryAsContext();
2837
+ const assertionWithContext = memoryContext ? `${assertion}
2838
+
2839
+ Previous workflow steps:
2840
+ ${memoryContext}` : assertion;
2841
+ const { executor } = await this.taskExecutor.waitFor(assertionWithContext, {
2744
2842
  timeoutMs: opt?.timeoutMs || 15 * 1e3,
2745
2843
  checkIntervalMs: opt?.checkIntervalMs || 3 * 1e3,
2746
- assertion
2844
+ assertion: assertionWithContext
2747
2845
  });
2748
2846
  const metadata = {
2749
2847
  status: executor.isInErrorState() ? "failed" : "finished",
@@ -2841,25 +2939,27 @@ ${errors}`);
2841
2939
  const executionDump = {
2842
2940
  name: screenshotTitle,
2843
2941
  description: content,
2844
- tasks: [{
2845
- type: "Screenshot",
2846
- subType: "log",
2847
- status: "finished",
2848
- executor: null,
2849
- param: {
2850
- title: screenshotTitle,
2851
- content
2852
- },
2853
- output: {
2854
- screenshot
2855
- },
2856
- thought: `Logged screenshot: ${screenshotTitle}`,
2857
- timing: {
2858
- start: Date.now(),
2859
- end: Date.now(),
2860
- cost: 0
2942
+ tasks: [
2943
+ {
2944
+ type: "Screenshot",
2945
+ subType: "log",
2946
+ status: "finished",
2947
+ executor: null,
2948
+ param: {
2949
+ title: screenshotTitle,
2950
+ content
2951
+ },
2952
+ output: {
2953
+ screenshot
2954
+ },
2955
+ thought: `Logged screenshot: ${screenshotTitle}`,
2956
+ timing: {
2957
+ start: Date.now(),
2958
+ end: Date.now(),
2959
+ cost: 0
2960
+ }
2861
2961
  }
2862
- }],
2962
+ ],
2863
2963
  sdkVersion: "1.0.0",
2864
2964
  logTime: Date.now(),
2865
2965
  model_name: "screenshot"
@@ -2911,7 +3011,9 @@ ${errors}`);
2911
3011
  totalTasks: stats.analytics.totalTasks,
2912
3012
  memoryHits: stats.analytics.memoryHits,
2913
3013
  memoryMisses: stats.analytics.memoryMisses,
2914
- memoryEffectiveness: Math.round(stats.analytics.memoryEffectiveness * 100),
3014
+ memoryEffectiveness: Math.round(
3015
+ stats.analytics.memoryEffectiveness * 100
3016
+ ),
2915
3017
  averageMemorySize: Math.round(stats.analytics.averageMemorySize * 100) / 100
2916
3018
  },
2917
3019
  config: stats.config,
@@ -2975,7 +3077,9 @@ ${errors}`);
2975
3077
  calculateSuccessRate(memory) {
2976
3078
  if (memory.length === 0)
2977
3079
  return 0;
2978
- const successCount = memory.filter((item) => item.metadata?.success !== false).length;
3080
+ const successCount = memory.filter(
3081
+ (item) => item.metadata?.success !== false
3082
+ ).length;
2979
3083
  return Math.round(successCount / memory.length * 100);
2980
3084
  }
2981
3085
  calculateAverageExecutionTime(memory) {
@@ -3296,8 +3400,8 @@ var WebPage = class extends Page {
3296
3400
 
3297
3401
  // src/playwright/ai-fixture.ts
3298
3402
  import { randomUUID } from "crypto";
3299
- import { getDebug as getDebug6 } from "misoai-shared/logger";
3300
3403
  import { test } from "@playwright/test";
3404
+ import { getDebug as getDebug6 } from "misoai-shared/logger";
3301
3405
  var debugPage2 = getDebug6("web:playwright:ai-fixture");
3302
3406
  var groupAndCaseForTest = (testInfo) => {
3303
3407
  let taskFile;