@midscene/core 1.17.5 → 1.17.7

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 (120) hide show
  1. package/dist/es/agent/agent-report-manager.mjs +1 -1
  2. package/dist/es/agent/agent-report-manager.mjs.map +1 -1
  3. package/dist/es/agent/utils.mjs +1 -1
  4. package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs +80 -0
  5. package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs.map +1 -0
  6. package/dist/es/ai-model/model-adapter/locate.mjs +2 -1
  7. package/dist/es/ai-model/model-adapter/locate.mjs.map +1 -1
  8. package/dist/es/ai-model/models/jev/locate.mjs +2 -12
  9. package/dist/es/ai-model/models/jev/locate.mjs.map +1 -1
  10. package/dist/es/ai-model/service-caller/call-ai.mjs +4 -1
  11. package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
  12. package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs +4 -3
  13. package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs.map +1 -1
  14. package/dist/es/ai-model/service-caller/openai/siwc/network.mjs +36 -0
  15. package/dist/es/ai-model/service-caller/openai/siwc/network.mjs.map +1 -0
  16. package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs +5 -3
  17. package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs.map +1 -1
  18. package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs +7 -4
  19. package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs.map +1 -1
  20. package/dist/es/ai-model/shared/json.mjs +6 -2
  21. package/dist/es/ai-model/shared/json.mjs.map +1 -1
  22. package/dist/es/ai-model/workflows/grounding/locate.mjs +1 -1
  23. package/dist/es/ai-model/workflows/grounding/locate.mjs.map +1 -1
  24. package/dist/es/ai-model/workflows/grounding/section-locate.mjs +1 -1
  25. package/dist/es/ai-model/workflows/grounding/section-locate.mjs.map +1 -1
  26. package/dist/es/ai-model/workflows/grounding/utils.mjs +8 -26
  27. package/dist/es/ai-model/workflows/grounding/utils.mjs.map +1 -1
  28. package/dist/es/dump/report-image-export.mjs +58 -0
  29. package/dist/es/dump/report-image-export.mjs.map +1 -0
  30. package/dist/es/dump/screenshot-store.mjs +5 -0
  31. package/dist/es/dump/screenshot-store.mjs.map +1 -1
  32. package/dist/es/report-cli.mjs +13 -29
  33. package/dist/es/report-cli.mjs.map +1 -1
  34. package/dist/es/report-generator.mjs +22 -8
  35. package/dist/es/report-generator.mjs.map +1 -1
  36. package/dist/es/report-html-template.mjs +1 -1
  37. package/dist/es/report-markdown.mjs +50 -53
  38. package/dist/es/report-markdown.mjs.map +1 -1
  39. package/dist/es/report.mjs +8 -24
  40. package/dist/es/report.mjs.map +1 -1
  41. package/dist/es/siwc.mjs +4 -0
  42. package/dist/es/task-runner.mjs +7 -2
  43. package/dist/es/task-runner.mjs.map +1 -1
  44. package/dist/es/utils.mjs +1 -1
  45. package/dist/lib/agent/agent-report-manager.js +1 -1
  46. package/dist/lib/agent/agent-report-manager.js.map +1 -1
  47. package/dist/lib/agent/utils.js +1 -1
  48. package/dist/lib/ai-model/model-adapter/locate-prompt-order.js +117 -0
  49. package/dist/lib/ai-model/model-adapter/locate-prompt-order.js.map +1 -0
  50. package/dist/lib/ai-model/model-adapter/locate.js +2 -1
  51. package/dist/lib/ai-model/model-adapter/locate.js.map +1 -1
  52. package/dist/lib/ai-model/models/jev/locate.js +2 -12
  53. package/dist/lib/ai-model/models/jev/locate.js.map +1 -1
  54. package/dist/lib/ai-model/service-caller/call-ai.js +4 -1
  55. package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
  56. package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js +3 -2
  57. package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js.map +1 -1
  58. package/dist/lib/ai-model/service-caller/openai/siwc/network.js +73 -0
  59. package/dist/lib/ai-model/service-caller/openai/siwc/network.js.map +1 -0
  60. package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js +4 -2
  61. package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js.map +1 -1
  62. package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js +7 -4
  63. package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js.map +1 -1
  64. package/dist/lib/ai-model/shared/json.js +6 -2
  65. package/dist/lib/ai-model/shared/json.js.map +1 -1
  66. package/dist/lib/ai-model/workflows/grounding/locate.js +1 -1
  67. package/dist/lib/ai-model/workflows/grounding/locate.js.map +1 -1
  68. package/dist/lib/ai-model/workflows/grounding/section-locate.js +1 -1
  69. package/dist/lib/ai-model/workflows/grounding/section-locate.js.map +1 -1
  70. package/dist/lib/ai-model/workflows/grounding/utils.js +8 -26
  71. package/dist/lib/ai-model/workflows/grounding/utils.js.map +1 -1
  72. package/dist/lib/dump/report-image-export.js +92 -0
  73. package/dist/lib/dump/report-image-export.js.map +1 -0
  74. package/dist/lib/dump/screenshot-store.js +5 -0
  75. package/dist/lib/dump/screenshot-store.js.map +1 -1
  76. package/dist/lib/report-cli.js +12 -28
  77. package/dist/lib/report-cli.js.map +1 -1
  78. package/dist/lib/report-generator.js +22 -8
  79. package/dist/lib/report-generator.js.map +1 -1
  80. package/dist/lib/report-html-template.js +1 -1
  81. package/dist/lib/report-markdown.js +50 -53
  82. package/dist/lib/report-markdown.js.map +1 -1
  83. package/dist/lib/report.js +6 -22
  84. package/dist/lib/report.js.map +1 -1
  85. package/dist/lib/siwc.js +52 -0
  86. package/dist/lib/siwc.js.map +1 -0
  87. package/dist/lib/task-runner.js +7 -2
  88. package/dist/lib/task-runner.js.map +1 -1
  89. package/dist/lib/utils.js +1 -1
  90. package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts +24 -0
  91. package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts.map +1 -0
  92. package/dist/types/ai-model/model-adapter/locate.d.ts.map +1 -1
  93. package/dist/types/ai-model/model-adapter/types.d.ts +9 -3
  94. package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
  95. package/dist/types/ai-model/models/jev/locate.d.ts.map +1 -1
  96. package/dist/types/ai-model/service-caller/openai/siwc/authorization.d.ts.map +1 -1
  97. package/dist/types/ai-model/service-caller/openai/siwc/network.d.ts +8 -0
  98. package/dist/types/ai-model/service-caller/openai/siwc/network.d.ts.map +1 -0
  99. package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts +1 -1
  100. package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts.map +1 -1
  101. package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts +2 -1
  102. package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts.map +1 -1
  103. package/dist/types/ai-model/shared/json.d.ts +4 -15
  104. package/dist/types/ai-model/shared/json.d.ts.map +1 -1
  105. package/dist/types/ai-model/workflows/grounding/utils.d.ts +4 -9
  106. package/dist/types/ai-model/workflows/grounding/utils.d.ts.map +1 -1
  107. package/dist/types/dump/report-image-export.d.ts +12 -0
  108. package/dist/types/dump/report-image-export.d.ts.map +1 -0
  109. package/dist/types/dump/screenshot-store.d.ts +2 -0
  110. package/dist/types/dump/screenshot-store.d.ts.map +1 -1
  111. package/dist/types/report-cli.d.ts.map +1 -1
  112. package/dist/types/report-generator.d.ts +4 -2
  113. package/dist/types/report-generator.d.ts.map +1 -1
  114. package/dist/types/report-markdown.d.ts +4 -2
  115. package/dist/types/report-markdown.d.ts.map +1 -1
  116. package/dist/types/report.d.ts.map +1 -1
  117. package/dist/types/siwc.d.ts +5 -0
  118. package/dist/types/siwc.d.ts.map +1 -0
  119. package/dist/types/task-runner.d.ts.map +1 -1
  120. package/package.json +10 -2
@@ -1 +1 @@
1
- {"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport * as fsAsync from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg, uuid } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent/report-file-name';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n extractLastDumpScriptSync,\n generateAgentReportComment,\n getBaseUrlFixScript,\n generateTestRunReportScriptTag as runnerDumpScript,\n streamDumpScriptsSync,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport {\n imageRefFileExtension,\n normalizeStoredImageRef,\n} from './dump/image-reference';\nimport {\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nimport {\n parseBase64ImageDataUrl,\n resolveImageSource,\n} from './dump/screenshot-store';\nexport {\n readReportActionDump,\n readReportArtifactManifestAsync,\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nexport { ExecutionRecorder } from './dump/execution-recorder';\nexport { ReportArtifactStore } from './dump/report-artifact-store';\nexport { ReportSession } from './dump/report-session';\nimport {\n publishReportArtifact,\n publishReportArtifactSync,\n} from './dump/report-artifact-publication';\nimport {\n ReportArtifactWriter,\n type ReportGroupAttributes,\n} from './dump/report-artifact-writer';\nimport { collectReportSummary } from './report-stats';\nimport type {\n AssembleTestRunReportOptions,\n IndexedTestRunReportSource,\n TestRunReportMetrics,\n TestRunReportSource,\n TestRunReportSourceIndex,\n} from './test-run-report';\nimport {\n type MergeTestRunReportSource,\n mergeTestRunReportDumps,\n} from './test-runner/reporting/merge-report';\nimport { collectTestRunReportMetrics } from './test-runner/reporting/metrics';\nimport {\n type ExecutionDump,\n type IExecutionDump,\n ReportActionDump,\n type ScreenshotMode,\n} from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, getVersion, reportHTMLContent } from './utils';\n\nexport {\n calculateTestRunHealth,\n classifyTestRunCase,\n} from './test-run-health';\n\nexport interface ExportReportArtifactOptions {\n reportPath: string;\n outputDir: string;\n reportFileName: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n overwrite?: boolean;\n}\n\n/** Export only referenced facts and images, validating them before publishing. */\nexport async function exportReportArtifact(\n options: ExportReportArtifactOptions,\n): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const input = path.resolve(options.reportPath);\n const output = path.resolve(options.outputDir);\n const external = options.outputFormat === 'html-and-external-assets';\n const artifact = path.join(output, options.reportFileName);\n const destination = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (\n input === destination ||\n (external && input.startsWith(`${artifact}${path.sep}`))\n )\n throw new Error('Export destination must differ from the input report.');\n if (!options.overwrite && existsSync(external ? artifact : destination))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const manifest = readReportArtifactManifest(input);\n const runner = readReportRunnerDump(input);\n const groups = new Map<\n string,\n { dump: ReportActionDump; attributes: ReportGroupAttributes }\n >();\n for (const source of readReportDumpScripts(input)) {\n const attributes = Object.fromEntries(\n [...source.openTag.matchAll(/([^\\s=]+)=\"([^\"]*)\"/g)]\n .filter(([, key]) => key !== 'type')\n .map(([, key, value]) => [key, decodeURIComponent(value)]),\n );\n const id = attributes['data-group-id'];\n if (!id) throw new Error('Report execution group is missing its ID.');\n const dump = ReportActionDump.fromSerializedString(\n manifest ? source.content : antiEscapeScriptTag(source.content),\n );\n const previous = groups.get(id);\n dump.executions = dedupeExecutionsKeepLatest([\n ...(previous?.dump.executions ?? []),\n ...dump.executions,\n ]);\n groups.set(id, { dump, attributes });\n }\n if (!groups.size && !runner)\n throw new Error('No report execution data was found in the input.');\n const hint = [...groups.values()][0]?.attributes['data-report-kind'];\n const kind =\n manifest?.kind ??\n (hint === 'agent' || hint === 'test' ? hint : runner ? 'test' : 'agent');\n await fsAsync.mkdir(output, { recursive: true });\n const temporary = await fsAsync.mkdtemp(path.join(output, '.report-export-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n kind,\n manifest?.reportId ?? runner?.runId,\n );\n await writer.initialize();\n for (const [id, group] of groups)\n await writer.addGroup(\n id,\n group.dump.serialize(),\n input,\n group.attributes,\n );\n await writer.finish(\n runner,\n mergedAgentReportComment([...groups.values()].map((group) => group.dump)),\n );\n await publishReportArtifact(\n external ? temporary : entry,\n external ? artifact : destination,\n external,\n options.overwrite,\n );\n return destination;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n}\n\n/**\n * Read the screenshot storage mode a report declared at generation time.\n *\n * Reports written by current versions stamp `data-screenshot-mode` onto every\n * dump script tag, so we only need to peek at the first real dump script (the\n * template's bundled JS also references the dump type string, hence the\n * `data-group-id` filter) and can stop streaming immediately.\n *\n * Returns undefined for legacy reports that predate the attribute or for\n * unreadable files, letting the caller fall back to a filesystem heuristic.\n */\nconst screenshotModeAttrRegExp = new RegExp(\n `${DATA_SCREENSHOT_MODE_ATTR}=\"(inline|directory)\"`,\n);\n\nfunction readDeclaredScreenshotMode(\n reportFilePath: string,\n): ScreenshotMode | undefined {\n let mode: ScreenshotMode | undefined;\n try {\n streamDumpScriptsSync(reportFilePath, ({ openTag }) => {\n // Skip false matches from the template's bundled JS code.\n if (!openTag.includes('data-group-id')) return false;\n const match = openTag.match(screenshotModeAttrRegExp);\n mode = match?.[1] as ScreenshotMode | undefined;\n return true; // the first real dump script decides the mode\n });\n } catch {\n // Unreadable / non-existent file — let the caller fall back.\n }\n return mode;\n}\n\n/**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n *\n * The mode is read from the report's own `data-screenshot-mode` metadata, which\n * is authoritative regardless of whether the run happened to capture any\n * screenshots. For legacy reports without the attribute we fall back to the old\n * filesystem heuristic (an `index.html` that has a sibling `screenshots/` dir).\n */\nexport function isDirectoryModeReport(reportFilePath: string): boolean {\n if (readReportArtifactManifest(reportFilePath)) return true;\n // Agent directory reports use index.html by default. Reports without the\n // versioned manifest use the existing metadata and layout heuristic.\n if (path.basename(reportFilePath) !== 'index.html') return false;\n\n const declared = readDeclaredScreenshotMode(reportFilePath);\n if (declared) return declared === 'directory';\n\n // Legacy fallback for reports generated before screenshotMode was embedded.\n return existsSync(path.join(path.dirname(reportFilePath), 'screenshots'));\n}\n\n/**\n * Whether a report lives in its own dedicated directory (`{name}/index.html`)\n * rather than being a single standalone file (`{name}.html`).\n *\n * This is a structural fact about *where the report file sits*, independent of\n * how it stores screenshots: a directory report can keep screenshots external\n * (a `screenshots/` sibling) OR inline them into `index.html`. Deletion needs\n * this — not the screenshot mode — to decide whether to remove the whole\n * directory or just unlink a file, so that an inline-screenshot report nested in\n * its own folder still has the folder removed instead of being orphaned.\n */\nfunction isDirectoryBasedReport(reportFilePath: string): boolean {\n return path.basename(reportFilePath) === 'index.html';\n}\n\n/**\n * Remove a complete report artifact from disk.\n *\n * Standalone reports own only their HTML file, while directory-based reports\n * own the directory containing `index.html`. Callers must decide whether they\n * own the source artifact and should only invoke this after its replacement\n * has been produced successfully.\n *\n * @param reportFilePath Path to the standalone report or directory report's\n * `index.html` entry point.\n * @throws When the owned report artifact cannot be removed.\n */\nexport function removeReportArtifact(reportFilePath: string): void {\n if (isDirectoryBasedReport(reportFilePath)) {\n rmSync(path.dirname(reportFilePath), { recursive: true, force: true });\n return;\n }\n unlinkSync(reportFilePath);\n}\n\n/**\n * Deduplicate executions by stable id, keeping only the last occurrence.\n * Old-format executions without id are always preserved.\n */\nexport function dedupeExecutionsKeepLatest<T extends Pick<ExecutionDump, 'id'>>(\n executions: T[],\n): T[] {\n let noIdCounter = 0;\n const deduped = new Map<string, T>();\n for (const exec of executions) {\n const key = exec.id || `__no_id_${noIdCounter++}`;\n deduped.set(key, exec);\n }\n return Array.from(deduped.values());\n}\n/**\n * Peek at the first `sdkVersion` field embedded in a midscene_web_dump\n * script tag inside the given report file. Returns undefined if no\n * recognizable tag or sdkVersion is present.\n */\nfunction peekReportSdkVersion(reportFilePath: string): string | undefined {\n try {\n let dump: string | undefined;\n streamReportDumpScripts(reportFilePath, ({ content }) => {\n dump = content;\n return true;\n });\n dump ??= extractLastDumpScriptSync(reportFilePath) ?? undefined;\n if (!dump) return undefined;\n const match = dump.match(/\"sdkVersion\"\\s*:\\s*\"([^\"]+)\"/);\n return match?.[1];\n } catch {\n return undefined;\n }\n}\n\nconst warnedMismatchedVersions = new Set<string>();\n\nfunction tryParseAgentReportDump(dumpString: string): ReportActionDump | null {\n const trimmed = dumpString.trimStart();\n if (!trimmed.startsWith('{') || !trimmed.includes('\"executions\"')) {\n return null;\n }\n\n try {\n return ReportActionDump.fromSerializedString(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction mergedAgentReportComment(reports: ReportActionDump[]): string {\n if (reports.length === 0) {\n return '';\n }\n if (reports.length === 1) {\n return generateAgentReportComment(reports[0]);\n }\n\n const deviceTypes = Array.from(\n new Set(reports.map((report) => report.deviceType).filter(Boolean)),\n );\n const mergedReport = new ReportActionDump({\n sdkVersion: reports[0].sdkVersion || getVersion(),\n groupName: 'Merged Midscene Report',\n groupDescription: 'Agent-readable summary for merged report HTML',\n modelBriefs: reports.flatMap((report) => report.modelBriefs ?? []),\n deviceType: deviceTypes.length === 1 ? deviceTypes[0] : 'mixed',\n executions: dedupeExecutionsKeepLatest(\n reports.flatMap((report) => report.executions ?? []),\n ),\n });\n return generateAgentReportComment(mergedReport);\n}\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n\n private createEmptyDumpString(groupName: string, groupDescription?: string) {\n return new ReportActionDump({\n sdkVersion: '',\n groupName,\n groupDescription,\n modelBriefs: [],\n executions: [],\n }).serialize();\n }\n\n public append(reportInfo: ReportFileWithAttributes) {\n if (\n !reportInfo.reportFilePath &&\n reportInfo.reportAttributes.testStatus !== 'skipped'\n ) {\n throw new Error(\n 'reportFilePath is required unless reportAttributes.testStatus is \"skipped\"',\n );\n }\n\n if (reportInfo.reportFilePath) {\n const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] ReportMergingTool version mismatch: source report was written by @midscene/core@${sourceVersion} but the merger is @midscene/core@${currentVersion}. This commonly means @midscene/core and the device package (e.g. @midscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`,\n );\n }\n }\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Merge multiple dump script contents (from the same source report)\n * into a single serialized ReportActionDump string.\n * If there's only one dump, return it as-is. If multiple, merge\n * all executions into the first dump's group structure.\n */\n private mergeDumpScripts(contents: string[], external: boolean): string {\n const unescaped = contents\n .map((c) => (external ? c : antiEscapeScriptTag(c)))\n .filter((c) => c.length > 0);\n if (unescaped.length === 0) return '';\n if (unescaped.length === 1) return unescaped[0];\n\n // Parse all dumps and collect executions, deduplicating by id (keep last).\n // Only executions with a stable id are deduped; old-format entries without\n // id are always kept (they may be distinct despite sharing the same name).\n const base = ReportActionDump.fromSerializedString(unescaped[0]);\n const allExecutions = [...base.executions];\n for (let i = 1; i < unescaped.length; i++) {\n const other = ReportActionDump.fromSerializedString(unescaped[i]);\n allExecutions.push(...other.executions);\n }\n base.executions = dedupeExecutionsKeepLatest(allExecutions);\n return base.serialize();\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n outputDir?: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n } = {},\n ): string | null {\n const {\n rmOriginalReports = false,\n overwrite = false,\n outputDir,\n outputFormat,\n } = opts;\n if (!this.reportInfos.length) {\n logMsg('No reports to merge');\n return null;\n }\n const runnerDumps = this.reportInfos.map((info) =>\n info.reportFilePath\n ? readReportRunnerDump(info.reportFilePath)\n : undefined,\n );\n const hasRunnerDump = runnerDumps.some(Boolean);\n const external = outputFormat === 'html-and-external-assets';\n const name =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n validateTestRunReportFileName(name);\n const targetDir = outputDir\n ? path.resolve(outputDir)\n : getMidsceneRunSubDir('report');\n mkdirSync(targetDir, { recursive: true });\n const artifact = path.join(targetDir, name);\n const outputFilePath = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!overwrite && existsSync(external ? artifact : outputFilePath))\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n if (\n rmOriginalReports &&\n this.reportInfos.some(\n (info) =>\n info.reportFilePath &&\n isDirectoryBasedReport(info.reportFilePath) &&\n outputFilePath.startsWith(\n `${path.dirname(path.resolve(info.reportFilePath))}${path.sep}`,\n ),\n )\n )\n throw new Error(\n 'Merged output must be outside source report directories when removing originals.',\n );\n const temporary = mkdtempSync(path.join(targetDir, '.merged-report-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n hasRunnerDump ? 'test' : 'agent',\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n const runnerSources: MergeTestRunReportSource[] = [];\n const groupIds = new Set<string>();\n for (const [index, info] of this.reportInfos.entries()) {\n const attributes = info.reportAttributes;\n let serialized = this.createEmptyDumpString(\n attributes.testTitle,\n attributes.testDescription,\n );\n let groupId = `merged-group-${index}`;\n if (info.reportFilePath) {\n const dumps = readReportDumpScripts(info.reportFilePath);\n const originalId = dumps[0]?.openTag.match(\n /data-group-id=\"([^\"]+)\"/,\n )?.[1];\n if (\n originalId &&\n !hasRunnerDump &&\n !groupIds.has(decodeURIComponent(originalId))\n )\n groupId = decodeURIComponent(originalId);\n serialized = dumps.length\n ? this.mergeDumpScripts(\n dumps.map((dump) => dump.content),\n Boolean(readReportArtifactManifest(info.reportFilePath)),\n )\n : (extractLastDumpScriptSync(info.reportFilePath) ?? serialized);\n }\n if (groupIds.has(groupId)) groupId = `merged-group-${uuid()}`;\n groupIds.add(groupId);\n const agentReport = tryParseAgentReportDump(serialized);\n if (agentReport) agentReports.push(agentReport);\n if (hasRunnerDump)\n runnerSources.push({\n reportId: groupId,\n sourcePath: info.reportFilePath ?? '<skipped-agent-report>',\n attributes,\n dump: runnerDumps[index],\n timing: agentReport\n ? agentReportTiming(agentReport, attributes.testDuration)\n : undefined,\n });\n writer.addGroupSync(groupId, serialized, info.reportFilePath ?? entry, {\n ...(hasRunnerDump ? { 'data-report-id': groupId } : {}),\n playwright_test_duration: attributes.testDuration,\n playwright_test_status: attributes.testStatus,\n playwright_test_title: attributes.testTitle,\n playwright_test_id: attributes.testId,\n playwright_test_description: attributes.testDescription,\n is_merged: true,\n });\n }\n let runner;\n if (hasRunnerDump) {\n const executions = dedupeExecutionsKeepLatest(\n agentReports.flatMap((report) => report.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n runner = mergeTestRunReportDumps(\n runnerSources,\n metrics,\n uuid(),\n executionMetrics,\n );\n }\n writer.finishSync(runner, mergedAgentReportComment(agentReports));\n publishReportArtifactSync(\n external ? temporary : entry,\n external ? artifact : outputFilePath,\n external,\n overwrite,\n );\n if (rmOriginalReports)\n for (const sourcePath of new Set(\n this.reportInfos.flatMap((info) =>\n info.reportFilePath ? [path.resolve(info.reportFilePath)] : [],\n ),\n )) {\n if (sourcePath !== outputFilePath) removeReportArtifact(sourcePath);\n }\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n return outputFilePath;\n } finally {\n rmSync(temporary, { recursive: true, force: true });\n }\n }\n}\n\nfunction agentReportTiming(\n report: ReportActionDump,\n durationMs: number,\n): MergeTestRunReportSource['timing'] {\n const timing = collectReportSummary(report).timing;\n const executionTimes = report.executions\n .map((execution) => execution.logTime)\n .filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value),\n );\n const started =\n timing.wallTimeStart ??\n (executionTimes.length ? Math.min(...executionTimes) : undefined);\n if (started === undefined) return undefined;\n const ended =\n timing.wallTimeEnd ??\n Math.max(\n ...executionTimes,\n started + (Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0),\n );\n return {\n startedAt: new Date(started).toISOString(),\n endedAt: new Date(ended).toISOString(),\n durationMs: Math.max(0, ended - started),\n };\n}\n\ninterface PreparedTestRunReportSource extends IndexedTestRunReportSource {\n dump: ReportActionDump;\n dumpPath?: string;\n imageOrigins: Map<string, string>;\n}\n\nconst validateTestRunReportFileName = (fileName: string): void => {\n if (\n !fileName ||\n fileName === '.' ||\n fileName === '..' ||\n path.basename(fileName) !== fileName ||\n fileName.endsWith('.html')\n ) {\n throw new Error(\n `Midscene Test reportFileName must be one path segment without an extension: ${fileName}`,\n );\n }\n};\n\nconst uniqueTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n): TestRunReportSource[] => {\n const unique = new Map<string, TestRunReportSource>();\n\n for (const source of sources) {\n const scopeId = source.scopeId.trim();\n const sourcePath = path.resolve(source.sourcePath);\n if (!scopeId) {\n throw new Error('Midscene Test report source scopeId must not be empty.');\n }\n\n unique.set(`${scopeId}\\u0000${source.dumpPath ?? sourcePath}`, {\n ...source,\n scopeId,\n sourcePath,\n });\n }\n\n return [...unique.values()];\n};\n\nconst prepareTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n loadedDumps?: ReadonlyMap<string, string | Error>,\n): {\n sources: PreparedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n capturedMetrics: TestRunReportMetrics;\n executionMetrics: Record<string, TestRunReportMetrics>;\n diagnostics: import('./test-run-report').TestRunReportDiagnostic[];\n} => {\n const prepared: PreparedTestRunReportSource[] = [];\n const diagnostics: import('./test-run-report').TestRunReportDiagnostic[] = [];\n const ownerByScopedExecution = new Map<string, string>();\n const preparedByPath = new Map<string, PreparedTestRunReportSource>();\n\n for (const [sourceIndex, source] of uniqueTestRunReportSources(\n sources,\n ).entries()) {\n let baseDump: ReportActionDump;\n let executions: IExecutionDump[];\n try {\n if (source.dumpPath) {\n const contents =\n loadedDumps?.get(source.dumpPath) ??\n readFileSync(source.dumpPath, 'utf8');\n if (contents instanceof Error) throw contents;\n baseDump = ReportActionDump.fromSerializedString(contents);\n executions = baseDump.executions;\n } else {\n if (!existsSync(source.sourcePath))\n throw new Error(`Agent report does not exist: ${source.sourcePath}`);\n const sourceVersion = peekReportSdkVersion(source.sourcePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] TestRunReportAssembler version mismatch: source report was written by @midscene/core@${sourceVersion} but the assembler is @midscene/core@${currentVersion}. Align the workspace package versions before generating the Midscene Test report.`,\n );\n }\n ({ baseDump, executions } = collectDedupedExecutions(\n source.sourcePath,\n ));\n }\n } catch (error) {\n diagnostics.push({\n level: 'warning',\n code: 'agent-source-unavailable',\n scopeId: source.scopeId,\n sourcePaths: [source.sourcePath],\n message: `Agent source could not be collected: ${String(error)}`,\n });\n continue;\n }\n // A borrowed Agent can serve several sequential test scopes. Reuse its\n // report group and metrics while indexing each scope's execution links.\n const sourceKey = source.sourcePath;\n const imageOrigins = new Map<string, string>();\n JSON.parse(baseDump.serialize(), (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n if (ref)\n imageOrigins.set(\n ref.id,\n ref.storage === 'file'\n ? (source.dumpPath ?? source.sourcePath)\n : source.sourcePath,\n );\n return value;\n });\n const shared = preparedByPath.get(sourceKey);\n if (shared) {\n for (const [id, origin] of imageOrigins)\n shared.imageOrigins.set(id, origin);\n if (source.dumpPath) {\n shared.dump.executions = dedupeExecutionsKeepLatest([\n ...shared.dump.executions,\n ...baseDump.executions,\n ]);\n }\n const scoped = prepared.find(\n (item) =>\n item.reportId === shared.reportId && item.scopeId === source.scopeId,\n );\n const executionIds = [\n ...new Set([\n ...(scoped?.executionIds ?? []),\n ...executions.flatMap((execution) =>\n execution.id ? [execution.id] : [],\n ),\n ]),\n ];\n for (const executionId of executionIds) {\n const key = `${source.scopeId}\\u0000${executionId}`;\n const owner = ownerByScopedExecution.get(key);\n if (owner && owner !== shared.reportId)\n throw new Error(\n `Agent execution ${executionId} is present in multiple reports for test scope ${source.scopeId}: ${owner}, ${shared.reportId}`,\n );\n ownerByScopedExecution.set(key, shared.reportId);\n }\n if (scoped) {\n scoped.executionIds = executionIds;\n const partial = [scoped.collection, source.collection].find(\n (collection) => collection?.status === 'partial',\n );\n scoped.collection = partial ?? source.collection ?? scoped.collection;\n } else\n prepared.push({\n ...shared,\n scopeId: source.scopeId,\n executionIds,\n collection: source.collection,\n });\n continue;\n }\n const reportId = `runner-report-${sourceIndex + 1}`;\n const executionIds: string[] = [];\n for (const execution of executions) {\n if (!execution.id) continue;\n const scopedExecution = `${source.scopeId}\\u0000${execution.id}`;\n const previousOwner = ownerByScopedExecution.get(scopedExecution);\n if (previousOwner && previousOwner !== reportId) {\n throw new Error(\n `Agent execution ${execution.id} is present in multiple reports for test scope ${source.scopeId}: ${previousOwner}, ${reportId}`,\n );\n }\n ownerByScopedExecution.set(scopedExecution, reportId);\n executionIds.push(execution.id);\n }\n\n const dump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n const preparedSource = {\n reportId,\n scopeId: source.scopeId,\n sourcePath: source.sourcePath,\n executionIds,\n collection: source.collection,\n dump,\n imageOrigins,\n ...(source.dumpPath ? { dumpPath: source.dumpPath } : {}),\n };\n prepared.push(preparedSource);\n preparedByPath.set(sourceKey, preparedSource);\n }\n\n // A legacy invocation can preserve a snapshot of an Agent also used by\n // native scopes. Count stable executions once across both paths.\n const executions = dedupeExecutionsKeepLatest(\n [...preparedByPath.values()].flatMap((source) => source.dump.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n return {\n sources: prepared,\n diagnostics,\n capturedMetrics: metrics,\n executionMetrics,\n metrics: {\n ...metrics,\n collection: {\n ...metrics.collection!,\n fields: Object.fromEntries(\n Object.entries(metrics.collection!.fields!).map(([field, status]) => [\n field,\n diagnostics.length\n ? prepared.length === 0\n ? 'unknown'\n : status === 'complete'\n ? 'partial'\n : status\n : status,\n ]),\n ) as NonNullable<\n NonNullable<TestRunReportMetrics['collection']>['fields']\n >,\n status: diagnostics.length\n ? prepared.length\n ? 'partial'\n : 'unknown'\n : metrics.collection!.status,\n expectedExecutionCount: 0,\n capturedExecutionCount: 0,\n reasons: [\n ...metrics.collection!.reasons,\n ...diagnostics.map((diagnostic) => diagnostic.message),\n ],\n },\n },\n };\n};\n\n/**\n * Build a self-contained Midscene Test report while preserving the existing\n * Agent dump format consumed by the Report App.\n *\n * Test hierarchy and status live in `midscene_test_run_dump`; Agent reports\n * remain independent `midscene_web_dump` groups addressed by `data-report-id`.\n */\nexport class TestRunReportAssembler {\n /** Runtime publisher: current Agent snapshots avoid parsing HTML or copying unused images. */\n public async assembleAsync(\n options: AssembleTestRunReportOptions,\n ): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const loaded = new Map<string, string | Error>();\n await Promise.all(\n options.sources.map(async (source) => {\n if (source.dumpPath) {\n try {\n loaded.set(\n source.dumpPath,\n await fsAsync.readFile(source.dumpPath, 'utf8'),\n );\n } catch (error) {\n loaded.set(\n source.dumpPath,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }),\n );\n const prepared = prepareTestRunReportSources(options.sources, loaded);\n const dump = options.buildRunnerDump({\n sources: prepared.sources,\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n });\n if (dump.kind !== 'test-runner' || dump.schemaVersion !== 1)\n throw new Error('Invalid Runner report dump');\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const outputDir = path.resolve(options.outputDir);\n await fsAsync.mkdir(outputDir, { recursive: true });\n const artifact = path.join(outputDir, options.reportFileName);\n const finalPath = directoryMode\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!options.overwrite && existsSync(directoryMode ? artifact : finalPath))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const temporary = await fsAsync.mkdtemp(\n path.join(outputDir, '.test-run-report-'),\n );\n const htmlPath = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n htmlPath,\n directoryMode,\n 'test',\n dump.runId,\n );\n await writer.initialize();\n const reports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n reports.push(source.dump);\n await writer.addGroup(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n await writer.finish(dump, mergedAgentReportComment(reports));\n await publishReportArtifact(\n directoryMode ? temporary : htmlPath,\n directoryMode ? artifact : finalPath,\n directoryMode,\n options.overwrite,\n );\n return finalPath;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n }\n\n public assemble(options: AssembleTestRunReportOptions): string {\n validateTestRunReportFileName(options.reportFileName);\n const outputDir = path.resolve(options.outputDir);\n mkdirSync(outputDir, { recursive: true });\n\n const prepared = prepareTestRunReportSources(options.sources);\n const index: TestRunReportSourceIndex = {\n sources: prepared.sources.map(\n ({ reportId, scopeId, sourcePath, executionIds, collection }) => ({\n reportId,\n scopeId,\n sourcePath,\n executionIds,\n collection,\n }),\n ),\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n };\n const runnerDump = options.buildRunnerDump(index);\n if (runnerDump.schemaVersion !== 1 || runnerDump.kind !== 'test-runner') {\n throw new Error(\n 'buildRunnerDump must return a Midscene Test report dump with schemaVersion 1 and kind \"test-runner\".',\n );\n }\n // Validate serializability before creating any output artifact.\n JSON.stringify(runnerDump);\n\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const finalArtifactPath = path.join(outputDir, options.reportFileName);\n const directoryReportPath = path.join(finalArtifactPath, 'index.html');\n const standaloneReportPath = `${finalArtifactPath}.html`;\n const finalReportPath = directoryMode\n ? directoryReportPath\n : standaloneReportPath;\n const existingArtifacts = [\n directoryMode ? finalArtifactPath : standaloneReportPath,\n ].filter(existsSync);\n if (existingArtifacts.length > 0 && !options.overwrite) {\n throw new Error(\n `Report artifact already exists: ${existingArtifacts[0]}\\nSet overwrite to true to overwrite this report.`,\n );\n }\n\n const temporaryRoot = mkdtempSync(\n path.join(outputDir, '.test-run-report-'),\n );\n const temporaryArtifactPath = path.join(\n temporaryRoot,\n options.reportFileName,\n );\n const temporaryReportPath = directoryMode\n ? path.join(temporaryArtifactPath, 'index.html')\n : `${temporaryArtifactPath}.html`;\n\n try {\n if (directoryMode) {\n mkdirSync(temporaryArtifactPath, { recursive: true });\n }\n\n const writer = new ReportArtifactWriter(\n temporaryReportPath,\n directoryMode,\n 'test',\n runnerDump.runId,\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n agentReports.push(source.dump);\n writer.addGroupSync(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n writer.finishSync(runnerDump, mergedAgentReportComment(agentReports));\n\n publishReportArtifactSync(\n directoryMode ? temporaryArtifactPath : temporaryReportPath,\n directoryMode ? finalArtifactPath : standaloneReportPath,\n directoryMode,\n options.overwrite,\n );\n rmSync(temporaryRoot, { recursive: true, force: true });\n return finalReportPath;\n } catch (error) {\n rmSync(temporaryRoot, { recursive: true, force: true });\n throw error;\n }\n }\n}\n\nexport interface SplitReportHtmlOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport interface SplitReportHtmlResult {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n}\n\nexport interface CollectedReportExecutions {\n baseDump: ReportActionDump;\n executions: IExecutionDump[];\n}\n\n/**\n * Collect executions from a report HTML, deduplicating by stable id while\n * keeping only the latest occurrence. Old-format executions without id are\n * always preserved.\n */\nexport function collectDedupedExecutions(\n htmlPath: string,\n): CollectedReportExecutions {\n let baseDump: ReportActionDump | null = null;\n let executionSerial = 0;\n const latestSerialByExecutionId = new Map<string, number>();\n\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (execution.id) {\n latestSerialByExecutionId.set(execution.id, executionSerial);\n }\n }\n return false;\n });\n\n const executions: IExecutionDump[] = [];\n executionSerial = 0;\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n if (!baseDump) {\n baseDump = groupedDump;\n }\n\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (\n execution.id &&\n latestSerialByExecutionId.get(execution.id) !== executionSerial\n ) {\n continue;\n }\n executions.push(execution);\n }\n\n return false;\n });\n\n if (!baseDump) {\n throw new Error(`No report dump scripts found in ${htmlPath}`);\n }\n\n return {\n baseDump,\n executions,\n };\n}\n\nfunction externalizeImagesInExecution(\n execution: IExecutionDump,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const visit = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) {\n visit(item);\n }\n return;\n }\n\n if (typeof node !== 'object' || node === null) return;\n\n const ref = normalizeStoredImageRef(node);\n if (ref) {\n const ext = imageRefFileExtension(ref);\n const fileName = `${ref.id}.${ext}`;\n const relativePath = `./screenshots/${fileName}`;\n const absolutePath = path.join(opts.screenshotsDir, fileName);\n\n if (!opts.writtenFiles.has(fileName)) {\n const resolved = resolveImageSource(ref, {\n reportPath: opts.htmlPath,\n });\n if (resolved.type === 'data-uri') {\n const { rawBase64 } = parseBase64ImageDataUrl(resolved.dataUri);\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n } else {\n copyFileSync(resolved.filePath, absolutePath);\n }\n opts.writtenFiles.add(fileName);\n }\n\n ref.storage = 'file';\n ref.path = relativePath;\n return;\n }\n\n for (const value of Object.values(node)) {\n visit(value);\n }\n };\n\n visit(execution);\n}\n\n/**\n * Reverse parse a Midscene report HTML into per-execution JSON files and\n * externalized screenshots.\n */\nexport function splitReportHtmlByExecution(\n options: SplitReportHtmlOptions,\n): SplitReportHtmlResult {\n const { htmlPath, outputDir } = options;\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const executionJsonFiles: string[] = [];\n const writtenScreenshotFiles = new Set<string>();\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n let fileIndex = 0;\n for (const execution of executions) {\n fileIndex += 1;\n externalizeImagesInExecution(execution, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshotFiles,\n });\n const singleExecutionDump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions: [execution],\n });\n\n const jsonFilePath = path.join(outputDir, `${fileIndex}.execution.json`);\n writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');\n executionJsonFiles.push(jsonFilePath);\n }\n\n return {\n executionJsonFiles,\n screenshotFiles: Array.from(writtenScreenshotFiles)\n .sort()\n .map((fileName) => path.join(screenshotsDir, fileName)),\n };\n}\n"],"names":["exportReportArtifact","options","validateTestRunReportFileName","input","path","output","external","artifact","destination","Error","existsSync","manifest","readReportArtifactManifest","runner","readReportRunnerDump","groups","Map","source","readReportDumpScripts","attributes","Object","key","value","decodeURIComponent","id","dump","ReportActionDump","antiEscapeScriptTag","previous","dedupeExecutionsKeepLatest","hint","kind","fsAsync","temporary","entry","writer","ReportArtifactWriter","group","mergedAgentReportComment","publishReportArtifact","screenshotModeAttrRegExp","RegExp","DATA_SCREENSHOT_MODE_ATTR","readDeclaredScreenshotMode","reportFilePath","mode","streamDumpScriptsSync","openTag","match","isDirectoryModeReport","declared","isDirectoryBasedReport","removeReportArtifact","rmSync","unlinkSync","executions","noIdCounter","deduped","exec","Array","peekReportSdkVersion","streamReportDumpScripts","content","extractLastDumpScriptSync","undefined","warnedMismatchedVersions","Set","tryParseAgentReportDump","dumpString","trimmed","reports","generateAgentReportComment","deviceTypes","report","Boolean","mergedReport","getVersion","ReportMergingTool","groupName","groupDescription","reportInfo","sourceVersion","currentVersion","logMsg","contents","unescaped","c","base","allExecutions","i","other","reportFileName","opts","rmOriginalReports","overwrite","outputDir","outputFormat","runnerDumps","info","hasRunnerDump","name","getReportFileName","targetDir","getMidsceneRunSubDir","mkdirSync","outputFilePath","mkdtempSync","agentReports","runnerSources","groupIds","index","serialized","groupId","dumps","originalId","uuid","agentReport","agentReportTiming","metrics","collectTestRunReportMetrics","executionMetrics","execution","mergeTestRunReportDumps","publishReportArtifactSync","sourcePath","durationMs","timing","collectReportSummary","executionTimes","Number","started","Math","ended","Date","fileName","uniqueTestRunReportSources","sources","unique","scopeId","prepareTestRunReportSources","loadedDumps","prepared","diagnostics","ownerByScopedExecution","preparedByPath","sourceIndex","baseDump","readFileSync","collectDedupedExecutions","error","String","sourceKey","imageOrigins","JSON","_key","ref","normalizeStoredImageRef","shared","origin","scoped","item","executionIds","executionId","owner","partial","collection","reportId","scopedExecution","previousOwner","preparedSource","field","status","diagnostic","TestRunReportAssembler","loaded","Promise","directoryMode","finalPath","htmlPath","runnerDump","finalArtifactPath","directoryReportPath","standaloneReportPath","finalReportPath","existingArtifacts","temporaryRoot","temporaryArtifactPath","temporaryReportPath","executionSerial","latestSerialByExecutionId","dumpScript","groupedDump","externalizeImagesInExecution","visit","node","ext","imageRefFileExtension","relativePath","absolutePath","resolved","resolveImageSource","rawBase64","parseBase64ImageDataUrl","writeFileSync","Buffer","copyFileSync","splitReportHtmlByExecution","screenshotsDir","executionJsonFiles","writtenScreenshotFiles","fileIndex","singleExecutionDump","jsonFilePath"],"mappings":"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"}
1
+ {"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n appendFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport * as fsAsync from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg, uuid } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent/report-file-name';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n extractLastDumpScriptSync,\n generateAgentReportComment,\n getBaseUrlFixScript,\n generateTestRunReportScriptTag as runnerDumpScript,\n streamDumpScriptsSync,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport { normalizeStoredImageRef } from './dump/image-reference';\nimport {\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nimport { ReportImageExporter } from './dump/report-image-export';\nexport {\n readReportActionDump,\n readReportArtifactManifestAsync,\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nexport { ExecutionRecorder } from './dump/execution-recorder';\nexport { ReportArtifactStore } from './dump/report-artifact-store';\nexport { ReportSession } from './dump/report-session';\nimport {\n publishReportArtifact,\n publishReportArtifactSync,\n} from './dump/report-artifact-publication';\nimport {\n ReportArtifactWriter,\n type ReportGroupAttributes,\n} from './dump/report-artifact-writer';\nimport { collectReportSummary } from './report-stats';\nimport type {\n AssembleTestRunReportOptions,\n IndexedTestRunReportSource,\n TestRunReportMetrics,\n TestRunReportSource,\n TestRunReportSourceIndex,\n} from './test-run-report';\nimport {\n type MergeTestRunReportSource,\n mergeTestRunReportDumps,\n} from './test-runner/reporting/merge-report';\nimport { collectTestRunReportMetrics } from './test-runner/reporting/metrics';\nimport {\n type ExecutionDump,\n type IExecutionDump,\n ReportActionDump,\n type ScreenshotMode,\n} from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, getVersion, reportHTMLContent } from './utils';\n\nexport {\n calculateTestRunHealth,\n classifyTestRunCase,\n} from './test-run-health';\n\nexport interface ExportReportArtifactOptions {\n reportPath: string;\n outputDir: string;\n reportFileName: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n overwrite?: boolean;\n}\n\n/** Export only referenced facts and images, validating them before publishing. */\nexport async function exportReportArtifact(\n options: ExportReportArtifactOptions,\n): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const input = path.resolve(options.reportPath);\n const output = path.resolve(options.outputDir);\n const external = options.outputFormat === 'html-and-external-assets';\n const artifact = path.join(output, options.reportFileName);\n const destination = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (\n input === destination ||\n (external && input.startsWith(`${artifact}${path.sep}`))\n )\n throw new Error('Export destination must differ from the input report.');\n if (!options.overwrite && existsSync(external ? artifact : destination))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const manifest = readReportArtifactManifest(input);\n const runner = readReportRunnerDump(input);\n const groups = new Map<\n string,\n { dump: ReportActionDump; attributes: ReportGroupAttributes }\n >();\n for (const source of readReportDumpScripts(input)) {\n const attributes = Object.fromEntries(\n [...source.openTag.matchAll(/([^\\s=]+)=\"([^\"]*)\"/g)]\n .filter(([, key]) => key !== 'type')\n .map(([, key, value]) => [key, decodeURIComponent(value)]),\n );\n const id = attributes['data-group-id'];\n if (!id) throw new Error('Report execution group is missing its ID.');\n const dump = ReportActionDump.fromSerializedString(\n manifest ? source.content : antiEscapeScriptTag(source.content),\n );\n const previous = groups.get(id);\n dump.executions = dedupeExecutionsKeepLatest([\n ...(previous?.dump.executions ?? []),\n ...dump.executions,\n ]);\n groups.set(id, { dump, attributes });\n }\n if (!groups.size && !runner)\n throw new Error('No report execution data was found in the input.');\n const hint = [...groups.values()][0]?.attributes['data-report-kind'];\n const kind =\n manifest?.kind ??\n (hint === 'agent' || hint === 'test' ? hint : runner ? 'test' : 'agent');\n await fsAsync.mkdir(output, { recursive: true });\n const temporary = await fsAsync.mkdtemp(path.join(output, '.report-export-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n kind,\n manifest?.reportId ?? runner?.runId,\n );\n await writer.initialize();\n for (const [id, group] of groups)\n await writer.addGroup(\n id,\n group.dump.serialize(),\n input,\n group.attributes,\n );\n await writer.finish(\n runner,\n mergedAgentReportComment([...groups.values()].map((group) => group.dump)),\n );\n await publishReportArtifact(\n external ? temporary : entry,\n external ? artifact : destination,\n external,\n options.overwrite,\n );\n return destination;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n}\n\n/**\n * Read the screenshot storage mode a report declared at generation time.\n *\n * Reports written by current versions stamp `data-screenshot-mode` onto every\n * dump script tag, so we only need to peek at the first real dump script (the\n * template's bundled JS also references the dump type string, hence the\n * `data-group-id` filter) and can stop streaming immediately.\n *\n * Returns undefined for legacy reports that predate the attribute or for\n * unreadable files, letting the caller fall back to a filesystem heuristic.\n */\nconst screenshotModeAttrRegExp = new RegExp(\n `${DATA_SCREENSHOT_MODE_ATTR}=\"(inline|directory)\"`,\n);\n\nfunction readDeclaredScreenshotMode(\n reportFilePath: string,\n): ScreenshotMode | undefined {\n let mode: ScreenshotMode | undefined;\n try {\n streamDumpScriptsSync(reportFilePath, ({ openTag }) => {\n // Skip false matches from the template's bundled JS code.\n if (!openTag.includes('data-group-id')) return false;\n const match = openTag.match(screenshotModeAttrRegExp);\n mode = match?.[1] as ScreenshotMode | undefined;\n return true; // the first real dump script decides the mode\n });\n } catch {\n // Unreadable / non-existent file — let the caller fall back.\n }\n return mode;\n}\n\n/**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n *\n * The mode is read from the report's own `data-screenshot-mode` metadata, which\n * is authoritative regardless of whether the run happened to capture any\n * screenshots. For legacy reports without the attribute we fall back to the old\n * filesystem heuristic (an `index.html` that has a sibling `screenshots/` dir).\n */\nexport function isDirectoryModeReport(reportFilePath: string): boolean {\n if (readReportArtifactManifest(reportFilePath)) return true;\n // Agent directory reports use index.html by default. Reports without the\n // versioned manifest use the existing metadata and layout heuristic.\n if (path.basename(reportFilePath) !== 'index.html') return false;\n\n const declared = readDeclaredScreenshotMode(reportFilePath);\n if (declared) return declared === 'directory';\n\n // Legacy fallback for reports generated before screenshotMode was embedded.\n return existsSync(path.join(path.dirname(reportFilePath), 'screenshots'));\n}\n\n/**\n * Whether a report lives in its own dedicated directory (`{name}/index.html`)\n * rather than being a single standalone file (`{name}.html`).\n *\n * This is a structural fact about *where the report file sits*, independent of\n * how it stores screenshots: a directory report can keep screenshots external\n * (a `screenshots/` sibling) OR inline them into `index.html`. Deletion needs\n * this — not the screenshot mode — to decide whether to remove the whole\n * directory or just unlink a file, so that an inline-screenshot report nested in\n * its own folder still has the folder removed instead of being orphaned.\n */\nfunction isDirectoryBasedReport(reportFilePath: string): boolean {\n return path.basename(reportFilePath) === 'index.html';\n}\n\n/**\n * Remove a complete report artifact from disk.\n *\n * Standalone reports own only their HTML file, while directory-based reports\n * own the directory containing `index.html`. Callers must decide whether they\n * own the source artifact and should only invoke this after its replacement\n * has been produced successfully.\n *\n * @param reportFilePath Path to the standalone report or directory report's\n * `index.html` entry point.\n * @throws When the owned report artifact cannot be removed.\n */\nexport function removeReportArtifact(reportFilePath: string): void {\n if (isDirectoryBasedReport(reportFilePath)) {\n rmSync(path.dirname(reportFilePath), { recursive: true, force: true });\n return;\n }\n unlinkSync(reportFilePath);\n}\n\n/**\n * Deduplicate executions by stable id, keeping only the last occurrence.\n * Old-format executions without id are always preserved.\n */\nexport function dedupeExecutionsKeepLatest<T extends Pick<ExecutionDump, 'id'>>(\n executions: T[],\n): T[] {\n let noIdCounter = 0;\n const deduped = new Map<string, T>();\n for (const exec of executions) {\n const key = exec.id || `__no_id_${noIdCounter++}`;\n deduped.set(key, exec);\n }\n return Array.from(deduped.values());\n}\n/**\n * Peek at the first `sdkVersion` field embedded in a midscene_web_dump\n * script tag inside the given report file. Returns undefined if no\n * recognizable tag or sdkVersion is present.\n */\nfunction peekReportSdkVersion(reportFilePath: string): string | undefined {\n try {\n let dump: string | undefined;\n streamReportDumpScripts(reportFilePath, ({ content }) => {\n dump = content;\n return true;\n });\n dump ??= extractLastDumpScriptSync(reportFilePath) ?? undefined;\n if (!dump) return undefined;\n const match = dump.match(/\"sdkVersion\"\\s*:\\s*\"([^\"]+)\"/);\n return match?.[1];\n } catch {\n return undefined;\n }\n}\n\nconst warnedMismatchedVersions = new Set<string>();\n\nfunction tryParseAgentReportDump(dumpString: string): ReportActionDump | null {\n const trimmed = dumpString.trimStart();\n if (!trimmed.startsWith('{') || !trimmed.includes('\"executions\"')) {\n return null;\n }\n\n try {\n return ReportActionDump.fromSerializedString(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction mergedAgentReportComment(reports: ReportActionDump[]): string {\n if (reports.length === 0) {\n return '';\n }\n if (reports.length === 1) {\n return generateAgentReportComment(reports[0]);\n }\n\n const deviceTypes = Array.from(\n new Set(reports.map((report) => report.deviceType).filter(Boolean)),\n );\n const mergedReport = new ReportActionDump({\n sdkVersion: reports[0].sdkVersion || getVersion(),\n groupName: 'Merged Midscene Report',\n groupDescription: 'Agent-readable summary for merged report HTML',\n modelBriefs: reports.flatMap((report) => report.modelBriefs ?? []),\n deviceType: deviceTypes.length === 1 ? deviceTypes[0] : 'mixed',\n executions: dedupeExecutionsKeepLatest(\n reports.flatMap((report) => report.executions ?? []),\n ),\n });\n return generateAgentReportComment(mergedReport);\n}\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n\n private createEmptyDumpString(groupName: string, groupDescription?: string) {\n return new ReportActionDump({\n sdkVersion: '',\n groupName,\n groupDescription,\n modelBriefs: [],\n executions: [],\n }).serialize();\n }\n\n public append(reportInfo: ReportFileWithAttributes) {\n if (\n !reportInfo.reportFilePath &&\n reportInfo.reportAttributes.testStatus !== 'skipped'\n ) {\n throw new Error(\n 'reportFilePath is required unless reportAttributes.testStatus is \"skipped\"',\n );\n }\n\n if (reportInfo.reportFilePath) {\n const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] ReportMergingTool version mismatch: source report was written by @midscene/core@${sourceVersion} but the merger is @midscene/core@${currentVersion}. This commonly means @midscene/core and the device package (e.g. @midscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`,\n );\n }\n }\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Merge multiple dump script contents (from the same source report)\n * into a single serialized ReportActionDump string.\n * If there's only one dump, return it as-is. If multiple, merge\n * all executions into the first dump's group structure.\n */\n private mergeDumpScripts(contents: string[], external: boolean): string {\n const unescaped = contents\n .map((c) => (external ? c : antiEscapeScriptTag(c)))\n .filter((c) => c.length > 0);\n if (unescaped.length === 0) return '';\n if (unescaped.length === 1) return unescaped[0];\n\n // Parse all dumps and collect executions, deduplicating by id (keep last).\n // Only executions with a stable id are deduped; old-format entries without\n // id are always kept (they may be distinct despite sharing the same name).\n const base = ReportActionDump.fromSerializedString(unescaped[0]);\n const allExecutions = [...base.executions];\n for (let i = 1; i < unescaped.length; i++) {\n const other = ReportActionDump.fromSerializedString(unescaped[i]);\n allExecutions.push(...other.executions);\n }\n base.executions = dedupeExecutionsKeepLatest(allExecutions);\n return base.serialize();\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n outputDir?: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n } = {},\n ): string | null {\n const {\n rmOriginalReports = false,\n overwrite = false,\n outputDir,\n outputFormat,\n } = opts;\n if (!this.reportInfos.length) {\n logMsg('No reports to merge');\n return null;\n }\n const runnerDumps = this.reportInfos.map((info) =>\n info.reportFilePath\n ? readReportRunnerDump(info.reportFilePath)\n : undefined,\n );\n const hasRunnerDump = runnerDumps.some(Boolean);\n const external = outputFormat === 'html-and-external-assets';\n const name =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n validateTestRunReportFileName(name);\n const targetDir = outputDir\n ? path.resolve(outputDir)\n : getMidsceneRunSubDir('report');\n mkdirSync(targetDir, { recursive: true });\n const artifact = path.join(targetDir, name);\n const outputFilePath = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!overwrite && existsSync(external ? artifact : outputFilePath))\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n if (\n rmOriginalReports &&\n this.reportInfos.some(\n (info) =>\n info.reportFilePath &&\n isDirectoryBasedReport(info.reportFilePath) &&\n outputFilePath.startsWith(\n `${path.dirname(path.resolve(info.reportFilePath))}${path.sep}`,\n ),\n )\n )\n throw new Error(\n 'Merged output must be outside source report directories when removing originals.',\n );\n const temporary = mkdtempSync(path.join(targetDir, '.merged-report-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n hasRunnerDump ? 'test' : 'agent',\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n const runnerSources: MergeTestRunReportSource[] = [];\n const groupIds = new Set<string>();\n for (const [index, info] of this.reportInfos.entries()) {\n const attributes = info.reportAttributes;\n let serialized = this.createEmptyDumpString(\n attributes.testTitle,\n attributes.testDescription,\n );\n let groupId = `merged-group-${index}`;\n if (info.reportFilePath) {\n const dumps = readReportDumpScripts(info.reportFilePath);\n const originalId = dumps[0]?.openTag.match(\n /data-group-id=\"([^\"]+)\"/,\n )?.[1];\n if (\n originalId &&\n !hasRunnerDump &&\n !groupIds.has(decodeURIComponent(originalId))\n )\n groupId = decodeURIComponent(originalId);\n serialized = dumps.length\n ? this.mergeDumpScripts(\n dumps.map((dump) => dump.content),\n Boolean(readReportArtifactManifest(info.reportFilePath)),\n )\n : (extractLastDumpScriptSync(info.reportFilePath) ?? serialized);\n }\n if (groupIds.has(groupId)) groupId = `merged-group-${uuid()}`;\n groupIds.add(groupId);\n const agentReport = tryParseAgentReportDump(serialized);\n if (agentReport) agentReports.push(agentReport);\n if (hasRunnerDump)\n runnerSources.push({\n reportId: groupId,\n sourcePath: info.reportFilePath ?? '<skipped-agent-report>',\n attributes,\n dump: runnerDumps[index],\n timing: agentReport\n ? agentReportTiming(agentReport, attributes.testDuration)\n : undefined,\n });\n writer.addGroupSync(groupId, serialized, info.reportFilePath ?? entry, {\n ...(hasRunnerDump ? { 'data-report-id': groupId } : {}),\n playwright_test_duration: attributes.testDuration,\n playwright_test_status: attributes.testStatus,\n playwright_test_title: attributes.testTitle,\n playwright_test_id: attributes.testId,\n playwright_test_description: attributes.testDescription,\n is_merged: true,\n });\n }\n let runner;\n if (hasRunnerDump) {\n const executions = dedupeExecutionsKeepLatest(\n agentReports.flatMap((report) => report.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n runner = mergeTestRunReportDumps(\n runnerSources,\n metrics,\n uuid(),\n executionMetrics,\n );\n }\n writer.finishSync(runner, mergedAgentReportComment(agentReports));\n publishReportArtifactSync(\n external ? temporary : entry,\n external ? artifact : outputFilePath,\n external,\n overwrite,\n );\n if (rmOriginalReports)\n for (const sourcePath of new Set(\n this.reportInfos.flatMap((info) =>\n info.reportFilePath ? [path.resolve(info.reportFilePath)] : [],\n ),\n )) {\n if (sourcePath !== outputFilePath) removeReportArtifact(sourcePath);\n }\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n return outputFilePath;\n } finally {\n rmSync(temporary, { recursive: true, force: true });\n }\n }\n}\n\nfunction agentReportTiming(\n report: ReportActionDump,\n durationMs: number,\n): MergeTestRunReportSource['timing'] {\n const timing = collectReportSummary(report).timing;\n const executionTimes = report.executions\n .map((execution) => execution.logTime)\n .filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value),\n );\n const started =\n timing.wallTimeStart ??\n (executionTimes.length ? Math.min(...executionTimes) : undefined);\n if (started === undefined) return undefined;\n const ended =\n timing.wallTimeEnd ??\n Math.max(\n ...executionTimes,\n started + (Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0),\n );\n return {\n startedAt: new Date(started).toISOString(),\n endedAt: new Date(ended).toISOString(),\n durationMs: Math.max(0, ended - started),\n };\n}\n\ninterface PreparedTestRunReportSource extends IndexedTestRunReportSource {\n dump: ReportActionDump;\n dumpPath?: string;\n imageOrigins: Map<string, string>;\n}\n\nconst validateTestRunReportFileName = (fileName: string): void => {\n if (\n !fileName ||\n fileName === '.' ||\n fileName === '..' ||\n path.basename(fileName) !== fileName ||\n fileName.endsWith('.html')\n ) {\n throw new Error(\n `Midscene Test reportFileName must be one path segment without an extension: ${fileName}`,\n );\n }\n};\n\nconst uniqueTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n): TestRunReportSource[] => {\n const unique = new Map<string, TestRunReportSource>();\n\n for (const source of sources) {\n const scopeId = source.scopeId.trim();\n const sourcePath = path.resolve(source.sourcePath);\n if (!scopeId) {\n throw new Error('Midscene Test report source scopeId must not be empty.');\n }\n\n unique.set(`${scopeId}\\u0000${source.dumpPath ?? sourcePath}`, {\n ...source,\n scopeId,\n sourcePath,\n });\n }\n\n return [...unique.values()];\n};\n\nconst prepareTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n loadedDumps?: ReadonlyMap<string, string | Error>,\n): {\n sources: PreparedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n capturedMetrics: TestRunReportMetrics;\n executionMetrics: Record<string, TestRunReportMetrics>;\n diagnostics: import('./test-run-report').TestRunReportDiagnostic[];\n} => {\n const prepared: PreparedTestRunReportSource[] = [];\n const diagnostics: import('./test-run-report').TestRunReportDiagnostic[] = [];\n const ownerByScopedExecution = new Map<string, string>();\n const preparedByPath = new Map<string, PreparedTestRunReportSource>();\n\n for (const [sourceIndex, source] of uniqueTestRunReportSources(\n sources,\n ).entries()) {\n let baseDump: ReportActionDump;\n let executions: IExecutionDump[];\n try {\n if (source.dumpPath) {\n const contents =\n loadedDumps?.get(source.dumpPath) ??\n readFileSync(source.dumpPath, 'utf8');\n if (contents instanceof Error) throw contents;\n baseDump = ReportActionDump.fromSerializedString(contents);\n executions = baseDump.executions;\n } else {\n if (!existsSync(source.sourcePath))\n throw new Error(`Agent report does not exist: ${source.sourcePath}`);\n const sourceVersion = peekReportSdkVersion(source.sourcePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] TestRunReportAssembler version mismatch: source report was written by @midscene/core@${sourceVersion} but the assembler is @midscene/core@${currentVersion}. Align the workspace package versions before generating the Midscene Test report.`,\n );\n }\n ({ baseDump, executions } = collectDedupedExecutions(\n source.sourcePath,\n ));\n }\n } catch (error) {\n diagnostics.push({\n level: 'warning',\n code: 'agent-source-unavailable',\n scopeId: source.scopeId,\n sourcePaths: [source.sourcePath],\n message: `Agent source could not be collected: ${String(error)}`,\n });\n continue;\n }\n // A borrowed Agent can serve several sequential test scopes. Reuse its\n // report group and metrics while indexing each scope's execution links.\n const sourceKey = source.sourcePath;\n const imageOrigins = new Map<string, string>();\n JSON.parse(baseDump.serialize(), (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n if (ref)\n imageOrigins.set(\n ref.id,\n ref.storage === 'file'\n ? (source.dumpPath ?? source.sourcePath)\n : source.sourcePath,\n );\n return value;\n });\n const shared = preparedByPath.get(sourceKey);\n if (shared) {\n for (const [id, origin] of imageOrigins)\n shared.imageOrigins.set(id, origin);\n if (source.dumpPath) {\n shared.dump.executions = dedupeExecutionsKeepLatest([\n ...shared.dump.executions,\n ...baseDump.executions,\n ]);\n }\n const scoped = prepared.find(\n (item) =>\n item.reportId === shared.reportId && item.scopeId === source.scopeId,\n );\n const executionIds = [\n ...new Set([\n ...(scoped?.executionIds ?? []),\n ...executions.flatMap((execution) =>\n execution.id ? [execution.id] : [],\n ),\n ]),\n ];\n for (const executionId of executionIds) {\n const key = `${source.scopeId}\\u0000${executionId}`;\n const owner = ownerByScopedExecution.get(key);\n if (owner && owner !== shared.reportId)\n throw new Error(\n `Agent execution ${executionId} is present in multiple reports for test scope ${source.scopeId}: ${owner}, ${shared.reportId}`,\n );\n ownerByScopedExecution.set(key, shared.reportId);\n }\n if (scoped) {\n scoped.executionIds = executionIds;\n const partial = [scoped.collection, source.collection].find(\n (collection) => collection?.status === 'partial',\n );\n scoped.collection = partial ?? source.collection ?? scoped.collection;\n } else\n prepared.push({\n ...shared,\n scopeId: source.scopeId,\n executionIds,\n collection: source.collection,\n });\n continue;\n }\n const reportId = `runner-report-${sourceIndex + 1}`;\n const executionIds: string[] = [];\n for (const execution of executions) {\n if (!execution.id) continue;\n const scopedExecution = `${source.scopeId}\\u0000${execution.id}`;\n const previousOwner = ownerByScopedExecution.get(scopedExecution);\n if (previousOwner && previousOwner !== reportId) {\n throw new Error(\n `Agent execution ${execution.id} is present in multiple reports for test scope ${source.scopeId}: ${previousOwner}, ${reportId}`,\n );\n }\n ownerByScopedExecution.set(scopedExecution, reportId);\n executionIds.push(execution.id);\n }\n\n const dump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n const preparedSource = {\n reportId,\n scopeId: source.scopeId,\n sourcePath: source.sourcePath,\n executionIds,\n collection: source.collection,\n dump,\n imageOrigins,\n ...(source.dumpPath ? { dumpPath: source.dumpPath } : {}),\n };\n prepared.push(preparedSource);\n preparedByPath.set(sourceKey, preparedSource);\n }\n\n // A legacy invocation can preserve a snapshot of an Agent also used by\n // native scopes. Count stable executions once across both paths.\n const executions = dedupeExecutionsKeepLatest(\n [...preparedByPath.values()].flatMap((source) => source.dump.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n return {\n sources: prepared,\n diagnostics,\n capturedMetrics: metrics,\n executionMetrics,\n metrics: {\n ...metrics,\n collection: {\n ...metrics.collection!,\n fields: Object.fromEntries(\n Object.entries(metrics.collection!.fields!).map(([field, status]) => [\n field,\n diagnostics.length\n ? prepared.length === 0\n ? 'unknown'\n : status === 'complete'\n ? 'partial'\n : status\n : status,\n ]),\n ) as NonNullable<\n NonNullable<TestRunReportMetrics['collection']>['fields']\n >,\n status: diagnostics.length\n ? prepared.length\n ? 'partial'\n : 'unknown'\n : metrics.collection!.status,\n expectedExecutionCount: 0,\n capturedExecutionCount: 0,\n reasons: [\n ...metrics.collection!.reasons,\n ...diagnostics.map((diagnostic) => diagnostic.message),\n ],\n },\n },\n };\n};\n\n/**\n * Build a self-contained Midscene Test report while preserving the existing\n * Agent dump format consumed by the Report App.\n *\n * Test hierarchy and status live in `midscene_test_run_dump`; Agent reports\n * remain independent `midscene_web_dump` groups addressed by `data-report-id`.\n */\nexport class TestRunReportAssembler {\n /** Runtime publisher: current Agent snapshots avoid parsing HTML or copying unused images. */\n public async assembleAsync(\n options: AssembleTestRunReportOptions,\n ): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const loaded = new Map<string, string | Error>();\n await Promise.all(\n options.sources.map(async (source) => {\n if (source.dumpPath) {\n try {\n loaded.set(\n source.dumpPath,\n await fsAsync.readFile(source.dumpPath, 'utf8'),\n );\n } catch (error) {\n loaded.set(\n source.dumpPath,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }),\n );\n const prepared = prepareTestRunReportSources(options.sources, loaded);\n const dump = options.buildRunnerDump({\n sources: prepared.sources,\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n });\n if (dump.kind !== 'test-runner' || dump.schemaVersion !== 1)\n throw new Error('Invalid Runner report dump');\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const outputDir = path.resolve(options.outputDir);\n await fsAsync.mkdir(outputDir, { recursive: true });\n const artifact = path.join(outputDir, options.reportFileName);\n const finalPath = directoryMode\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!options.overwrite && existsSync(directoryMode ? artifact : finalPath))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const temporary = await fsAsync.mkdtemp(\n path.join(outputDir, '.test-run-report-'),\n );\n const htmlPath = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n htmlPath,\n directoryMode,\n 'test',\n dump.runId,\n );\n await writer.initialize();\n const reports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n reports.push(source.dump);\n await writer.addGroup(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n await writer.finish(dump, mergedAgentReportComment(reports));\n await publishReportArtifact(\n directoryMode ? temporary : htmlPath,\n directoryMode ? artifact : finalPath,\n directoryMode,\n options.overwrite,\n );\n return finalPath;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n }\n\n public assemble(options: AssembleTestRunReportOptions): string {\n validateTestRunReportFileName(options.reportFileName);\n const outputDir = path.resolve(options.outputDir);\n mkdirSync(outputDir, { recursive: true });\n\n const prepared = prepareTestRunReportSources(options.sources);\n const index: TestRunReportSourceIndex = {\n sources: prepared.sources.map(\n ({ reportId, scopeId, sourcePath, executionIds, collection }) => ({\n reportId,\n scopeId,\n sourcePath,\n executionIds,\n collection,\n }),\n ),\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n };\n const runnerDump = options.buildRunnerDump(index);\n if (runnerDump.schemaVersion !== 1 || runnerDump.kind !== 'test-runner') {\n throw new Error(\n 'buildRunnerDump must return a Midscene Test report dump with schemaVersion 1 and kind \"test-runner\".',\n );\n }\n // Validate serializability before creating any output artifact.\n JSON.stringify(runnerDump);\n\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const finalArtifactPath = path.join(outputDir, options.reportFileName);\n const directoryReportPath = path.join(finalArtifactPath, 'index.html');\n const standaloneReportPath = `${finalArtifactPath}.html`;\n const finalReportPath = directoryMode\n ? directoryReportPath\n : standaloneReportPath;\n const existingArtifacts = [\n directoryMode ? finalArtifactPath : standaloneReportPath,\n ].filter(existsSync);\n if (existingArtifacts.length > 0 && !options.overwrite) {\n throw new Error(\n `Report artifact already exists: ${existingArtifacts[0]}\\nSet overwrite to true to overwrite this report.`,\n );\n }\n\n const temporaryRoot = mkdtempSync(\n path.join(outputDir, '.test-run-report-'),\n );\n const temporaryArtifactPath = path.join(\n temporaryRoot,\n options.reportFileName,\n );\n const temporaryReportPath = directoryMode\n ? path.join(temporaryArtifactPath, 'index.html')\n : `${temporaryArtifactPath}.html`;\n\n try {\n if (directoryMode) {\n mkdirSync(temporaryArtifactPath, { recursive: true });\n }\n\n const writer = new ReportArtifactWriter(\n temporaryReportPath,\n directoryMode,\n 'test',\n runnerDump.runId,\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n agentReports.push(source.dump);\n writer.addGroupSync(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n writer.finishSync(runnerDump, mergedAgentReportComment(agentReports));\n\n publishReportArtifactSync(\n directoryMode ? temporaryArtifactPath : temporaryReportPath,\n directoryMode ? finalArtifactPath : standaloneReportPath,\n directoryMode,\n options.overwrite,\n );\n rmSync(temporaryRoot, { recursive: true, force: true });\n return finalReportPath;\n } catch (error) {\n rmSync(temporaryRoot, { recursive: true, force: true });\n throw error;\n }\n }\n}\n\nexport interface SplitReportHtmlOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport interface SplitReportHtmlResult {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n}\n\nexport interface CollectedReportExecutions {\n baseDump: ReportActionDump;\n executions: IExecutionDump[];\n}\n\n/**\n * Collect executions from a report HTML, deduplicating by stable id while\n * keeping only the latest occurrence. Old-format executions without id are\n * always preserved.\n */\nexport function collectDedupedExecutions(\n htmlPath: string,\n): CollectedReportExecutions {\n let baseDump: ReportActionDump | null = null;\n let executionSerial = 0;\n const latestSerialByExecutionId = new Map<string, number>();\n\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (execution.id) {\n latestSerialByExecutionId.set(execution.id, executionSerial);\n }\n }\n return false;\n });\n\n const executions: IExecutionDump[] = [];\n executionSerial = 0;\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n if (!baseDump) {\n baseDump = groupedDump;\n }\n\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (\n execution.id &&\n latestSerialByExecutionId.get(execution.id) !== executionSerial\n ) {\n continue;\n }\n executions.push(execution);\n }\n\n return false;\n });\n\n if (!baseDump) {\n throw new Error(`No report dump scripts found in ${htmlPath}`);\n }\n\n return {\n baseDump,\n executions,\n };\n}\n\nfunction externalizeImagesInExecution(\n execution: IExecutionDump,\n images: ReportImageExporter,\n): void {\n const visit = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) {\n visit(item);\n }\n return;\n }\n\n if (typeof node !== 'object' || node === null) return;\n\n const ref = normalizeStoredImageRef(node);\n if (ref) {\n const fileName = images.write(ref);\n const relativePath = `./screenshots/${fileName}`;\n\n ref.storage = 'file';\n ref.path = relativePath;\n return;\n }\n\n for (const value of Object.values(node)) {\n visit(value);\n }\n };\n\n visit(execution);\n}\n\n/**\n * Reverse parse a Midscene report HTML into per-execution JSON files and\n * externalized screenshots.\n */\nexport function splitReportHtmlByExecution(\n options: SplitReportHtmlOptions,\n): SplitReportHtmlResult {\n const { htmlPath, outputDir } = options;\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const executionJsonFiles: string[] = [];\n const images = new ReportImageExporter(htmlPath, screenshotsDir);\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n let fileIndex = 0;\n for (const execution of executions) {\n fileIndex += 1;\n externalizeImagesInExecution(execution, images);\n const singleExecutionDump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions: [execution],\n });\n\n const jsonFilePath = path.join(outputDir, `${fileIndex}.execution.json`);\n writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');\n executionJsonFiles.push(jsonFilePath);\n }\n\n return {\n executionJsonFiles,\n screenshotFiles: images.files,\n };\n}\n"],"names":["exportReportArtifact","options","validateTestRunReportFileName","input","path","output","external","artifact","destination","Error","existsSync","manifest","readReportArtifactManifest","runner","readReportRunnerDump","groups","Map","source","readReportDumpScripts","attributes","Object","key","value","decodeURIComponent","id","dump","ReportActionDump","antiEscapeScriptTag","previous","dedupeExecutionsKeepLatest","hint","kind","fsAsync","temporary","entry","writer","ReportArtifactWriter","group","mergedAgentReportComment","publishReportArtifact","screenshotModeAttrRegExp","RegExp","DATA_SCREENSHOT_MODE_ATTR","readDeclaredScreenshotMode","reportFilePath","mode","streamDumpScriptsSync","openTag","match","isDirectoryModeReport","declared","isDirectoryBasedReport","removeReportArtifact","rmSync","unlinkSync","executions","noIdCounter","deduped","exec","Array","peekReportSdkVersion","streamReportDumpScripts","content","extractLastDumpScriptSync","undefined","warnedMismatchedVersions","Set","tryParseAgentReportDump","dumpString","trimmed","reports","generateAgentReportComment","deviceTypes","report","Boolean","mergedReport","getVersion","ReportMergingTool","groupName","groupDescription","reportInfo","sourceVersion","currentVersion","logMsg","contents","unescaped","c","base","allExecutions","i","other","reportFileName","opts","rmOriginalReports","overwrite","outputDir","outputFormat","runnerDumps","info","hasRunnerDump","name","getReportFileName","targetDir","getMidsceneRunSubDir","mkdirSync","outputFilePath","mkdtempSync","agentReports","runnerSources","groupIds","index","serialized","groupId","dumps","originalId","uuid","agentReport","agentReportTiming","metrics","collectTestRunReportMetrics","executionMetrics","execution","mergeTestRunReportDumps","publishReportArtifactSync","sourcePath","durationMs","timing","collectReportSummary","executionTimes","Number","started","Math","ended","Date","fileName","uniqueTestRunReportSources","sources","unique","scopeId","prepareTestRunReportSources","loadedDumps","prepared","diagnostics","ownerByScopedExecution","preparedByPath","sourceIndex","baseDump","readFileSync","collectDedupedExecutions","error","String","sourceKey","imageOrigins","JSON","_key","ref","normalizeStoredImageRef","shared","origin","scoped","item","executionIds","executionId","owner","partial","collection","reportId","scopedExecution","previousOwner","preparedSource","field","status","diagnostic","TestRunReportAssembler","loaded","Promise","directoryMode","finalPath","htmlPath","runnerDump","finalArtifactPath","directoryReportPath","standaloneReportPath","finalReportPath","existingArtifacts","temporaryRoot","temporaryArtifactPath","temporaryReportPath","executionSerial","latestSerialByExecutionId","dumpScript","groupedDump","externalizeImagesInExecution","images","visit","node","relativePath","splitReportHtmlByExecution","screenshotsDir","executionJsonFiles","ReportImageExporter","fileIndex","singleExecutionDump","jsonFilePath","writeFileSync"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { refreshSiwcCredentials } from "./ai-model/service-caller/openai/siwc/refresh.mjs";
2
+ import { createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback } from "./ai-model/service-caller/openai/siwc/authorization.mjs";
3
+ import { SiwcError } from "./ai-model/service-caller/openai/siwc/errors.mjs";
4
+ export { SiwcError, createSiwcAuthorization, exchangeSiwcCode, parseSiwcCallback, refreshSiwcCredentials };
@@ -15,6 +15,11 @@ function _define_property(obj, key, value) {
15
15
  }
16
16
  const debug = getDebug('task-runner');
17
17
  const UI_CONTEXT_CACHE_TTL_MS = 300;
18
+ function serializeTaskError(error, signal) {
19
+ const serializedError = serializeError(error);
20
+ if (signal?.aborted && error === signal.reason) serializedError.message = truncateSerializedErrorString(`Operation interrupted by an external abort signal.\nReason: ${serializedError.message}`);
21
+ return serializedError;
22
+ }
18
23
  function serializeErrorTask(task) {
19
24
  if (!task) return;
20
25
  return {
@@ -212,7 +217,7 @@ class TaskRunner {
212
217
  taskIndex++;
213
218
  } catch (error) {
214
219
  successfullyCompleted = false;
215
- const serializedError = serializeError(error);
220
+ const serializedError = serializeTaskError(error, this.abortSignal);
216
221
  task.error = serializedError;
217
222
  task.errorMessage = serializedError.message;
218
223
  task.errorStack = serializedError.stack;
@@ -242,7 +247,7 @@ class TaskRunner {
242
247
  } else {
243
248
  this.status = 'error';
244
249
  const errorTask = this.latestErrorTask();
245
- const error = errorTask?.error ?? (this.abortSignal?.aborted ? serializeError(this.abortSignal.reason) : {
250
+ const error = errorTask?.error ?? (this.abortSignal?.aborted ? serializeTaskError(this.abortSignal.reason, this.abortSignal) : {
246
251
  name: 'Error',
247
252
  message: 'Task execution failed'
248
253
  });
@@ -1 +1 @@
1
- {"version":3,"file":"task-runner.mjs","sources":["../../src/task-runner.ts"],"sourcesContent":["import type { ScreenshotItem } from '@/screenshot-item';\nimport { setTimingFieldOnce } from '@/task-timing';\nimport {\n ExecutionDump,\n type ExecutionRecorderItem,\n type ExecutionTask,\n type ExecutionTaskActionApply,\n type ExecutionTaskApply,\n type ExecutionTaskPlanningLocateOutput,\n type ExecutionTaskProgressOptions,\n type ExecutionTaskReturn,\n type ExecutorContext,\n type PlanningActionParamError,\n type UIContext,\n} from '@/types';\nimport {\n type SerializedError,\n serializeError,\n truncateSerializedErrorString,\n} from '@midscene/shared/agent-tools/error-formatter';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, uuid } from '@midscene/shared/utils';\n\nconst debug = getDebug('task-runner');\nconst UI_CONTEXT_CACHE_TTL_MS = 300;\n\n/**\n * A native, per-task lifecycle notification. The runner is the single source\n * of truth for when a task actually transitions, so it reports each transition\n * exactly once with the task that changed. Consumers no longer have to re-scan\n * the task list and diff it against remembered keys to reconstruct this stream.\n */\nexport type TaskRunnerEventKind =\n | 'append'\n | 'start'\n | 'finish'\n | 'error'\n | 'cancel';\n\nexport interface TaskRunnerEvent {\n kind: TaskRunnerEventKind;\n task: ExecutionTask;\n runner: TaskRunner;\n}\n\nexport interface SerializedErrorTask {\n taskId: string;\n type: ExecutionTask['type'];\n subType?: string;\n status: ExecutionTask['status'];\n thought?: string;\n errorMessage?: string;\n}\n\nexport interface SerializedTaskExecutionError extends SerializedError {\n code: 'TASK_EXECUTION_FAILED';\n cause: SerializedError;\n task?: SerializedErrorTask;\n}\n\nfunction serializeErrorTask(\n task: ExecutionTask | null,\n): SerializedErrorTask | undefined {\n if (!task) {\n return undefined;\n }\n\n return {\n taskId: truncateSerializedErrorString(task.taskId),\n type: task.type,\n ...(task.subType === undefined\n ? {}\n : { subType: truncateSerializedErrorString(task.subType) }),\n status: task.status,\n ...(task.thought === undefined\n ? {}\n : { thought: truncateSerializedErrorString(task.thought) }),\n ...(task.errorMessage === undefined\n ? {}\n : {\n errorMessage: truncateSerializedErrorString(task.errorMessage),\n }),\n };\n}\n\nexport type TaskRunnerEventListener = (\n event: TaskRunnerEvent,\n) => Promise<void> | void;\n\n/** Minimal prompt-image shape retained as report asset metadata. */\nexport interface ExecutionReferenceImage {\n readonly url: string;\n}\n\ntype TaskRunnerInitOptions = ExecutionTaskProgressOptions & {\n tasks?: ExecutionTaskApply[];\n abortSignal?: AbortSignal;\n referenceImages?: readonly ExecutionReferenceImage[];\n /**\n * Coarse \"the execution snapshot changed\" signal. Fires on any state change\n * (append, status flips, completion) with the whole runner, so consumers can\n * re-dump/re-render the current snapshot. Deliberately batch-granular, unlike\n * the per-task {@link onTaskEvent} stream.\n */\n onSnapshotChange?: (\n runner: TaskRunner,\n error?: TaskExecutionError,\n ) => Promise<void> | void;\n onTaskEvent?: TaskRunnerEventListener;\n};\n\ntype TaskRunnerOperationOptions = {\n allowWhenError?: boolean;\n};\n\nexport class TaskRunner {\n readonly id: string;\n name: string;\n\n tasks: ExecutionTask[];\n\n // status of runner\n status: 'init' | 'pending' | 'running' | 'completed' | 'error';\n\n onTaskStart?: ExecutionTaskProgressOptions['onTaskStart'];\n\n private readonly uiContextBuilder: (\n signal?: AbortSignal,\n ) => Promise<UIContext>;\n private readonly abortSignal?: AbortSignal;\n\n private readonly onSnapshotChange?:\n | ((runner: TaskRunner, error?: TaskExecutionError) => Promise<void> | void)\n | undefined;\n\n private readonly onTaskEvent?: TaskRunnerEventListener | undefined;\n\n private readonly executionLogTime: number;\n\n private readonly referenceImageUrls = new Set<string>();\n\n constructor(\n name: string,\n uiContextBuilder: (signal?: AbortSignal) => Promise<UIContext>,\n options?: TaskRunnerInitOptions,\n ) {\n this.id = uuid();\n this.status =\n options?.tasks && options.tasks.length > 0 ? 'pending' : 'init';\n this.name = name;\n this.tasks = (options?.tasks || []).map((item) =>\n this.markTaskAsPending(item),\n );\n this.onTaskStart = options?.onTaskStart;\n this.uiContextBuilder = uiContextBuilder;\n this.abortSignal = options?.abortSignal;\n this.onSnapshotChange = options?.onSnapshotChange;\n this.onTaskEvent = options?.onTaskEvent;\n this.executionLogTime = Date.now();\n for (const image of options?.referenceImages ?? []) {\n this.referenceImageUrls.add(image.url);\n }\n }\n\n private async emitSnapshotChange(error?: TaskExecutionError): Promise<void> {\n if (!this.onSnapshotChange) {\n return;\n }\n await this.onSnapshotChange(this, error);\n }\n\n private async emitTaskEvent(\n kind: TaskRunnerEventKind,\n task: ExecutionTask,\n ): Promise<void> {\n if (!this.onTaskEvent) {\n return;\n }\n try {\n await this.onTaskEvent({ kind, task, runner: this });\n } catch (error) {\n console.error('Error in onTaskEvent listener', error);\n }\n }\n\n private lastUiContext?: {\n context: UIContext;\n capturedAt: number;\n };\n\n private async getUiContext(options?: { forceRefresh?: boolean }): Promise<\n UIContext | undefined\n > {\n this.abortSignal?.throwIfAborted();\n const now = Date.now();\n const shouldReuse =\n !options?.forceRefresh &&\n this.lastUiContext &&\n now - this.lastUiContext.capturedAt <= UI_CONTEXT_CACHE_TTL_MS;\n\n if (shouldReuse && this.lastUiContext?.context) {\n debug(\n `reuse cached uiContext captured ${now - this.lastUiContext.capturedAt}ms ago`,\n );\n return this.lastUiContext?.context;\n }\n\n try {\n const uiContext = await this.uiContextBuilder(this.abortSignal);\n this.abortSignal?.throwIfAborted();\n if (uiContext) {\n this.lastUiContext = {\n context: uiContext,\n capturedAt: Date.now(),\n };\n } else {\n this.lastUiContext = undefined;\n }\n return uiContext;\n } catch (error) {\n this.lastUiContext = undefined;\n throw error;\n }\n }\n\n private async captureScreenshot(): Promise<ScreenshotItem | undefined> {\n try {\n const uiContext = await this.getUiContext({ forceRefresh: true });\n return uiContext?.screenshot;\n } catch (error) {\n console.error('error while capturing screenshot', error);\n }\n return undefined;\n }\n\n private attachRecorderItem(\n task: ExecutionTask,\n screenshot: ScreenshotItem | undefined,\n phase: 'after-calling',\n ): void {\n if (!phase || !screenshot) {\n return;\n }\n\n const recorderItem: ExecutionRecorderItem = {\n type: 'screenshot',\n ts: Date.now(),\n screenshot,\n timing: phase,\n };\n\n if (!task.recorder) {\n task.recorder = [recorderItem];\n return;\n }\n task.recorder.push(recorderItem);\n }\n\n private markTaskAsPending(task: ExecutionTaskApply): ExecutionTask {\n return {\n taskId: uuid(),\n status: 'pending',\n ...task,\n };\n }\n\n private normalizeStatusFromError(\n options?: TaskRunnerOperationOptions,\n errorMessage?: string,\n ): void {\n if (this.status !== 'error') {\n return;\n }\n assert(\n options?.allowWhenError,\n errorMessage ||\n `task runner is in error state, cannot proceed\\nerror=${this.latestErrorTask()?.errorMessage}\\n${this.latestErrorTask()?.errorStack}`,\n );\n // reset runner state so new tasks can run\n this.status = this.tasks.length > 0 ? 'pending' : 'init';\n }\n\n async append(\n task: ExecutionTaskApply[] | ExecutionTaskApply,\n options?: TaskRunnerOperationOptions,\n ): Promise<void> {\n this.abortSignal?.throwIfAborted();\n this.normalizeStatusFromError(\n options,\n `task runner is in error state, cannot append task\\nerror=${this.latestErrorTask()?.errorMessage}\\n${this.latestErrorTask()?.errorStack}`,\n );\n const appended: ExecutionTask[] = [];\n if (Array.isArray(task)) {\n appended.push(...task.map((item) => this.markTaskAsPending(item)));\n } else {\n appended.push(this.markTaskAsPending(task));\n }\n this.tasks.push(...appended);\n if (this.status !== 'running') {\n this.status = 'pending';\n }\n await this.emitSnapshotChange();\n for (const appendedTask of appended) {\n await this.emitTaskEvent('append', appendedTask);\n }\n }\n\n async appendAndFlush(\n task: ExecutionTaskApply[] | ExecutionTaskApply,\n options?: TaskRunnerOperationOptions,\n ): Promise<{ output: any; thought?: string } | undefined> {\n await this.append(task, options);\n return this.flush(options);\n }\n\n async flush(\n options?: TaskRunnerOperationOptions,\n ): Promise<{ output: any; thought?: string } | undefined> {\n if (this.status === 'init' && this.tasks.length > 0) {\n console.warn(\n 'illegal state for task runner, status is init but tasks are not empty',\n );\n }\n\n this.normalizeStatusFromError(options, 'task runner is in error state');\n assert(this.status !== 'running', 'task runner is already running');\n assert(this.status !== 'completed', 'task runner is already completed');\n\n const nextPendingIndex = this.tasks.findIndex(\n (task) => task.status === 'pending',\n );\n if (nextPendingIndex < 0) {\n // all tasks are completed\n return;\n }\n\n this.status = 'running';\n await this.emitSnapshotChange();\n let taskIndex = nextPendingIndex;\n let successfullyCompleted = true;\n\n let previousFindOutput: ExecutionTaskPlanningLocateOutput | undefined;\n\n while (taskIndex < this.tasks.length) {\n const task = this.tasks[taskIndex];\n assert(\n task.status === 'pending',\n `task status should be pending, but got: ${task.status}`,\n );\n let taskStarted = false;\n try {\n this.abortSignal?.throwIfAborted();\n taskStarted = true;\n task.timing = { start: Date.now() };\n task.status = 'running';\n await this.emitSnapshotChange();\n await this.emitTaskEvent('start', task);\n this.abortSignal?.throwIfAborted();\n try {\n if (this.onTaskStart) {\n await this.onTaskStart(task, this.abortSignal);\n }\n } catch (e) {\n console.error('error in onTaskStart', e);\n }\n this.abortSignal?.throwIfAborted();\n assert(\n ['Insight', 'Action Space', 'Planning'].indexOf(task.type) >= 0,\n `unsupported task type: ${task.type}`,\n );\n\n const { executor } = task;\n assert(executor, `executor is required for task type: ${task.type}`);\n\n let returnValue;\n // For Insight tasks (Query/Assert/WaitFor), always get fresh context\n // to ensure we have the latest UI state after any preceding actions\n const forceRefresh = task.type === 'Insight';\n setTimingFieldOnce(task.timing, 'getUiContextStart');\n const uiContext = await this.getUiContext({ forceRefresh });\n setTimingFieldOnce(task.timing, 'getUiContextEnd');\n this.abortSignal?.throwIfAborted();\n\n task.uiContext = uiContext;\n const executorContext: ExecutorContext = {\n task,\n element: previousFindOutput?.element,\n uiContext,\n signal: this.abortSignal,\n };\n\n if (task.type === 'Insight') {\n assert(\n task.subType === 'Query' ||\n task.subType === 'Assert' ||\n task.subType === 'WaitFor' ||\n task.subType === 'Boolean' ||\n task.subType === 'Number' ||\n task.subType === 'String',\n `unsupported service subType: ${task.subType}`,\n );\n returnValue = await task.executor(executorContext);\n } else if (task.type === 'Planning') {\n returnValue = await task.executor(executorContext);\n if (task.subType === 'Locate') {\n previousFindOutput = (\n returnValue as ExecutionTaskReturn<ExecutionTaskPlanningLocateOutput>\n )?.output;\n }\n } else if (task.type === 'Action Space') {\n try {\n returnValue = await task.executor(executorContext);\n } finally {\n // The short TTL still lets planning and the action itself share one\n // context. Once an action settles, time alone can no longer prove\n // that the cached context represents the current UI.\n this.lastUiContext = undefined;\n }\n } else {\n console.warn(\n `unsupported task type: ${task.type}, will try to execute it directly`,\n );\n returnValue = await task.executor(executorContext);\n }\n\n this.abortSignal?.throwIfAborted();\n const isLastTask = taskIndex === this.tasks.length - 1;\n\n if (isLastTask) {\n setTimingFieldOnce(task.timing, 'captureAfterCallingSnapshotStart');\n const screenshot = await this.captureScreenshot();\n this.abortSignal?.throwIfAborted();\n this.attachRecorderItem(task, screenshot, 'after-calling');\n setTimingFieldOnce(task.timing, 'captureAfterCallingSnapshotEnd');\n }\n // Cancellation may arrive during the report screenshot, after planning\n // has returned but before the next batch of actions is built.\n this.abortSignal?.throwIfAborted();\n\n Object.assign(task, returnValue);\n task.status = 'finished';\n task.timing.end = Date.now();\n task.timing.cost = task.timing.end - task.timing.start;\n await this.emitSnapshotChange();\n await this.emitTaskEvent('finish', task);\n taskIndex++;\n } catch (error) {\n successfullyCompleted = false;\n const serializedError = serializeError(error);\n task.error = serializedError;\n task.errorMessage = serializedError.message;\n task.errorStack = serializedError.stack;\n\n task.status = taskStarted ? 'failed' : 'cancelled';\n if (taskStarted && task.timing) {\n task.timing.end = Date.now();\n task.timing.cost = task.timing.end - task.timing.start;\n }\n await this.emitSnapshotChange();\n await this.emitTaskEvent(taskStarted ? 'error' : 'cancel', task);\n break;\n }\n }\n\n // set all remaining tasks as cancelled\n const cancelledTasks: ExecutionTask[] = [];\n for (let i = taskIndex + 1; i < this.tasks.length; i++) {\n this.tasks[i].status = 'cancelled';\n cancelledTasks.push(this.tasks[i]);\n }\n if (cancelledTasks.length > 0) {\n await this.emitSnapshotChange();\n for (const cancelledTask of cancelledTasks) {\n await this.emitTaskEvent('cancel', cancelledTask);\n }\n }\n\n let finalizeError: TaskExecutionError | undefined;\n if (!successfullyCompleted) {\n this.status = 'error';\n const errorTask = this.latestErrorTask();\n const error =\n errorTask?.error ??\n (this.abortSignal?.aborted\n ? serializeError(this.abortSignal.reason)\n : {\n name: 'Error',\n message: 'Task execution failed',\n });\n finalizeError = new TaskExecutionError(\n error,\n serializeErrorTask(errorTask),\n );\n await this.emitSnapshotChange(finalizeError);\n } else {\n this.status = 'completed';\n await this.emitSnapshotChange();\n }\n\n if (finalizeError) {\n throw finalizeError;\n }\n\n if (this.tasks.length) {\n // return the last output\n const outputIndex = Math.min(taskIndex, this.tasks.length - 1);\n const { thought, output } = this.tasks[outputIndex];\n return {\n thought,\n output,\n };\n }\n }\n\n isInErrorState(): boolean {\n return this.status === 'error';\n }\n\n latestErrorTask(): ExecutionTask | null {\n if (this.status !== 'error') {\n return null;\n }\n // Find the LAST failed task (not the first one)\n // This is important when using allowWhenError to continue after errors\n for (let i = this.tasks.length - 1; i >= 0; i--) {\n if (this.tasks[i].status === 'failed') {\n return this.tasks[i];\n }\n }\n return null;\n }\n\n dump(): ExecutionDump {\n return new ExecutionDump(\n {\n id: this.id,\n logTime: this.executionLogTime,\n name: this.name,\n tasks: this.tasks,\n },\n { referenceImageUrls: [...this.referenceImageUrls] },\n );\n }\n\n async appendErrorPlan(errorMsg: string): Promise<{\n output: undefined;\n runner: TaskRunner;\n }> {\n const errorTask: ExecutionTaskActionApply<PlanningActionParamError> = {\n type: 'Action Space',\n subType: 'Error',\n param: {\n thought: errorMsg,\n },\n thought: errorMsg,\n executor: async () => {\n throw new Error(errorMsg || 'error without thought');\n },\n };\n await this.appendAndFlush(errorTask);\n\n return {\n output: undefined,\n runner: this,\n };\n }\n}\n\nexport class TaskExecutionError extends Error {\n readonly code = 'TASK_EXECUTION_FAILED' as const;\n\n override readonly cause: SerializedError;\n\n readonly task?: SerializedErrorTask;\n\n constructor(error: SerializedError, task?: SerializedErrorTask) {\n super(error.message, { cause: error });\n this.name = 'TaskExecutionError';\n this.cause = error;\n this.task = task;\n if (this.stack) {\n this.stack = truncateSerializedErrorString(this.stack);\n }\n }\n\n /**\n * Return the same bounded fields already stored on the live error. The\n * wrapper stack identifies where TaskExecutionError was created; cause.stack\n * preserves the executor's original stack.\n */\n toJSON(): SerializedTaskExecutionError {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n ...(this.stack === undefined ? {} : { stack: this.stack }),\n cause: this.cause,\n ...(this.task === undefined ? {} : { task: this.task }),\n };\n }\n}\n"],"names":["debug","getDebug","UI_CONTEXT_CACHE_TTL_MS","serializeErrorTask","task","truncateSerializedErrorString","undefined","TaskRunner","error","kind","console","options","now","Date","shouldReuse","uiContext","screenshot","phase","recorderItem","uuid","errorMessage","assert","appended","Array","item","appendedTask","nextPendingIndex","taskIndex","successfullyCompleted","previousFindOutput","taskStarted","e","executor","returnValue","forceRefresh","setTimingFieldOnce","executorContext","isLastTask","Object","serializedError","serializeError","cancelledTasks","i","cancelledTask","finalizeError","errorTask","TaskExecutionError","outputIndex","Math","thought","output","ExecutionDump","errorMsg","Error","name","uiContextBuilder","Set","image"],"mappings":"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"}
1
+ {"version":3,"file":"task-runner.mjs","sources":["../../src/task-runner.ts"],"sourcesContent":["import type { ScreenshotItem } from '@/screenshot-item';\nimport { setTimingFieldOnce } from '@/task-timing';\nimport {\n ExecutionDump,\n type ExecutionRecorderItem,\n type ExecutionTask,\n type ExecutionTaskActionApply,\n type ExecutionTaskApply,\n type ExecutionTaskPlanningLocateOutput,\n type ExecutionTaskProgressOptions,\n type ExecutionTaskReturn,\n type ExecutorContext,\n type PlanningActionParamError,\n type UIContext,\n} from '@/types';\nimport {\n type SerializedError,\n serializeError,\n truncateSerializedErrorString,\n} from '@midscene/shared/agent-tools/error-formatter';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, uuid } from '@midscene/shared/utils';\n\nconst debug = getDebug('task-runner');\nconst UI_CONTEXT_CACHE_TTL_MS = 300;\n\nfunction serializeTaskError(\n error: unknown,\n signal?: AbortSignal,\n): SerializedError {\n const serializedError = serializeError(error);\n // Do not relabel an unrelated error that races with cancellation.\n // Preserve the original stack and diagnostics alongside the explanation.\n if (signal?.aborted && error === signal.reason) {\n serializedError.message = truncateSerializedErrorString(\n `Operation interrupted by an external abort signal.\\nReason: ${serializedError.message}`,\n );\n }\n return serializedError;\n}\n\n/**\n * A native, per-task lifecycle notification. The runner is the single source\n * of truth for when a task actually transitions, so it reports each transition\n * exactly once with the task that changed. Consumers no longer have to re-scan\n * the task list and diff it against remembered keys to reconstruct this stream.\n */\nexport type TaskRunnerEventKind =\n | 'append'\n | 'start'\n | 'finish'\n | 'error'\n | 'cancel';\n\nexport interface TaskRunnerEvent {\n kind: TaskRunnerEventKind;\n task: ExecutionTask;\n runner: TaskRunner;\n}\n\nexport interface SerializedErrorTask {\n taskId: string;\n type: ExecutionTask['type'];\n subType?: string;\n status: ExecutionTask['status'];\n thought?: string;\n errorMessage?: string;\n}\n\nexport interface SerializedTaskExecutionError extends SerializedError {\n code: 'TASK_EXECUTION_FAILED';\n cause: SerializedError;\n task?: SerializedErrorTask;\n}\n\nfunction serializeErrorTask(\n task: ExecutionTask | null,\n): SerializedErrorTask | undefined {\n if (!task) {\n return undefined;\n }\n\n return {\n taskId: truncateSerializedErrorString(task.taskId),\n type: task.type,\n ...(task.subType === undefined\n ? {}\n : { subType: truncateSerializedErrorString(task.subType) }),\n status: task.status,\n ...(task.thought === undefined\n ? {}\n : { thought: truncateSerializedErrorString(task.thought) }),\n ...(task.errorMessage === undefined\n ? {}\n : {\n errorMessage: truncateSerializedErrorString(task.errorMessage),\n }),\n };\n}\n\nexport type TaskRunnerEventListener = (\n event: TaskRunnerEvent,\n) => Promise<void> | void;\n\n/** Minimal prompt-image shape retained as report asset metadata. */\nexport interface ExecutionReferenceImage {\n readonly url: string;\n}\n\ntype TaskRunnerInitOptions = ExecutionTaskProgressOptions & {\n tasks?: ExecutionTaskApply[];\n abortSignal?: AbortSignal;\n referenceImages?: readonly ExecutionReferenceImage[];\n /**\n * Coarse \"the execution snapshot changed\" signal. Fires on any state change\n * (append, status flips, completion) with the whole runner, so consumers can\n * re-dump/re-render the current snapshot. Deliberately batch-granular, unlike\n * the per-task {@link onTaskEvent} stream.\n */\n onSnapshotChange?: (\n runner: TaskRunner,\n error?: TaskExecutionError,\n ) => Promise<void> | void;\n onTaskEvent?: TaskRunnerEventListener;\n};\n\ntype TaskRunnerOperationOptions = {\n allowWhenError?: boolean;\n};\n\nexport class TaskRunner {\n readonly id: string;\n name: string;\n\n tasks: ExecutionTask[];\n\n // status of runner\n status: 'init' | 'pending' | 'running' | 'completed' | 'error';\n\n onTaskStart?: ExecutionTaskProgressOptions['onTaskStart'];\n\n private readonly uiContextBuilder: (\n signal?: AbortSignal,\n ) => Promise<UIContext>;\n private readonly abortSignal?: AbortSignal;\n\n private readonly onSnapshotChange?:\n | ((runner: TaskRunner, error?: TaskExecutionError) => Promise<void> | void)\n | undefined;\n\n private readonly onTaskEvent?: TaskRunnerEventListener | undefined;\n\n private readonly executionLogTime: number;\n\n private readonly referenceImageUrls = new Set<string>();\n\n constructor(\n name: string,\n uiContextBuilder: (signal?: AbortSignal) => Promise<UIContext>,\n options?: TaskRunnerInitOptions,\n ) {\n this.id = uuid();\n this.status =\n options?.tasks && options.tasks.length > 0 ? 'pending' : 'init';\n this.name = name;\n this.tasks = (options?.tasks || []).map((item) =>\n this.markTaskAsPending(item),\n );\n this.onTaskStart = options?.onTaskStart;\n this.uiContextBuilder = uiContextBuilder;\n this.abortSignal = options?.abortSignal;\n this.onSnapshotChange = options?.onSnapshotChange;\n this.onTaskEvent = options?.onTaskEvent;\n this.executionLogTime = Date.now();\n for (const image of options?.referenceImages ?? []) {\n this.referenceImageUrls.add(image.url);\n }\n }\n\n private async emitSnapshotChange(error?: TaskExecutionError): Promise<void> {\n if (!this.onSnapshotChange) {\n return;\n }\n await this.onSnapshotChange(this, error);\n }\n\n private async emitTaskEvent(\n kind: TaskRunnerEventKind,\n task: ExecutionTask,\n ): Promise<void> {\n if (!this.onTaskEvent) {\n return;\n }\n try {\n await this.onTaskEvent({ kind, task, runner: this });\n } catch (error) {\n console.error('Error in onTaskEvent listener', error);\n }\n }\n\n private lastUiContext?: {\n context: UIContext;\n capturedAt: number;\n };\n\n private async getUiContext(options?: { forceRefresh?: boolean }): Promise<\n UIContext | undefined\n > {\n this.abortSignal?.throwIfAborted();\n const now = Date.now();\n const shouldReuse =\n !options?.forceRefresh &&\n this.lastUiContext &&\n now - this.lastUiContext.capturedAt <= UI_CONTEXT_CACHE_TTL_MS;\n\n if (shouldReuse && this.lastUiContext?.context) {\n debug(\n `reuse cached uiContext captured ${now - this.lastUiContext.capturedAt}ms ago`,\n );\n return this.lastUiContext?.context;\n }\n\n try {\n const uiContext = await this.uiContextBuilder(this.abortSignal);\n this.abortSignal?.throwIfAborted();\n if (uiContext) {\n this.lastUiContext = {\n context: uiContext,\n capturedAt: Date.now(),\n };\n } else {\n this.lastUiContext = undefined;\n }\n return uiContext;\n } catch (error) {\n this.lastUiContext = undefined;\n throw error;\n }\n }\n\n private async captureScreenshot(): Promise<ScreenshotItem | undefined> {\n try {\n const uiContext = await this.getUiContext({ forceRefresh: true });\n return uiContext?.screenshot;\n } catch (error) {\n console.error('error while capturing screenshot', error);\n }\n return undefined;\n }\n\n private attachRecorderItem(\n task: ExecutionTask,\n screenshot: ScreenshotItem | undefined,\n phase: 'after-calling',\n ): void {\n if (!phase || !screenshot) {\n return;\n }\n\n const recorderItem: ExecutionRecorderItem = {\n type: 'screenshot',\n ts: Date.now(),\n screenshot,\n timing: phase,\n };\n\n if (!task.recorder) {\n task.recorder = [recorderItem];\n return;\n }\n task.recorder.push(recorderItem);\n }\n\n private markTaskAsPending(task: ExecutionTaskApply): ExecutionTask {\n return {\n taskId: uuid(),\n status: 'pending',\n ...task,\n };\n }\n\n private normalizeStatusFromError(\n options?: TaskRunnerOperationOptions,\n errorMessage?: string,\n ): void {\n if (this.status !== 'error') {\n return;\n }\n assert(\n options?.allowWhenError,\n errorMessage ||\n `task runner is in error state, cannot proceed\\nerror=${this.latestErrorTask()?.errorMessage}\\n${this.latestErrorTask()?.errorStack}`,\n );\n // reset runner state so new tasks can run\n this.status = this.tasks.length > 0 ? 'pending' : 'init';\n }\n\n async append(\n task: ExecutionTaskApply[] | ExecutionTaskApply,\n options?: TaskRunnerOperationOptions,\n ): Promise<void> {\n this.abortSignal?.throwIfAborted();\n this.normalizeStatusFromError(\n options,\n `task runner is in error state, cannot append task\\nerror=${this.latestErrorTask()?.errorMessage}\\n${this.latestErrorTask()?.errorStack}`,\n );\n const appended: ExecutionTask[] = [];\n if (Array.isArray(task)) {\n appended.push(...task.map((item) => this.markTaskAsPending(item)));\n } else {\n appended.push(this.markTaskAsPending(task));\n }\n this.tasks.push(...appended);\n if (this.status !== 'running') {\n this.status = 'pending';\n }\n await this.emitSnapshotChange();\n for (const appendedTask of appended) {\n await this.emitTaskEvent('append', appendedTask);\n }\n }\n\n async appendAndFlush(\n task: ExecutionTaskApply[] | ExecutionTaskApply,\n options?: TaskRunnerOperationOptions,\n ): Promise<{ output: any; thought?: string } | undefined> {\n await this.append(task, options);\n return this.flush(options);\n }\n\n async flush(\n options?: TaskRunnerOperationOptions,\n ): Promise<{ output: any; thought?: string } | undefined> {\n if (this.status === 'init' && this.tasks.length > 0) {\n console.warn(\n 'illegal state for task runner, status is init but tasks are not empty',\n );\n }\n\n this.normalizeStatusFromError(options, 'task runner is in error state');\n assert(this.status !== 'running', 'task runner is already running');\n assert(this.status !== 'completed', 'task runner is already completed');\n\n const nextPendingIndex = this.tasks.findIndex(\n (task) => task.status === 'pending',\n );\n if (nextPendingIndex < 0) {\n // all tasks are completed\n return;\n }\n\n this.status = 'running';\n await this.emitSnapshotChange();\n let taskIndex = nextPendingIndex;\n let successfullyCompleted = true;\n\n let previousFindOutput: ExecutionTaskPlanningLocateOutput | undefined;\n\n while (taskIndex < this.tasks.length) {\n const task = this.tasks[taskIndex];\n assert(\n task.status === 'pending',\n `task status should be pending, but got: ${task.status}`,\n );\n let taskStarted = false;\n try {\n this.abortSignal?.throwIfAborted();\n taskStarted = true;\n task.timing = { start: Date.now() };\n task.status = 'running';\n await this.emitSnapshotChange();\n await this.emitTaskEvent('start', task);\n this.abortSignal?.throwIfAborted();\n try {\n if (this.onTaskStart) {\n await this.onTaskStart(task, this.abortSignal);\n }\n } catch (e) {\n console.error('error in onTaskStart', e);\n }\n this.abortSignal?.throwIfAborted();\n assert(\n ['Insight', 'Action Space', 'Planning'].indexOf(task.type) >= 0,\n `unsupported task type: ${task.type}`,\n );\n\n const { executor } = task;\n assert(executor, `executor is required for task type: ${task.type}`);\n\n let returnValue;\n // For Insight tasks (Query/Assert/WaitFor), always get fresh context\n // to ensure we have the latest UI state after any preceding actions\n const forceRefresh = task.type === 'Insight';\n setTimingFieldOnce(task.timing, 'getUiContextStart');\n const uiContext = await this.getUiContext({ forceRefresh });\n setTimingFieldOnce(task.timing, 'getUiContextEnd');\n this.abortSignal?.throwIfAborted();\n\n task.uiContext = uiContext;\n const executorContext: ExecutorContext = {\n task,\n element: previousFindOutput?.element,\n uiContext,\n signal: this.abortSignal,\n };\n\n if (task.type === 'Insight') {\n assert(\n task.subType === 'Query' ||\n task.subType === 'Assert' ||\n task.subType === 'WaitFor' ||\n task.subType === 'Boolean' ||\n task.subType === 'Number' ||\n task.subType === 'String',\n `unsupported service subType: ${task.subType}`,\n );\n returnValue = await task.executor(executorContext);\n } else if (task.type === 'Planning') {\n returnValue = await task.executor(executorContext);\n if (task.subType === 'Locate') {\n previousFindOutput = (\n returnValue as ExecutionTaskReturn<ExecutionTaskPlanningLocateOutput>\n )?.output;\n }\n } else if (task.type === 'Action Space') {\n try {\n returnValue = await task.executor(executorContext);\n } finally {\n // The short TTL still lets planning and the action itself share one\n // context. Once an action settles, time alone can no longer prove\n // that the cached context represents the current UI.\n this.lastUiContext = undefined;\n }\n } else {\n console.warn(\n `unsupported task type: ${task.type}, will try to execute it directly`,\n );\n returnValue = await task.executor(executorContext);\n }\n\n this.abortSignal?.throwIfAborted();\n const isLastTask = taskIndex === this.tasks.length - 1;\n\n if (isLastTask) {\n setTimingFieldOnce(task.timing, 'captureAfterCallingSnapshotStart');\n const screenshot = await this.captureScreenshot();\n this.abortSignal?.throwIfAborted();\n this.attachRecorderItem(task, screenshot, 'after-calling');\n setTimingFieldOnce(task.timing, 'captureAfterCallingSnapshotEnd');\n }\n // Cancellation may arrive during the report screenshot, after planning\n // has returned but before the next batch of actions is built.\n this.abortSignal?.throwIfAborted();\n\n Object.assign(task, returnValue);\n task.status = 'finished';\n task.timing.end = Date.now();\n task.timing.cost = task.timing.end - task.timing.start;\n await this.emitSnapshotChange();\n await this.emitTaskEvent('finish', task);\n taskIndex++;\n } catch (error) {\n successfullyCompleted = false;\n const serializedError = serializeTaskError(error, this.abortSignal);\n task.error = serializedError;\n task.errorMessage = serializedError.message;\n task.errorStack = serializedError.stack;\n\n task.status = taskStarted ? 'failed' : 'cancelled';\n if (taskStarted && task.timing) {\n task.timing.end = Date.now();\n task.timing.cost = task.timing.end - task.timing.start;\n }\n await this.emitSnapshotChange();\n await this.emitTaskEvent(taskStarted ? 'error' : 'cancel', task);\n break;\n }\n }\n\n // set all remaining tasks as cancelled\n const cancelledTasks: ExecutionTask[] = [];\n for (let i = taskIndex + 1; i < this.tasks.length; i++) {\n this.tasks[i].status = 'cancelled';\n cancelledTasks.push(this.tasks[i]);\n }\n if (cancelledTasks.length > 0) {\n await this.emitSnapshotChange();\n for (const cancelledTask of cancelledTasks) {\n await this.emitTaskEvent('cancel', cancelledTask);\n }\n }\n\n let finalizeError: TaskExecutionError | undefined;\n if (!successfullyCompleted) {\n this.status = 'error';\n const errorTask = this.latestErrorTask();\n const error =\n errorTask?.error ??\n (this.abortSignal?.aborted\n ? serializeTaskError(this.abortSignal.reason, this.abortSignal)\n : {\n name: 'Error',\n message: 'Task execution failed',\n });\n finalizeError = new TaskExecutionError(\n error,\n serializeErrorTask(errorTask),\n );\n await this.emitSnapshotChange(finalizeError);\n } else {\n this.status = 'completed';\n await this.emitSnapshotChange();\n }\n\n if (finalizeError) {\n throw finalizeError;\n }\n\n if (this.tasks.length) {\n // return the last output\n const outputIndex = Math.min(taskIndex, this.tasks.length - 1);\n const { thought, output } = this.tasks[outputIndex];\n return {\n thought,\n output,\n };\n }\n }\n\n isInErrorState(): boolean {\n return this.status === 'error';\n }\n\n latestErrorTask(): ExecutionTask | null {\n if (this.status !== 'error') {\n return null;\n }\n // Find the LAST failed task (not the first one)\n // This is important when using allowWhenError to continue after errors\n for (let i = this.tasks.length - 1; i >= 0; i--) {\n if (this.tasks[i].status === 'failed') {\n return this.tasks[i];\n }\n }\n return null;\n }\n\n dump(): ExecutionDump {\n return new ExecutionDump(\n {\n id: this.id,\n logTime: this.executionLogTime,\n name: this.name,\n tasks: this.tasks,\n },\n { referenceImageUrls: [...this.referenceImageUrls] },\n );\n }\n\n async appendErrorPlan(errorMsg: string): Promise<{\n output: undefined;\n runner: TaskRunner;\n }> {\n const errorTask: ExecutionTaskActionApply<PlanningActionParamError> = {\n type: 'Action Space',\n subType: 'Error',\n param: {\n thought: errorMsg,\n },\n thought: errorMsg,\n executor: async () => {\n throw new Error(errorMsg || 'error without thought');\n },\n };\n await this.appendAndFlush(errorTask);\n\n return {\n output: undefined,\n runner: this,\n };\n }\n}\n\nexport class TaskExecutionError extends Error {\n readonly code = 'TASK_EXECUTION_FAILED' as const;\n\n override readonly cause: SerializedError;\n\n readonly task?: SerializedErrorTask;\n\n constructor(error: SerializedError, task?: SerializedErrorTask) {\n super(error.message, { cause: error });\n this.name = 'TaskExecutionError';\n this.cause = error;\n this.task = task;\n if (this.stack) {\n this.stack = truncateSerializedErrorString(this.stack);\n }\n }\n\n /**\n * Return the same bounded fields already stored on the live error. The\n * wrapper stack identifies where TaskExecutionError was created; cause.stack\n * preserves the executor's original stack.\n */\n toJSON(): SerializedTaskExecutionError {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n ...(this.stack === undefined ? {} : { stack: this.stack }),\n cause: this.cause,\n ...(this.task === undefined ? {} : { task: this.task }),\n };\n }\n}\n"],"names":["debug","getDebug","UI_CONTEXT_CACHE_TTL_MS","serializeTaskError","error","signal","serializedError","serializeError","truncateSerializedErrorString","serializeErrorTask","task","undefined","TaskRunner","kind","console","options","now","Date","shouldReuse","uiContext","screenshot","phase","recorderItem","uuid","errorMessage","assert","appended","Array","item","appendedTask","nextPendingIndex","taskIndex","successfullyCompleted","previousFindOutput","taskStarted","e","executor","returnValue","forceRefresh","setTimingFieldOnce","executorContext","isLastTask","Object","cancelledTasks","i","cancelledTask","finalizeError","errorTask","TaskExecutionError","outputIndex","Math","thought","output","ExecutionDump","errorMsg","Error","name","uiContextBuilder","Set","image"],"mappings":";;;;;;;;;;;;;;;AAuBA,MAAMA,QAAQC,SAAS;AACvB,MAAMC,0BAA0B;AAEhC,SAASC,mBACPC,KAAc,EACdC,MAAoB;IAEpB,MAAMC,kBAAkBC,eAAeH;IAGvC,IAAIC,QAAQ,WAAWD,UAAUC,OAAO,MAAM,EAC5CC,gBAAgB,OAAO,GAAGE,8BACxB,CAAC,4DAA4D,EAAEF,gBAAgB,OAAO,EAAE;IAG5F,OAAOA;AACT;AAoCA,SAASG,mBACPC,IAA0B;IAE1B,IAAI,CAACA,MACH;IAGF,OAAO;QACL,QAAQF,8BAA8BE,KAAK,MAAM;QACjD,MAAMA,KAAK,IAAI;QACf,GAAIA,AAAiBC,WAAjBD,KAAK,OAAO,GACZ,CAAC,IACD;YAAE,SAASF,8BAA8BE,KAAK,OAAO;QAAE,CAAC;QAC5D,QAAQA,KAAK,MAAM;QACnB,GAAIA,AAAiBC,WAAjBD,KAAK,OAAO,GACZ,CAAC,IACD;YAAE,SAASF,8BAA8BE,KAAK,OAAO;QAAE,CAAC;QAC5D,GAAIA,AAAsBC,WAAtBD,KAAK,YAAY,GACjB,CAAC,IACD;YACE,cAAcF,8BAA8BE,KAAK,YAAY;QAC/D,CAAC;IACP;AACF;AAgCO,MAAME;IAiDX,MAAc,mBAAmBR,KAA0B,EAAiB;QAC1E,IAAI,CAAC,IAAI,CAAC,gBAAgB,EACxB;QAEF,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAEA;IACpC;IAEA,MAAc,cACZS,IAAyB,EACzBH,IAAmB,EACJ;QACf,IAAI,CAAC,IAAI,CAAC,WAAW,EACnB;QAEF,IAAI;YACF,MAAM,IAAI,CAAC,WAAW,CAAC;gBAAEG;gBAAMH;gBAAM,QAAQ,IAAI;YAAC;QACpD,EAAE,OAAON,OAAO;YACdU,QAAQ,KAAK,CAAC,iCAAiCV;QACjD;IACF;IAOA,MAAc,aAAaW,OAAoC,EAE7D;QACA,IAAI,CAAC,WAAW,EAAE;QAClB,MAAMC,MAAMC,KAAK,GAAG;QACpB,MAAMC,cACJ,CAACH,SAAS,gBACV,IAAI,CAAC,aAAa,IAClBC,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,IAAId;QAEzC,IAAIgB,eAAe,IAAI,CAAC,aAAa,EAAE,SAAS;YAC9ClB,MACE,CAAC,gCAAgC,EAAEgB,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC;YAEhF,OAAO,IAAI,CAAC,aAAa,EAAE;QAC7B;QAEA,IAAI;YACF,MAAMG,YAAY,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW;YAC9D,IAAI,CAAC,WAAW,EAAE;YAClB,IAAIA,WACF,IAAI,CAAC,aAAa,GAAG;gBACnB,SAASA;gBACT,YAAYF,KAAK,GAAG;YACtB;iBAEA,IAAI,CAAC,aAAa,GAAGN;YAEvB,OAAOQ;QACT,EAAE,OAAOf,OAAO;YACd,IAAI,CAAC,aAAa,GAAGO;YACrB,MAAMP;QACR;IACF;IAEA,MAAc,oBAAyD;QACrE,IAAI;YACF,MAAMe,YAAY,MAAM,IAAI,CAAC,YAAY,CAAC;gBAAE,cAAc;YAAK;YAC/D,OAAOA,WAAW;QACpB,EAAE,OAAOf,OAAO;YACdU,QAAQ,KAAK,CAAC,oCAAoCV;QACpD;IAEF;IAEQ,mBACNM,IAAmB,EACnBU,UAAsC,EACtCC,KAAsB,EAChB;QACN,IAAI,CAACA,SAAS,CAACD,YACb;QAGF,MAAME,eAAsC;YAC1C,MAAM;YACN,IAAIL,KAAK,GAAG;YACZG;YACA,QAAQC;QACV;QAEA,IAAI,CAACX,KAAK,QAAQ,EAAE;YAClBA,KAAK,QAAQ,GAAG;gBAACY;aAAa;YAC9B;QACF;QACAZ,KAAK,QAAQ,CAAC,IAAI,CAACY;IACrB;IAEQ,kBAAkBZ,IAAwB,EAAiB;QACjE,OAAO;YACL,QAAQa;YACR,QAAQ;YACR,GAAGb,IAAI;QACT;IACF;IAEQ,yBACNK,OAAoC,EACpCS,YAAqB,EACf;QACN,IAAI,AAAgB,YAAhB,IAAI,CAAC,MAAM,EACb;QAEFC,OACEV,SAAS,gBACTS,gBACE,CAAC,qDAAqD,EAAE,IAAI,CAAC,eAAe,IAAI,aAAa,EAAE,EAAE,IAAI,CAAC,eAAe,IAAI,YAAY;QAGzI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,YAAY;IACpD;IAEA,MAAM,OACJd,IAA+C,EAC/CK,OAAoC,EACrB;QACf,IAAI,CAAC,WAAW,EAAE;QAClB,IAAI,CAAC,wBAAwB,CAC3BA,SACA,CAAC,yDAAyD,EAAE,IAAI,CAAC,eAAe,IAAI,aAAa,EAAE,EAAE,IAAI,CAAC,eAAe,IAAI,YAAY;QAE3I,MAAMW,WAA4B,EAAE;QACpC,IAAIC,MAAM,OAAO,CAACjB,OAChBgB,SAAS,IAAI,IAAIhB,KAAK,GAAG,CAAC,CAACkB,OAAS,IAAI,CAAC,iBAAiB,CAACA;aAE3DF,SAAS,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAChB;QAEvC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAIgB;QACnB,IAAI,AAAgB,cAAhB,IAAI,CAAC,MAAM,EACb,IAAI,CAAC,MAAM,GAAG;QAEhB,MAAM,IAAI,CAAC,kBAAkB;QAC7B,KAAK,MAAMG,gBAAgBH,SACzB,MAAM,IAAI,CAAC,aAAa,CAAC,UAAUG;IAEvC;IAEA,MAAM,eACJnB,IAA+C,EAC/CK,OAAoC,EACoB;QACxD,MAAM,IAAI,CAAC,MAAM,CAACL,MAAMK;QACxB,OAAO,IAAI,CAAC,KAAK,CAACA;IACpB;IAEA,MAAM,MACJA,OAAoC,EACoB;QACxD,IAAI,AAAgB,WAAhB,IAAI,CAAC,MAAM,IAAe,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAChDD,QAAQ,IAAI,CACV;QAIJ,IAAI,CAAC,wBAAwB,CAACC,SAAS;QACvCU,OAAO,AAAgB,cAAhB,IAAI,CAAC,MAAM,EAAgB;QAClCA,OAAO,AAAgB,gBAAhB,IAAI,CAAC,MAAM,EAAkB;QAEpC,MAAMK,mBAAmB,IAAI,CAAC,KAAK,CAAC,SAAS,CAC3C,CAACpB,OAASA,AAAgB,cAAhBA,KAAK,MAAM;QAEvB,IAAIoB,mBAAmB,GAErB;QAGF,IAAI,CAAC,MAAM,GAAG;QACd,MAAM,IAAI,CAAC,kBAAkB;QAC7B,IAAIC,YAAYD;QAChB,IAAIE,wBAAwB;QAE5B,IAAIC;QAEJ,MAAOF,YAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAE;YACpC,MAAMrB,OAAO,IAAI,CAAC,KAAK,CAACqB,UAAU;YAClCN,OACEf,AAAgB,cAAhBA,KAAK,MAAM,EACX,CAAC,wCAAwC,EAAEA,KAAK,MAAM,EAAE;YAE1D,IAAIwB,cAAc;YAClB,IAAI;gBACF,IAAI,CAAC,WAAW,EAAE;gBAClBA,cAAc;gBACdxB,KAAK,MAAM,GAAG;oBAAE,OAAOO,KAAK,GAAG;gBAAG;gBAClCP,KAAK,MAAM,GAAG;gBACd,MAAM,IAAI,CAAC,kBAAkB;gBAC7B,MAAM,IAAI,CAAC,aAAa,CAAC,SAASA;gBAClC,IAAI,CAAC,WAAW,EAAE;gBAClB,IAAI;oBACF,IAAI,IAAI,CAAC,WAAW,EAClB,MAAM,IAAI,CAAC,WAAW,CAACA,MAAM,IAAI,CAAC,WAAW;gBAEjD,EAAE,OAAOyB,GAAG;oBACVrB,QAAQ,KAAK,CAAC,wBAAwBqB;gBACxC;gBACA,IAAI,CAAC,WAAW,EAAE;gBAClBV,OACE;oBAAC;oBAAW;oBAAgB;iBAAW,CAAC,OAAO,CAACf,KAAK,IAAI,KAAK,GAC9D,CAAC,uBAAuB,EAAEA,KAAK,IAAI,EAAE;gBAGvC,MAAM,EAAE0B,QAAQ,EAAE,GAAG1B;gBACrBe,OAAOW,UAAU,CAAC,oCAAoC,EAAE1B,KAAK,IAAI,EAAE;gBAEnE,IAAI2B;gBAGJ,MAAMC,eAAe5B,AAAc,cAAdA,KAAK,IAAI;gBAC9B6B,mBAAmB7B,KAAK,MAAM,EAAE;gBAChC,MAAMS,YAAY,MAAM,IAAI,CAAC,YAAY,CAAC;oBAAEmB;gBAAa;gBACzDC,mBAAmB7B,KAAK,MAAM,EAAE;gBAChC,IAAI,CAAC,WAAW,EAAE;gBAElBA,KAAK,SAAS,GAAGS;gBACjB,MAAMqB,kBAAmC;oBACvC9B;oBACA,SAASuB,oBAAoB;oBAC7Bd;oBACA,QAAQ,IAAI,CAAC,WAAW;gBAC1B;gBAEA,IAAIT,AAAc,cAAdA,KAAK,IAAI,EAAgB;oBAC3Be,OACEf,AAAiB,YAAjBA,KAAK,OAAO,IACVA,AAAiB,aAAjBA,KAAK,OAAO,IACZA,AAAiB,cAAjBA,KAAK,OAAO,IACZA,AAAiB,cAAjBA,KAAK,OAAO,IACZA,AAAiB,aAAjBA,KAAK,OAAO,IACZA,AAAiB,aAAjBA,KAAK,OAAO,EACd,CAAC,6BAA6B,EAAEA,KAAK,OAAO,EAAE;oBAEhD2B,cAAc,MAAM3B,KAAK,QAAQ,CAAC8B;gBACpC,OAAO,IAAI9B,AAAc,eAAdA,KAAK,IAAI,EAAiB;oBACnC2B,cAAc,MAAM3B,KAAK,QAAQ,CAAC8B;oBAClC,IAAI9B,AAAiB,aAAjBA,KAAK,OAAO,EACduB,qBACEI,aACC;gBAEP,OAAO,IAAI3B,AAAc,mBAAdA,KAAK,IAAI,EAClB,IAAI;oBACF2B,cAAc,MAAM3B,KAAK,QAAQ,CAAC8B;gBACpC,SAAU;oBAIR,IAAI,CAAC,aAAa,GAAG7B;gBACvB;qBACK;oBACLG,QAAQ,IAAI,CACV,CAAC,uBAAuB,EAAEJ,KAAK,IAAI,CAAC,iCAAiC,CAAC;oBAExE2B,cAAc,MAAM3B,KAAK,QAAQ,CAAC8B;gBACpC;gBAEA,IAAI,CAAC,WAAW,EAAE;gBAClB,MAAMC,aAAaV,cAAc,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG;gBAErD,IAAIU,YAAY;oBACdF,mBAAmB7B,KAAK,MAAM,EAAE;oBAChC,MAAMU,aAAa,MAAM,IAAI,CAAC,iBAAiB;oBAC/C,IAAI,CAAC,WAAW,EAAE;oBAClB,IAAI,CAAC,kBAAkB,CAACV,MAAMU,YAAY;oBAC1CmB,mBAAmB7B,KAAK,MAAM,EAAE;gBAClC;gBAGA,IAAI,CAAC,WAAW,EAAE;gBAElBgC,OAAO,MAAM,CAAChC,MAAM2B;gBACpB3B,KAAK,MAAM,GAAG;gBACdA,KAAK,MAAM,CAAC,GAAG,GAAGO,KAAK,GAAG;gBAC1BP,KAAK,MAAM,CAAC,IAAI,GAAGA,KAAK,MAAM,CAAC,GAAG,GAAGA,KAAK,MAAM,CAAC,KAAK;gBACtD,MAAM,IAAI,CAAC,kBAAkB;gBAC7B,MAAM,IAAI,CAAC,aAAa,CAAC,UAAUA;gBACnCqB;YACF,EAAE,OAAO3B,OAAO;gBACd4B,wBAAwB;gBACxB,MAAM1B,kBAAkBH,mBAAmBC,OAAO,IAAI,CAAC,WAAW;gBAClEM,KAAK,KAAK,GAAGJ;gBACbI,KAAK,YAAY,GAAGJ,gBAAgB,OAAO;gBAC3CI,KAAK,UAAU,GAAGJ,gBAAgB,KAAK;gBAEvCI,KAAK,MAAM,GAAGwB,cAAc,WAAW;gBACvC,IAAIA,eAAexB,KAAK,MAAM,EAAE;oBAC9BA,KAAK,MAAM,CAAC,GAAG,GAAGO,KAAK,GAAG;oBAC1BP,KAAK,MAAM,CAAC,IAAI,GAAGA,KAAK,MAAM,CAAC,GAAG,GAAGA,KAAK,MAAM,CAAC,KAAK;gBACxD;gBACA,MAAM,IAAI,CAAC,kBAAkB;gBAC7B,MAAM,IAAI,CAAC,aAAa,CAACwB,cAAc,UAAU,UAAUxB;gBAC3D;YACF;QACF;QAGA,MAAMiC,iBAAkC,EAAE;QAC1C,IAAK,IAAIC,IAAIb,YAAY,GAAGa,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAEA,IAAK;YACtD,IAAI,CAAC,KAAK,CAACA,EAAE,CAAC,MAAM,GAAG;YACvBD,eAAe,IAAI,CAAC,IAAI,CAAC,KAAK,CAACC,EAAE;QACnC;QACA,IAAID,eAAe,MAAM,GAAG,GAAG;YAC7B,MAAM,IAAI,CAAC,kBAAkB;YAC7B,KAAK,MAAME,iBAAiBF,eAC1B,MAAM,IAAI,CAAC,aAAa,CAAC,UAAUE;QAEvC;QAEA,IAAIC;QACJ,IAAKd,uBAgBE;YACL,IAAI,CAAC,MAAM,GAAG;YACd,MAAM,IAAI,CAAC,kBAAkB;QAC/B,OAnB4B;YAC1B,IAAI,CAAC,MAAM,GAAG;YACd,MAAMe,YAAY,IAAI,CAAC,eAAe;YACtC,MAAM3C,QACJ2C,WAAW,SACV,KAAI,CAAC,WAAW,EAAE,UACf5C,mBAAmB,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,IAC5D;gBACE,MAAM;gBACN,SAAS;YACX;YACN2C,gBAAgB,IAAIE,mBAClB5C,OACAK,mBAAmBsC;YAErB,MAAM,IAAI,CAAC,kBAAkB,CAACD;QAChC;QAKA,IAAIA,eACF,MAAMA;QAGR,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAErB,MAAMG,cAAcC,KAAK,GAAG,CAACnB,WAAW,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG;YAC5D,MAAM,EAAEoB,OAAO,EAAEC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAACH,YAAY;YACnD,OAAO;gBACLE;gBACAC;YACF;QACF;IACF;IAEA,iBAA0B;QACxB,OAAO,AAAgB,YAAhB,IAAI,CAAC,MAAM;IACpB;IAEA,kBAAwC;QACtC,IAAI,AAAgB,YAAhB,IAAI,CAAC,MAAM,EACb,OAAO;QAIT,IAAK,IAAIR,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAGA,KAAK,GAAGA,IAC1C,IAAI,AAAyB,aAAzB,IAAI,CAAC,KAAK,CAACA,EAAE,CAAC,MAAM,EACtB,OAAO,IAAI,CAAC,KAAK,CAACA,EAAE;QAGxB,OAAO;IACT;IAEA,OAAsB;QACpB,OAAO,IAAIS,cACT;YACE,IAAI,IAAI,CAAC,EAAE;YACX,SAAS,IAAI,CAAC,gBAAgB;YAC9B,MAAM,IAAI,CAAC,IAAI;YACf,OAAO,IAAI,CAAC,KAAK;QACnB,GACA;YAAE,oBAAoB;mBAAI,IAAI,CAAC,kBAAkB;aAAC;QAAC;IAEvD;IAEA,MAAM,gBAAgBC,QAAgB,EAGnC;QACD,MAAMP,YAAgE;YACpE,MAAM;YACN,SAAS;YACT,OAAO;gBACL,SAASO;YACX;YACA,SAASA;YACT,UAAU;gBACR,MAAM,IAAIC,MAAMD,YAAY;YAC9B;QACF;QACA,MAAM,IAAI,CAAC,cAAc,CAACP;QAE1B,OAAO;YACL,QAAQpC;YACR,QAAQ,IAAI;QACd;IACF;IAxaA,YACE6C,IAAY,EACZC,gBAA8D,EAC9D1C,OAA+B,CAC/B;QA7BF,uBAAS,MAAT;QACA;QAEA;QAGA;QAEA;QAEA,uBAAiB,oBAAjB;QAGA,uBAAiB,eAAjB;QAEA,uBAAiB,oBAAjB;QAIA,uBAAiB,eAAjB;QAEA,uBAAiB,oBAAjB;QAEA,uBAAiB,sBAAqB,IAAI2C;QA8C1C,uBAAQ,iBAAR;QAvCE,IAAI,CAAC,EAAE,GAAGnC;QACV,IAAI,CAAC,MAAM,GACTR,SAAS,SAASA,QAAQ,KAAK,CAAC,MAAM,GAAG,IAAI,YAAY;QAC3D,IAAI,CAAC,IAAI,GAAGyC;QACZ,IAAI,CAAC,KAAK,GAAIzC,AAAAA,CAAAA,SAAS,SAAS,EAAC,EAAG,GAAG,CAAC,CAACa,OACvC,IAAI,CAAC,iBAAiB,CAACA;QAEzB,IAAI,CAAC,WAAW,GAAGb,SAAS;QAC5B,IAAI,CAAC,gBAAgB,GAAG0C;QACxB,IAAI,CAAC,WAAW,GAAG1C,SAAS;QAC5B,IAAI,CAAC,gBAAgB,GAAGA,SAAS;QACjC,IAAI,CAAC,WAAW,GAAGA,SAAS;QAC5B,IAAI,CAAC,gBAAgB,GAAGE,KAAK,GAAG;QAChC,KAAK,MAAM0C,SAAS5C,SAAS,mBAAmB,EAAE,CAChD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC4C,MAAM,GAAG;IAEzC;AAoZF;AAEO,MAAMX,2BAA2BO;IAsBtC,SAAuC;QACrC,OAAO;YACL,MAAM,IAAI,CAAC,IAAI;YACf,MAAM,IAAI,CAAC,IAAI;YACf,SAAS,IAAI,CAAC,OAAO;YACrB,GAAI,AAAe5C,WAAf,IAAI,CAAC,KAAK,GAAiB,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,KAAK;YAAC,CAAC;YACzD,OAAO,IAAI,CAAC,KAAK;YACjB,GAAI,AAAcA,WAAd,IAAI,CAAC,IAAI,GAAiB,CAAC,IAAI;gBAAE,MAAM,IAAI,CAAC,IAAI;YAAC,CAAC;QACxD;IACF;IAxBA,YAAYP,KAAsB,EAAEM,IAA0B,CAAE;QAC9D,KAAK,CAACN,MAAM,OAAO,EAAE;YAAE,OAAOA;QAAM,IAPtC,uBAAS,QAAO,0BAEhB,uBAAkB,SAAlB,SAEA,uBAAS,QAAT;QAIE,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,KAAK,GAAGA;QACb,IAAI,CAAC,IAAI,GAAGM;QACZ,IAAI,IAAI,CAAC,KAAK,EACZ,IAAI,CAAC,KAAK,GAAGF,8BAA8B,IAAI,CAAC,KAAK;IAEzD;AAiBF"}
package/dist/es/utils.mjs CHANGED
@@ -184,7 +184,7 @@ function stringifyDumpData(data, indents) {
184
184
  return JSON.stringify(data, replacerForPageObject, indents);
185
185
  }
186
186
  function getVersion() {
187
- return "1.17.5";
187
+ return "1.17.7";
188
188
  }
189
189
  function debugLog(...message) {
190
190
  const debugMode = process.env[MIDSCENE_DEBUG_MODE];
@@ -86,7 +86,7 @@ class AgentReportManager {
86
86
  report.session = session;
87
87
  report.file = void 0;
88
88
  }
89
- report.generator.enableSourceSnapshots?.();
89
+ report.generator.enableSourceSnapshots?.(report.yamlScopeId);
90
90
  }
91
91
  async _createReportSource(scopeId, executionIds) {
92
92
  return this.reportState.generator.createSourceSnapshot?.(scopeId, executionIds);