@midscene/core 1.4.6 → 1.4.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.
- package/dist/es/agent/agent.mjs +4 -56
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/task-builder.mjs +4 -1
- package/dist/es/agent/task-builder.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +91 -18
- package/dist/es/agent/utils.mjs.map +1 -1
- package/dist/es/ai-model/auto-glm/planning.mjs +1 -1
- package/dist/es/ai-model/auto-glm/planning.mjs.map +1 -1
- package/dist/es/ai-model/inspect.mjs +5 -5
- package/dist/es/ai-model/inspect.mjs.map +1 -1
- package/dist/es/ai-model/llm-planning.mjs +3 -3
- package/dist/es/ai-model/llm-planning.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/index.mjs +71 -45
- package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
- package/dist/es/ai-model/ui-tars-planning.mjs +14 -14
- package/dist/es/ai-model/ui-tars-planning.mjs.map +1 -1
- package/dist/es/common.mjs +1 -14
- package/dist/es/common.mjs.map +1 -1
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/dump/html-utils.mjs +2 -1
- package/dist/es/dump/html-utils.mjs.map +1 -1
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-generator.mjs +2 -2
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report.mjs +39 -7
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/service/index.mjs +6 -6
- package/dist/es/service/index.mjs.map +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +2 -2
- package/dist/lib/agent/agent.js +4 -56
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/task-builder.js +4 -1
- package/dist/lib/agent/task-builder.js.map +1 -1
- package/dist/lib/agent/utils.js +96 -14
- package/dist/lib/agent/utils.js.map +1 -1
- package/dist/lib/ai-model/auto-glm/planning.js +1 -1
- package/dist/lib/ai-model/auto-glm/planning.js.map +1 -1
- package/dist/lib/ai-model/inspect.js +5 -5
- package/dist/lib/ai-model/inspect.js.map +1 -1
- package/dist/lib/ai-model/llm-planning.js +3 -3
- package/dist/lib/ai-model/llm-planning.js.map +1 -1
- package/dist/lib/ai-model/service-caller/index.js +73 -47
- package/dist/lib/ai-model/service-caller/index.js.map +1 -1
- package/dist/lib/ai-model/ui-tars-planning.js +14 -14
- package/dist/lib/ai-model/ui-tars-planning.js.map +1 -1
- package/dist/lib/common.js +1 -14
- package/dist/lib/common.js.map +1 -1
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/dump/html-utils.js +4 -0
- package/dist/lib/dump/html-utils.js.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-generator.js +1 -1
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report.js +36 -4
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/service/index.js +6 -6
- package/dist/lib/service/index.js.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/types/agent/agent.d.ts +1 -15
- package/dist/types/agent/utils.d.ts +13 -1
- package/dist/types/ai-model/index.d.ts +1 -1
- package/dist/types/ai-model/service-caller/index.d.ts +4 -2
- package/dist/types/common.d.ts +0 -310
- package/dist/types/device/index.d.ts +1 -2
- package/dist/types/dump/html-utils.d.ts +11 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/report.d.ts +5 -0
- package/dist/types/types.d.ts +2 -2
- package/package.json +2 -2
package/dist/es/report.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n rmSync,\n unlinkSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { logMsg } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent';\nimport {\n BASE_URL_FIX_SCRIPT,\n extractLastDumpScriptSync,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, reportHTMLContent } from './utils';\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n public append(reportInfo: ReportFileWithAttributes) {\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\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 private isDirectoryModeReport(reportFilePath: string): boolean {\n const reportDir = path.dirname(reportFilePath);\n return (\n path.basename(reportFilePath) === 'index.html' &&\n existsSync(path.join(reportDir, 'screenshots'))\n );\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts?: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n },\n ): string | null {\n if (this.reportInfos.length <= 1) {\n logMsg('Not enough reports to merge');\n return null;\n }\n\n const { rmOriginalReports = false, overwrite = false } = opts ?? {};\n const targetDir = getMidsceneRunSubDir('report');\n\n // Check if any source report is directory mode\n const hasDirectoryModeReport = this.reportInfos.some((info) =>\n this.isDirectoryModeReport(info.reportFilePath),\n );\n\n const resolvedName =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n\n // Directory mode: output as {name}/index.html to keep relative paths working\n // Inline mode: output as {name}.html (single file)\n const outputFilePath = hasDirectoryModeReport\n ? path.resolve(targetDir, resolvedName, 'index.html')\n : path.resolve(targetDir, `${resolvedName}.html`);\n\n if (reportFileName !== 'AUTO' && existsSync(outputFilePath)) {\n if (!overwrite) {\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n }\n if (hasDirectoryModeReport) {\n rmSync(path.dirname(outputFilePath), { recursive: true, force: true });\n } else {\n unlinkSync(outputFilePath);\n }\n }\n\n if (hasDirectoryModeReport) {\n mkdirSync(path.dirname(outputFilePath), { recursive: true });\n }\n\n logMsg(\n `Start merging ${this.reportInfos.length} reports...\\nCreating template file...`,\n );\n\n try {\n // Write template\n appendFileSync(outputFilePath, getReportTpl());\n\n // For directory-mode output, inject base URL fix script\n if (hasDirectoryModeReport) {\n appendFileSync(outputFilePath, BASE_URL_FIX_SCRIPT);\n }\n\n // Process all reports one by one\n for (let i = 0; i < this.reportInfos.length; i++) {\n const reportInfo = this.reportInfos[i];\n logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);\n\n if (this.isDirectoryModeReport(reportInfo.reportFilePath)) {\n // Directory mode: copy external screenshot files\n const reportDir = path.dirname(reportInfo.reportFilePath);\n const screenshotsDir = path.join(reportDir, 'screenshots');\n const mergedScreenshotsDir = path.join(\n path.dirname(outputFilePath),\n 'screenshots',\n );\n mkdirSync(mergedScreenshotsDir, { recursive: true });\n for (const file of readdirSync(screenshotsDir)) {\n const src = path.join(screenshotsDir, file);\n const dest = path.join(mergedScreenshotsDir, file);\n copyFileSync(src, dest);\n }\n } else {\n // Inline mode: stream image scripts to output file\n streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath);\n }\n\n const dumpString = extractLastDumpScriptSync(reportInfo.reportFilePath);\n const { reportAttributes } = reportInfo;\n\n const reportHtmlStr = `${reportHTMLContent(\n {\n dumpString,\n attributes: {\n playwright_test_duration: reportAttributes.testDuration,\n playwright_test_status: reportAttributes.testStatus,\n playwright_test_title: reportAttributes.testTitle,\n playwright_test_id: reportAttributes.testId,\n playwright_test_description: reportAttributes.testDescription,\n },\n },\n undefined,\n undefined,\n false,\n )}\\n`;\n\n appendFileSync(outputFilePath, reportHtmlStr);\n }\n\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n\n // Remove original reports if needed\n if (rmOriginalReports) {\n for (const info of this.reportInfos) {\n try {\n if (this.isDirectoryModeReport(info.reportFilePath)) {\n // Directory mode: remove the entire report directory\n const reportDir = path.dirname(info.reportFilePath);\n rmSync(reportDir, { recursive: true, force: true });\n } else {\n unlinkSync(info.reportFilePath);\n }\n } catch (error) {\n logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);\n }\n }\n logMsg(`Removed ${this.reportInfos.length} original reports`);\n }\n return outputFilePath;\n } catch (error) {\n logMsg(`Error in mergeReports: ${error}`);\n throw error;\n }\n }\n}\n"],"names":["ReportMergingTool","reportInfo","reportFilePath","reportDir","path","existsSync","reportFileName","opts","logMsg","rmOriginalReports","overwrite","targetDir","getMidsceneRunSubDir","hasDirectoryModeReport","info","resolvedName","getReportFileName","outputFilePath","Error","rmSync","unlinkSync","mkdirSync","appendFileSync","getReportTpl","BASE_URL_FIX_SCRIPT","i","screenshotsDir","mergedScreenshotsDir","file","readdirSync","src","dest","copyFileSync","streamImageScriptsToFile","dumpString","extractLastDumpScriptSync","reportAttributes","reportHtmlStr","reportHTMLContent","undefined","error"],"mappings":";;;;;;;;;;;;;;;;;AAqBO,MAAMA;IAEJ,OAAOC,UAAoC,EAAE;QAClD,IAAI,CAAC,WAAW,CAAC,IAAI,CAACA;IACxB;IACO,QAAQ;QACb,IAAI,CAAC,WAAW,GAAG,EAAE;IACvB;IAMQ,sBAAsBC,cAAsB,EAAW;QAC7D,MAAMC,YAAYC,QAAaF;QAC/B,OACEE,AAAkC,iBAAlCA,SAAcF,mBACdG,WAAWD,KAAUD,WAAW;IAEpC;IAEO,aACLG,iBAAkC,MAAM,EACxCC,IAGC,EACc;QACf,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,GAAG;YAChCC,OAAO;YACP,OAAO;QACT;QAEA,MAAM,EAAEC,oBAAoB,KAAK,EAAEC,YAAY,KAAK,EAAE,GAAGH,QAAQ,CAAC;QAClE,MAAMI,YAAYC,qBAAqB;QAGvC,MAAMC,yBAAyB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAACC,OACpD,IAAI,CAAC,qBAAqB,CAACA,KAAK,cAAc;QAGhD,MAAMC,eACJT,AAAmB,WAAnBA,iBACIU,kBAAkB,mBAClBV;QAIN,MAAMW,iBAAiBJ,yBACnBT,QAAaO,WAAWI,cAAc,gBACtCX,QAAaO,WAAW,GAAGI,aAAa,KAAK,CAAC;QAElD,IAAIT,AAAmB,WAAnBA,kBAA6BD,WAAWY,iBAAiB;YAC3D,IAAI,CAACP,WACH,MAAM,IAAIQ,MACR,CAAC,4BAA4B,EAAED,eAAe,+CAA+C,CAAC;YAGlG,IAAIJ,wBACFM,OAAOf,QAAaa,iBAAiB;gBAAE,WAAW;gBAAM,OAAO;YAAK;iBAEpEG,WAAWH;QAEf;QAEA,IAAIJ,wBACFQ,UAAUjB,QAAaa,iBAAiB;YAAE,WAAW;QAAK;QAG5DT,OACE,CAAC,cAAc,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,sCAAsC,CAAC;QAGlF,IAAI;YAEFc,eAAeL,gBAAgBM;YAG/B,IAAIV,wBACFS,eAAeL,gBAAgBO;YAIjC,IAAK,IAAIC,IAAI,GAAGA,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAEA,IAAK;gBAChD,MAAMxB,aAAa,IAAI,CAAC,WAAW,CAACwB,EAAE;gBACtCjB,OAAO,CAAC,kBAAkB,EAAEiB,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;gBAE9D,IAAI,IAAI,CAAC,qBAAqB,CAACxB,WAAW,cAAc,GAAG;oBAEzD,MAAME,YAAYC,QAAaH,WAAW,cAAc;oBACxD,MAAMyB,iBAAiBtB,KAAUD,WAAW;oBAC5C,MAAMwB,uBAAuBvB,KAC3BA,QAAaa,iBACb;oBAEFI,UAAUM,sBAAsB;wBAAE,WAAW;oBAAK;oBAClD,KAAK,MAAMC,QAAQC,YAAYH,gBAAiB;wBAC9C,MAAMI,MAAM1B,KAAUsB,gBAAgBE;wBACtC,MAAMG,OAAO3B,KAAUuB,sBAAsBC;wBAC7CI,aAAaF,KAAKC;oBACpB;gBACF,OAEEE,yBAAyBhC,WAAW,cAAc,EAAEgB;gBAGtD,MAAMiB,aAAaC,0BAA0BlC,WAAW,cAAc;gBACtE,MAAM,EAAEmC,gBAAgB,EAAE,GAAGnC;gBAE7B,MAAMoC,gBAAgB,GAAGC,kBACvB;oBACEJ;oBACA,YAAY;wBACV,0BAA0BE,iBAAiB,YAAY;wBACvD,wBAAwBA,iBAAiB,UAAU;wBACnD,uBAAuBA,iBAAiB,SAAS;wBACjD,oBAAoBA,iBAAiB,MAAM;wBAC3C,6BAA6BA,iBAAiB,eAAe;oBAC/D;gBACF,GACAG,QACAA,QACA,OACA,EAAE,CAAC;gBAELjB,eAAeL,gBAAgBoB;YACjC;YAEA7B,OAAO,CAAC,gCAAgC,EAAES,gBAAgB;YAG1D,IAAIR,mBAAmB;gBACrB,KAAK,MAAMK,QAAQ,IAAI,CAAC,WAAW,CACjC,IAAI;oBACF,IAAI,IAAI,CAAC,qBAAqB,CAACA,KAAK,cAAc,GAAG;wBAEnD,MAAMX,YAAYC,QAAaU,KAAK,cAAc;wBAClDK,OAAOhB,WAAW;4BAAE,WAAW;4BAAM,OAAO;wBAAK;oBACnD,OACEiB,WAAWN,KAAK,cAAc;gBAElC,EAAE,OAAO0B,OAAO;oBACdhC,OAAO,CAAC,sBAAsB,EAAEM,KAAK,cAAc,CAAC,EAAE,EAAE0B,OAAO;gBACjE;gBAEFhC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,iBAAiB,CAAC;YAC9D;YACA,OAAOS;QACT,EAAE,OAAOuB,OAAO;YACdhC,OAAO,CAAC,uBAAuB,EAAEgC,OAAO;YACxC,MAAMA;QACR;IACF;;QAvJA,uBAAQ,eAA0C,EAAE;;AAwJtD"}
|
|
@@ -191,7 +191,7 @@ class Service {
|
|
|
191
191
|
async describe(target, modelConfig, opt) {
|
|
192
192
|
assert(target, 'target is required for service.describe');
|
|
193
193
|
const context = await this.contextRetrieverFn();
|
|
194
|
-
const {
|
|
194
|
+
const { shotSize } = context;
|
|
195
195
|
const screenshotBase64 = context.screenshot.base64;
|
|
196
196
|
assert(screenshotBase64, 'screenshot is required for service.describe');
|
|
197
197
|
const { modelFamily } = modelConfig;
|
|
@@ -205,7 +205,7 @@ class Service {
|
|
|
205
205
|
} : target;
|
|
206
206
|
let imagePayload = await compositeElementInfoImg({
|
|
207
207
|
inputImgBase64: screenshotBase64,
|
|
208
|
-
size,
|
|
208
|
+
size: shotSize,
|
|
209
209
|
elementsPositionInfo: [
|
|
210
210
|
{
|
|
211
211
|
rect: targetRect
|
|
@@ -214,14 +214,14 @@ class Service {
|
|
|
214
214
|
borderThickness: 3
|
|
215
215
|
});
|
|
216
216
|
if (opt?.deepThink) {
|
|
217
|
-
const searchArea = expandSearchArea(targetRect,
|
|
218
|
-
const widthRatio = searchArea.width /
|
|
219
|
-
const heightRatio = searchArea.height /
|
|
217
|
+
const searchArea = expandSearchArea(targetRect, shotSize);
|
|
218
|
+
const widthRatio = searchArea.width / shotSize.width;
|
|
219
|
+
const heightRatio = searchArea.height / shotSize.height;
|
|
220
220
|
if (widthRatio >= 0.5 && heightRatio >= 0.5) {
|
|
221
221
|
debug('describe: cropping to searchArea', searchArea);
|
|
222
222
|
const croppedResult = await cropByRect(imagePayload, searchArea, 'qwen2.5-vl' === modelFamily);
|
|
223
223
|
imagePayload = croppedResult.imageBase64;
|
|
224
|
-
} else debug('describe: skip cropping, search area too small (%dx%d on %dx%d)', searchArea.width, searchArea.height,
|
|
224
|
+
} else debug('describe: skip cropping, search area too small (%dx%d on %dx%d)', searchArea.width, searchArea.height, shotSize.width, shotSize.height);
|
|
225
225
|
}
|
|
226
226
|
const msgs = [
|
|
227
227
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service/index.mjs","sources":["../../../src/service/index.ts"],"sourcesContent":["import { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport {\n AIResponseParseError,\n AiExtractElementInfo,\n AiLocateElement,\n callAIWithObjectResponse,\n} from '@/ai-model/index';\nimport { AiLocateSection } from '@/ai-model/inspect';\nimport { elementDescriberInstruction } from '@/ai-model/prompt/describe';\nimport { type AIArgs, expandSearchArea } from '@/common';\nimport type {\n AIDescribeElementResponse,\n AIUsageInfo,\n DetailedLocateParam,\n LocateResultWithDump,\n PartialServiceDumpFromSDK,\n Rect,\n ServiceExtractOption,\n ServiceExtractParam,\n ServiceExtractResult,\n ServiceTaskInfo,\n UIContext,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport {\n type IModelConfig,\n MIDSCENE_FORCE_DEEP_THINK,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { compositeElementInfoImg, cropByRect } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { TMultimodalPrompt } from '../common';\nimport { createServiceDump } from './utils';\n\nexport interface LocateOpts {\n context?: UIContext;\n}\n\nexport type AnyValue<T> = {\n [K in keyof T]: unknown extends T[K] ? any : T[K];\n};\n\ninterface ServiceOptions {\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n}\n\nconst debug = getDebug('ai:service');\nexport default class Service {\n contextRetrieverFn: () => Promise<UIContext> | UIContext;\n\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n\n constructor(\n context: UIContext | (() => Promise<UIContext> | UIContext),\n opt?: ServiceOptions,\n ) {\n assert(context, 'context is required for Service');\n if (typeof context === 'function') {\n this.contextRetrieverFn = context;\n } else {\n this.contextRetrieverFn = () => Promise.resolve(context);\n }\n\n if (typeof opt?.taskInfo !== 'undefined') {\n this.taskInfo = opt.taskInfo;\n }\n }\n\n async locate(\n query: DetailedLocateParam,\n opt: LocateOpts,\n modelConfig: IModelConfig,\n ): Promise<LocateResultWithDump> {\n const queryPrompt = typeof query === 'string' ? query : query.prompt;\n assert(queryPrompt, 'query is required for locate');\n\n assert(typeof query === 'object', 'query should be an object for locate');\n\n const globalDeepThinkSwitch = globalConfigManager.getEnvConfigInBoolean(\n MIDSCENE_FORCE_DEEP_THINK,\n );\n if (globalDeepThinkSwitch) {\n debug('globalDeepThinkSwitch', globalDeepThinkSwitch);\n }\n let searchAreaPrompt;\n if (query.deepThink || globalDeepThinkSwitch) {\n searchAreaPrompt = query.prompt;\n }\n\n const { modelFamily } = modelConfig;\n\n if (searchAreaPrompt && !modelFamily) {\n console.warn(\n 'The \"deepThink\" feature is not supported with multimodal LLM. Please config VL model for Midscene. https://midscenejs.com/model-config',\n );\n searchAreaPrompt = undefined;\n }\n\n if (searchAreaPrompt && isAutoGLM(modelFamily)) {\n console.warn('The \"deepThink\" feature is not supported with AutoGLM.');\n searchAreaPrompt = undefined;\n }\n\n const context = opt?.context || (await this.contextRetrieverFn());\n\n let searchArea: Rect | undefined = undefined;\n let searchAreaRawResponse: string | undefined = undefined;\n let searchAreaUsage: AIUsageInfo | undefined = undefined;\n let searchAreaResponse:\n | Awaited<ReturnType<typeof AiLocateSection>>\n | undefined = undefined;\n if (searchAreaPrompt) {\n searchAreaResponse = await AiLocateSection({\n context,\n sectionDescription: searchAreaPrompt,\n modelConfig,\n });\n assert(\n searchAreaResponse.rect,\n `cannot find search area for \"${searchAreaPrompt}\"${\n searchAreaResponse.error ? `: ${searchAreaResponse.error}` : ''\n }`,\n );\n searchAreaRawResponse = searchAreaResponse.rawResponse;\n searchAreaUsage = searchAreaResponse.usage;\n searchArea = searchAreaResponse.rect;\n }\n\n const startTime = Date.now();\n const { parseResult, rect, rawResponse, usage, reasoning_content } =\n await AiLocateElement({\n context,\n targetElementDescription: queryPrompt,\n searchConfig: searchAreaResponse,\n modelConfig,\n });\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: JSON.stringify(rawResponse),\n formatResponse: JSON.stringify(parseResult),\n usage,\n searchArea,\n searchAreaRawResponse,\n searchAreaUsage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `failed to locate element: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'locate',\n userQuery: {\n element: queryPrompt,\n },\n matchedElement: [],\n matchedRect: rect,\n data: null,\n taskInfo,\n deepThink: !!searchArea,\n error: errorLog,\n };\n\n const elements = parseResult.elements || [];\n\n const dump = createServiceDump({\n ...dumpData,\n matchedElement: elements,\n });\n\n if (errorLog) {\n throw new ServiceError(errorLog, dump);\n }\n\n if (elements.length > 1) {\n throw new ServiceError(\n `locate: multiple elements found, length = ${elements.length}`,\n dump,\n );\n }\n\n if (elements.length === 1) {\n return {\n element: {\n center: elements[0]!.center,\n rect: elements[0]!.rect,\n description: elements[0]!.description,\n },\n rect,\n dump,\n };\n }\n\n return {\n element: null,\n rect,\n dump,\n };\n }\n\n async extract<T>(\n dataDemand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n pageDescription?: string,\n multimodalPrompt?: TMultimodalPrompt,\n context?: UIContext,\n ): Promise<ServiceExtractResult<T>> {\n assert(context, 'context is required for extract');\n assert(\n typeof dataDemand === 'object' || typeof dataDemand === 'string',\n `dataDemand should be object or string, but get ${typeof dataDemand}`,\n );\n\n const startTime = Date.now();\n\n let parseResult: Awaited<\n ReturnType<typeof AiExtractElementInfo<T>>\n >['parseResult'];\n let rawResponse: string;\n let usage: Awaited<ReturnType<typeof AiExtractElementInfo<T>>>['usage'];\n let reasoning_content: string | undefined;\n\n try {\n const result = await AiExtractElementInfo<T>({\n context,\n dataQuery: dataDemand,\n multimodalPrompt,\n extractOption: opt,\n modelConfig,\n pageDescription,\n });\n parseResult = result.parseResult;\n rawResponse = result.rawResponse;\n usage = result.usage;\n reasoning_content = result.reasoning_content;\n } catch (error) {\n if (error instanceof AIResponseParseError) {\n // Create dump with usage and rawResponse from the error\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: error.rawResponse,\n usage: error.usage,\n };\n const dump = createServiceDump({\n type: 'extract',\n userQuery: { dataDemand },\n matchedElement: [],\n data: null,\n taskInfo,\n error: error.message,\n });\n throw new ServiceError(error.message, dump);\n }\n throw error;\n }\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse,\n formatResponse: JSON.stringify(parseResult),\n usage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `AI response error: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'extract',\n userQuery: {\n dataDemand,\n },\n matchedElement: [],\n data: null,\n taskInfo,\n error: errorLog,\n };\n\n const { data, thought } = parseResult || {};\n\n const dump = createServiceDump({\n ...dumpData,\n data,\n });\n\n if (errorLog && !data) {\n throw new ServiceError(errorLog, dump);\n }\n\n return {\n data,\n thought,\n usage,\n reasoning_content,\n dump,\n };\n }\n\n async describe(\n target: Rect | [number, number],\n modelConfig: IModelConfig,\n opt?: {\n deepThink?: boolean;\n },\n ): Promise<Pick<AIDescribeElementResponse, 'description'>> {\n assert(target, 'target is required for service.describe');\n const context = await this.contextRetrieverFn();\n const { size } = context;\n const screenshotBase64 = context.screenshot.base64;\n assert(screenshotBase64, 'screenshot is required for service.describe');\n // The result of the \"describe\" function will be used for positioning, so essentially it is a form of grounding.\n const { modelFamily } = modelConfig;\n const systemPrompt = elementDescriberInstruction();\n\n // Convert [x,y] center point to Rect if needed\n const defaultRectSize = 30;\n const targetRect: Rect = Array.isArray(target)\n ? {\n left: Math.floor(target[0] - defaultRectSize / 2),\n top: Math.floor(target[1] - defaultRectSize / 2),\n width: defaultRectSize,\n height: defaultRectSize,\n }\n : target;\n\n let imagePayload = await compositeElementInfoImg({\n inputImgBase64: screenshotBase64,\n size,\n elementsPositionInfo: [\n {\n rect: targetRect,\n },\n ],\n borderThickness: 3,\n });\n\n if (opt?.deepThink) {\n const searchArea = expandSearchArea(targetRect, size);\n // Only crop when the search area covers at least 50% of the screen\n // in both dimensions. Small crops (e.g., 500px on 1920x1080) lose\n // too much context and cause model hallucinations.\n const widthRatio = searchArea.width / size.width;\n const heightRatio = searchArea.height / size.height;\n if (widthRatio >= 0.5 && heightRatio >= 0.5) {\n debug('describe: cropping to searchArea', searchArea);\n const croppedResult = await cropByRect(\n imagePayload,\n searchArea,\n modelFamily === 'qwen2.5-vl',\n );\n imagePayload = croppedResult.imageBase64;\n } else {\n debug(\n 'describe: skip cropping, search area too small (%dx%d on %dx%d)',\n searchArea.width,\n searchArea.height,\n size.width,\n size.height,\n );\n }\n }\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n },\n ];\n\n const res = await callAIWithObjectResponse<AIDescribeElementResponse>(\n msgs,\n modelConfig,\n );\n\n const { content } = res;\n assert(!content.error, `describe failed: ${content.error}`);\n assert(content.description, 'failed to describe the element');\n return content;\n }\n}\n"],"names":["debug","getDebug","Service","query","opt","modelConfig","queryPrompt","assert","globalDeepThinkSwitch","globalConfigManager","MIDSCENE_FORCE_DEEP_THINK","searchAreaPrompt","modelFamily","console","undefined","isAutoGLM","context","searchArea","searchAreaRawResponse","searchAreaUsage","searchAreaResponse","AiLocateSection","startTime","Date","parseResult","rect","rawResponse","usage","reasoning_content","AiLocateElement","timeCost","taskInfo","JSON","errorLog","dumpData","elements","dump","createServiceDump","ServiceError","dataDemand","pageDescription","multimodalPrompt","result","AiExtractElementInfo","error","AIResponseParseError","data","thought","target","size","screenshotBase64","systemPrompt","elementDescriberInstruction","defaultRectSize","targetRect","Array","Math","imagePayload","compositeElementInfoImg","expandSearchArea","widthRatio","heightRatio","croppedResult","cropByRect","msgs","res","callAIWithObjectResponse","content","Promise"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service/index.mjs","sources":["../../../src/service/index.ts"],"sourcesContent":["import { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport {\n AIResponseParseError,\n AiExtractElementInfo,\n AiLocateElement,\n callAIWithObjectResponse,\n} from '@/ai-model/index';\nimport { AiLocateSection } from '@/ai-model/inspect';\nimport { elementDescriberInstruction } from '@/ai-model/prompt/describe';\nimport { type AIArgs, expandSearchArea } from '@/common';\nimport type {\n AIDescribeElementResponse,\n AIUsageInfo,\n DetailedLocateParam,\n LocateResultWithDump,\n PartialServiceDumpFromSDK,\n Rect,\n ServiceExtractOption,\n ServiceExtractParam,\n ServiceExtractResult,\n ServiceTaskInfo,\n UIContext,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport {\n type IModelConfig,\n MIDSCENE_FORCE_DEEP_THINK,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { compositeElementInfoImg, cropByRect } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { TMultimodalPrompt } from '../common';\nimport { createServiceDump } from './utils';\n\nexport interface LocateOpts {\n context?: UIContext;\n}\n\nexport type AnyValue<T> = {\n [K in keyof T]: unknown extends T[K] ? any : T[K];\n};\n\ninterface ServiceOptions {\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n}\n\nconst debug = getDebug('ai:service');\nexport default class Service {\n contextRetrieverFn: () => Promise<UIContext> | UIContext;\n\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n\n constructor(\n context: UIContext | (() => Promise<UIContext> | UIContext),\n opt?: ServiceOptions,\n ) {\n assert(context, 'context is required for Service');\n if (typeof context === 'function') {\n this.contextRetrieverFn = context;\n } else {\n this.contextRetrieverFn = () => Promise.resolve(context);\n }\n\n if (typeof opt?.taskInfo !== 'undefined') {\n this.taskInfo = opt.taskInfo;\n }\n }\n\n async locate(\n query: DetailedLocateParam,\n opt: LocateOpts,\n modelConfig: IModelConfig,\n ): Promise<LocateResultWithDump> {\n const queryPrompt = typeof query === 'string' ? query : query.prompt;\n assert(queryPrompt, 'query is required for locate');\n\n assert(typeof query === 'object', 'query should be an object for locate');\n\n const globalDeepThinkSwitch = globalConfigManager.getEnvConfigInBoolean(\n MIDSCENE_FORCE_DEEP_THINK,\n );\n if (globalDeepThinkSwitch) {\n debug('globalDeepThinkSwitch', globalDeepThinkSwitch);\n }\n let searchAreaPrompt;\n if (query.deepThink || globalDeepThinkSwitch) {\n searchAreaPrompt = query.prompt;\n }\n\n const { modelFamily } = modelConfig;\n\n if (searchAreaPrompt && !modelFamily) {\n console.warn(\n 'The \"deepThink\" feature is not supported with multimodal LLM. Please config VL model for Midscene. https://midscenejs.com/model-config',\n );\n searchAreaPrompt = undefined;\n }\n\n if (searchAreaPrompt && isAutoGLM(modelFamily)) {\n console.warn('The \"deepThink\" feature is not supported with AutoGLM.');\n searchAreaPrompt = undefined;\n }\n\n const context = opt?.context || (await this.contextRetrieverFn());\n\n let searchArea: Rect | undefined = undefined;\n let searchAreaRawResponse: string | undefined = undefined;\n let searchAreaUsage: AIUsageInfo | undefined = undefined;\n let searchAreaResponse:\n | Awaited<ReturnType<typeof AiLocateSection>>\n | undefined = undefined;\n if (searchAreaPrompt) {\n searchAreaResponse = await AiLocateSection({\n context,\n sectionDescription: searchAreaPrompt,\n modelConfig,\n });\n assert(\n searchAreaResponse.rect,\n `cannot find search area for \"${searchAreaPrompt}\"${\n searchAreaResponse.error ? `: ${searchAreaResponse.error}` : ''\n }`,\n );\n searchAreaRawResponse = searchAreaResponse.rawResponse;\n searchAreaUsage = searchAreaResponse.usage;\n searchArea = searchAreaResponse.rect;\n }\n\n const startTime = Date.now();\n const { parseResult, rect, rawResponse, usage, reasoning_content } =\n await AiLocateElement({\n context,\n targetElementDescription: queryPrompt,\n searchConfig: searchAreaResponse,\n modelConfig,\n });\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: JSON.stringify(rawResponse),\n formatResponse: JSON.stringify(parseResult),\n usage,\n searchArea,\n searchAreaRawResponse,\n searchAreaUsage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `failed to locate element: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'locate',\n userQuery: {\n element: queryPrompt,\n },\n matchedElement: [],\n matchedRect: rect,\n data: null,\n taskInfo,\n deepThink: !!searchArea,\n error: errorLog,\n };\n\n const elements = parseResult.elements || [];\n\n const dump = createServiceDump({\n ...dumpData,\n matchedElement: elements,\n });\n\n if (errorLog) {\n throw new ServiceError(errorLog, dump);\n }\n\n if (elements.length > 1) {\n throw new ServiceError(\n `locate: multiple elements found, length = ${elements.length}`,\n dump,\n );\n }\n\n if (elements.length === 1) {\n return {\n element: {\n center: elements[0]!.center,\n rect: elements[0]!.rect,\n description: elements[0]!.description,\n },\n rect,\n dump,\n };\n }\n\n return {\n element: null,\n rect,\n dump,\n };\n }\n\n async extract<T>(\n dataDemand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n pageDescription?: string,\n multimodalPrompt?: TMultimodalPrompt,\n context?: UIContext,\n ): Promise<ServiceExtractResult<T>> {\n assert(context, 'context is required for extract');\n assert(\n typeof dataDemand === 'object' || typeof dataDemand === 'string',\n `dataDemand should be object or string, but get ${typeof dataDemand}`,\n );\n\n const startTime = Date.now();\n\n let parseResult: Awaited<\n ReturnType<typeof AiExtractElementInfo<T>>\n >['parseResult'];\n let rawResponse: string;\n let usage: Awaited<ReturnType<typeof AiExtractElementInfo<T>>>['usage'];\n let reasoning_content: string | undefined;\n\n try {\n const result = await AiExtractElementInfo<T>({\n context,\n dataQuery: dataDemand,\n multimodalPrompt,\n extractOption: opt,\n modelConfig,\n pageDescription,\n });\n parseResult = result.parseResult;\n rawResponse = result.rawResponse;\n usage = result.usage;\n reasoning_content = result.reasoning_content;\n } catch (error) {\n if (error instanceof AIResponseParseError) {\n // Create dump with usage and rawResponse from the error\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: error.rawResponse,\n usage: error.usage,\n };\n const dump = createServiceDump({\n type: 'extract',\n userQuery: { dataDemand },\n matchedElement: [],\n data: null,\n taskInfo,\n error: error.message,\n });\n throw new ServiceError(error.message, dump);\n }\n throw error;\n }\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse,\n formatResponse: JSON.stringify(parseResult),\n usage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `AI response error: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'extract',\n userQuery: {\n dataDemand,\n },\n matchedElement: [],\n data: null,\n taskInfo,\n error: errorLog,\n };\n\n const { data, thought } = parseResult || {};\n\n const dump = createServiceDump({\n ...dumpData,\n data,\n });\n\n if (errorLog && !data) {\n throw new ServiceError(errorLog, dump);\n }\n\n return {\n data,\n thought,\n usage,\n reasoning_content,\n dump,\n };\n }\n\n async describe(\n target: Rect | [number, number],\n modelConfig: IModelConfig,\n opt?: {\n deepThink?: boolean;\n },\n ): Promise<Pick<AIDescribeElementResponse, 'description'>> {\n assert(target, 'target is required for service.describe');\n const context = await this.contextRetrieverFn();\n const { shotSize } = context;\n const screenshotBase64 = context.screenshot.base64;\n assert(screenshotBase64, 'screenshot is required for service.describe');\n // The result of the \"describe\" function will be used for positioning, so essentially it is a form of grounding.\n const { modelFamily } = modelConfig;\n const systemPrompt = elementDescriberInstruction();\n\n // Convert [x,y] center point to Rect if needed\n const defaultRectSize = 30;\n const targetRect: Rect = Array.isArray(target)\n ? {\n left: Math.floor(target[0] - defaultRectSize / 2),\n top: Math.floor(target[1] - defaultRectSize / 2),\n width: defaultRectSize,\n height: defaultRectSize,\n }\n : target;\n\n let imagePayload = await compositeElementInfoImg({\n inputImgBase64: screenshotBase64,\n size: shotSize,\n elementsPositionInfo: [\n {\n rect: targetRect,\n },\n ],\n borderThickness: 3,\n });\n\n if (opt?.deepThink) {\n const searchArea = expandSearchArea(targetRect, shotSize);\n // Only crop when the search area covers at least 50% of the screen\n // in both dimensions. Small crops (e.g., 500px on 1920x1080) lose\n // too much context and cause model hallucinations.\n const widthRatio = searchArea.width / shotSize.width;\n const heightRatio = searchArea.height / shotSize.height;\n if (widthRatio >= 0.5 && heightRatio >= 0.5) {\n debug('describe: cropping to searchArea', searchArea);\n const croppedResult = await cropByRect(\n imagePayload,\n searchArea,\n modelFamily === 'qwen2.5-vl',\n );\n imagePayload = croppedResult.imageBase64;\n } else {\n debug(\n 'describe: skip cropping, search area too small (%dx%d on %dx%d)',\n searchArea.width,\n searchArea.height,\n shotSize.width,\n shotSize.height,\n );\n }\n }\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n },\n ];\n\n const res = await callAIWithObjectResponse<AIDescribeElementResponse>(\n msgs,\n modelConfig,\n );\n\n const { content } = res;\n assert(!content.error, `describe failed: ${content.error}`);\n assert(content.description, 'failed to describe the element');\n return content;\n }\n}\n"],"names":["debug","getDebug","Service","query","opt","modelConfig","queryPrompt","assert","globalDeepThinkSwitch","globalConfigManager","MIDSCENE_FORCE_DEEP_THINK","searchAreaPrompt","modelFamily","console","undefined","isAutoGLM","context","searchArea","searchAreaRawResponse","searchAreaUsage","searchAreaResponse","AiLocateSection","startTime","Date","parseResult","rect","rawResponse","usage","reasoning_content","AiLocateElement","timeCost","taskInfo","JSON","errorLog","dumpData","elements","dump","createServiceDump","ServiceError","dataDemand","pageDescription","multimodalPrompt","result","AiExtractElementInfo","error","AIResponseParseError","data","thought","target","shotSize","screenshotBase64","systemPrompt","elementDescriberInstruction","defaultRectSize","targetRect","Array","Math","imagePayload","compositeElementInfoImg","expandSearchArea","widthRatio","heightRatio","croppedResult","cropByRect","msgs","res","callAIWithObjectResponse","content","Promise"],"mappings":"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"}
|
package/dist/es/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport { restoreImageReferences } from './dump/image-restoration';\nimport { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport * from './yaml';\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n time_cost: number | undefined;\n model_name: string | undefined;\n model_description: string | undefined;\n intent: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport interface AIElementCoordinatesResponse {\n bbox: [number, number, number, number];\n errors?: string[];\n}\n\nexport type AIElementResponse = AIElementCoordinatesResponse;\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox: [number, number, number, number];\n references_bbox?: [number, number, number, number][];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect: Rect;\n center: [number, number];\n centerDistance?: number;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepThink: boolean;\n verifyResult?: LocateValidatorResult;\n}\n\n/**\n * context\n */\n\nexport abstract class UIContext {\n abstract screenshot: ScreenshotItem;\n\n abstract size: Size;\n\n abstract _isFrozen?: boolean;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n rect?: Rect;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: string;\n rawResponse?: string;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n searchAreaRawResponse?: string;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: Record<string, any>;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement: LocateResultElement[];\n matchedRect?: Rect;\n deepThink?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport class ServiceError extends Error {\n dump: ServiceDump;\n\n constructor(message: string, dump: ServiceDump) {\n super(message);\n this.name = 'ServiceError';\n this.dump = dump;\n }\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n}\n\n/**\n * planning\n *\n */\n\nexport interface PlanningLocateParam extends DetailedLocateParam {\n bbox?: [number, number, number, number];\n}\n\nexport interface PlanningAction<ParamType = any> {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n param: ParamType;\n}\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n rawResponse?: string;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n timing?: string;\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n subTask?: boolean;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n executor: (\n param: TaskParam,\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n error?: Error;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/**\n * Replacer function for JSON serialization that handles Page, Browser objects and ScreenshotItem\n */\nfunction replacerForDumpSerialization(_key: string, value: any): any {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n // Handle ScreenshotItem serialization\n if (value && typeof value.toSerializable === 'function') {\n return value.toSerializable();\n }\n return value;\n}\n\n/**\n * Reviver function for JSON deserialization that handles ScreenshotItem formats.\n *\n * BEHAVIOR:\n * - For { $screenshot: \"id\" } format: Left as-is (plain object)\n * Consumer must use imageMap to restore base64 data\n * - For { base64: \"...\" } format: Creates ScreenshotItem from base64 data\n *\n * @param key - JSON key being processed\n * @param value - JSON value being processed\n * @returns Restored value\n */\nfunction reviverForDumpDeserialization(key: string, value: any): any {\n // Only process screenshot fields\n if (key !== 'screenshot' || typeof value !== 'object' || value === null) {\n return value;\n }\n\n // Handle serialized format: { $screenshot: \"id\" }\n // Leave as plain object — consumer uses imageMap to restore\n if (ScreenshotItem.isSerialized(value)) {\n return value;\n }\n\n // Handle inline base64 format: { base64: \"...\" }\n if ('base64' in value && typeof value.base64 === 'string') {\n return value;\n }\n\n return value;\n}\n\n/**\n * ExecutionDump class for serializing and deserializing execution dumps\n */\nexport class ExecutionDump implements IExecutionDump {\n logTime: number;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n\n constructor(data: IExecutionDump) {\n this.logTime = data.logTime;\n this.name = data.name;\n this.description = data.description;\n this.tasks = data.tasks;\n this.aiActContext = data.aiActContext;\n }\n\n /**\n * Serialize the ExecutionDump to a JSON string\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IExecutionDump {\n return {\n logTime: this.logTime,\n name: this.name,\n description: this.description,\n tasks: this.tasks.map((task) => ({\n ...task,\n recorder: task.recorder || [],\n })),\n aiActContext: this.aiActContext,\n };\n }\n\n /**\n * Create an ExecutionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ExecutionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IExecutionDump;\n return new ExecutionDump(parsed);\n }\n\n /**\n * Create an ExecutionDump instance from a plain object\n */\n static fromJSON(data: IExecutionDump): ExecutionDump {\n return new ExecutionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from tasks.\n * Scans through uiContext and recorder items to find screenshots.\n *\n * @returns Array of ScreenshotItem instances\n */\n collectScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n\n for (const task of this.tasks) {\n // Collect uiContext.screenshot if present\n if (task.uiContext?.screenshot instanceof ScreenshotItem) {\n screenshots.push(task.uiContext.screenshot);\n }\n\n // Collect recorder screenshots\n if (task.recorder) {\n for (const record of task.recorder) {\n if (record.screenshot instanceof ScreenshotItem) {\n screenshots.push(record.screenshot);\n }\n }\n }\n }\n\n return screenshots;\n }\n}\n\n/*\ntask - service-locate\n*/\nexport type ExecutionTaskInsightLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n {\n userInstruction: string;\n aiActContext?: string;\n },\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport type ExecutionTaskPlanningLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nGrouped dump\n*/\nexport interface IGroupedActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: string[];\n executions: IExecutionDump[];\n}\n\n/**\n * GroupedActionDump class for serializing and deserializing grouped action dumps\n */\nexport class GroupedActionDump implements IGroupedActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: string[];\n executions: ExecutionDump[];\n\n constructor(data: IGroupedActionDump) {\n this.sdkVersion = data.sdkVersion;\n this.groupName = data.groupName;\n this.groupDescription = data.groupDescription;\n this.modelBriefs = data.modelBriefs;\n this.executions = data.executions.map((exec) =>\n exec instanceof ExecutionDump ? exec : ExecutionDump.fromJSON(exec),\n );\n }\n\n /**\n * Serialize the GroupedActionDump to a JSON string\n * Uses compact { $screenshot: id } format\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Serialize the GroupedActionDump with inline screenshots to a JSON string.\n * Each ScreenshotItem is replaced with { base64: \"...\" }.\n */\n serializeWithInlineScreenshots(indents?: number): string {\n const processValue = (obj: unknown): unknown => {\n if (obj instanceof ScreenshotItem) {\n return { base64: obj.base64 };\n }\n if (Array.isArray(obj)) {\n return obj.map(processValue);\n }\n if (obj && typeof obj === 'object') {\n const entries = Object.entries(obj).map(([key, value]) => [\n key,\n processValue(value),\n ]);\n return Object.fromEntries(entries);\n }\n return obj;\n };\n\n const data = processValue(this.toJSON());\n return JSON.stringify(data, null, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IGroupedActionDump {\n return {\n sdkVersion: this.sdkVersion,\n groupName: this.groupName,\n groupDescription: this.groupDescription,\n modelBriefs: this.modelBriefs,\n executions: this.executions.map((exec) => exec.toJSON()),\n };\n }\n\n /**\n * Create a GroupedActionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): GroupedActionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IGroupedActionDump;\n return new GroupedActionDump(parsed);\n }\n\n /**\n * Create a GroupedActionDump instance from a plain object\n */\n static fromJSON(data: IGroupedActionDump): GroupedActionDump {\n return new GroupedActionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from all executions.\n *\n * @returns Array of all ScreenshotItem instances across all executions\n */\n collectAllScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n for (const execution of this.executions) {\n screenshots.push(...execution.collectScreenshots());\n }\n return screenshots;\n }\n\n /**\n * Serialize the dump to files with screenshots as separate PNG files.\n * Creates:\n * - {basePath} - dump JSON with { $screenshot: id } references\n * - {basePath}.screenshots/ - PNG files\n * - {basePath}.screenshots.json - ID to path mapping\n *\n * @param basePath - Base path for the dump file\n */\n serializeToFiles(basePath: string): void {\n const screenshotsDir = `${basePath}.screenshots`;\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n // Write screenshots to separate files\n const screenshotMap: Record<string, string> = {};\n const screenshots = this.collectAllScreenshots();\n\n for (const screenshot of screenshots) {\n if (screenshot.hasBase64()) {\n const imagePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n const rawBase64 = screenshot.rawBase64;\n writeFileSync(imagePath, Buffer.from(rawBase64, 'base64'));\n screenshotMap[screenshot.id] = imagePath;\n }\n }\n\n // Write screenshot map file\n writeFileSync(\n `${basePath}.screenshots.json`,\n JSON.stringify(screenshotMap),\n 'utf-8',\n );\n\n // Write dump JSON with references\n writeFileSync(basePath, this.serialize(), 'utf-8');\n }\n\n /**\n * Read dump from files and return JSON string with inline screenshots.\n * Reads the dump JSON and screenshot files, then inlines the base64 data.\n *\n * @param basePath - Base path for the dump file\n * @returns JSON string with inline screenshots ({ base64: \"...\" } format)\n */\n static fromFilesAsInlineJson(basePath: string): string {\n const dumpString = readFileSync(basePath, 'utf-8');\n const screenshotsMapPath = `${basePath}.screenshots.json`;\n\n if (!existsSync(screenshotsMapPath)) {\n return dumpString;\n }\n\n // Read screenshot map and build imageMap from files\n const screenshotMap: Record<string, string> = JSON.parse(\n readFileSync(screenshotsMapPath, 'utf-8'),\n );\n\n const imageMap: Record<string, string> = {};\n for (const [id, filePath] of Object.entries(screenshotMap)) {\n if (existsSync(filePath)) {\n const data = readFileSync(filePath);\n const mime =\n filePath.endsWith('.jpeg') || filePath.endsWith('.jpg')\n ? 'jpeg'\n : 'png';\n imageMap[id] = `data:image/${mime};base64,${data.toString('base64')}`;\n }\n }\n\n // Restore image references\n const dumpData = JSON.parse(dumpString);\n const processedData = restoreImageReferences(dumpData, imageMap);\n return JSON.stringify(processedData);\n }\n\n /**\n * Clean up all files associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n */\n static cleanupFiles(basePath: string): void {\n const filesToClean = [\n basePath,\n `${basePath}.screenshots.json`,\n `${basePath}.screenshots`,\n ];\n\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true, recursive: true });\n } catch {\n // Ignore errors - file may already be deleted\n }\n }\n }\n\n /**\n * Get all file paths associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n * @returns Array of all associated file paths\n */\n static getFilePaths(basePath: string): string[] {\n return [\n basePath,\n `${basePath}.screenshots.json`,\n `${basePath}.screenshots`,\n ];\n }\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Token usage information if available */\n usage?: AIUsageInfo;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n call: (param: TParam, context: ExecutorContext) => Promise<TReturn> | TReturn;\n delayAfterRunner?: number;\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\nexport type WebUIContext = UIContext;\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface AgentOpt {\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n aiActContext?: string;\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults to 20 (40 for `vlm-ui-tars`) when not provided.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * When set to true, Midscene will use the target device's time (Android/iOS)\n * instead of the system time. Useful when the device time differs from the\n * host machine. Default: false\n */\n useDeviceTimestamp?: boolean;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileWithAttributes {\n reportFilePath: string;\n reportAttributes: {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n };\n}\n"],"names":["UIContext","ServiceError","Error","message","dump","replacerForDumpSerialization","_key","value","reviverForDumpDeserialization","key","ScreenshotItem","ExecutionDump","indents","JSON","task","serialized","parsed","data","screenshots","record","GroupedActionDump","processValue","obj","Array","entries","Object","exec","execution","basePath","screenshotsDir","existsSync","mkdirSync","screenshotMap","screenshot","imagePath","join","rawBase64","writeFileSync","Buffer","dumpString","readFileSync","screenshotsMapPath","imageMap","id","filePath","mime","dumpData","processedData","restoreImageReferences","filesToClean","rmSync"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport { restoreImageReferences } from './dump/image-restoration';\nimport { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport * from './yaml';\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n time_cost: number | undefined;\n model_name: string | undefined;\n model_description: string | undefined;\n intent: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport interface AIElementCoordinatesResponse {\n bbox: [number, number, number, number];\n errors?: string[];\n}\n\nexport type AIElementResponse = AIElementCoordinatesResponse;\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox: [number, number, number, number];\n references_bbox?: [number, number, number, number][];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect: Rect;\n center: [number, number];\n centerDistance?: number;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepThink: boolean;\n verifyResult?: LocateValidatorResult;\n}\n\n/**\n * context\n */\n\nexport abstract class UIContext {\n abstract screenshot: ScreenshotItem;\n\n abstract shotSize: Size;\n\n abstract _isFrozen?: boolean;\n\n // @deprecated - backward compatibility for aiLocate\n abstract deprecatedDpr?: number;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n rect?: Rect;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: string;\n rawResponse?: string;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n searchAreaRawResponse?: string;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: Record<string, any>;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement: LocateResultElement[];\n matchedRect?: Rect;\n deepThink?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport class ServiceError extends Error {\n dump: ServiceDump;\n\n constructor(message: string, dump: ServiceDump) {\n super(message);\n this.name = 'ServiceError';\n this.dump = dump;\n }\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n}\n\n/**\n * planning\n *\n */\n\nexport interface PlanningLocateParam extends DetailedLocateParam {\n bbox?: [number, number, number, number];\n}\n\nexport interface PlanningAction<ParamType = any> {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n param: ParamType;\n}\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n rawResponse?: string;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n timing?: string;\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n subTask?: boolean;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n executor: (\n param: TaskParam,\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n error?: Error;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/**\n * Replacer function for JSON serialization that handles Page, Browser objects and ScreenshotItem\n */\nfunction replacerForDumpSerialization(_key: string, value: any): any {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n // Handle ScreenshotItem serialization\n if (value && typeof value.toSerializable === 'function') {\n return value.toSerializable();\n }\n return value;\n}\n\n/**\n * Reviver function for JSON deserialization that handles ScreenshotItem formats.\n *\n * BEHAVIOR:\n * - For { $screenshot: \"id\" } format: Left as-is (plain object)\n * Consumer must use imageMap to restore base64 data\n * - For { base64: \"...\" } format: Creates ScreenshotItem from base64 data\n *\n * @param key - JSON key being processed\n * @param value - JSON value being processed\n * @returns Restored value\n */\nfunction reviverForDumpDeserialization(key: string, value: any): any {\n // Only process screenshot fields\n if (key !== 'screenshot' || typeof value !== 'object' || value === null) {\n return value;\n }\n\n // Handle serialized format: { $screenshot: \"id\" }\n // Leave as plain object — consumer uses imageMap to restore\n if (ScreenshotItem.isSerialized(value)) {\n return value;\n }\n\n // Handle inline base64 format: { base64: \"...\" }\n if ('base64' in value && typeof value.base64 === 'string') {\n return value;\n }\n\n return value;\n}\n\n/**\n * ExecutionDump class for serializing and deserializing execution dumps\n */\nexport class ExecutionDump implements IExecutionDump {\n logTime: number;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n\n constructor(data: IExecutionDump) {\n this.logTime = data.logTime;\n this.name = data.name;\n this.description = data.description;\n this.tasks = data.tasks;\n this.aiActContext = data.aiActContext;\n }\n\n /**\n * Serialize the ExecutionDump to a JSON string\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IExecutionDump {\n return {\n logTime: this.logTime,\n name: this.name,\n description: this.description,\n tasks: this.tasks.map((task) => ({\n ...task,\n recorder: task.recorder || [],\n })),\n aiActContext: this.aiActContext,\n };\n }\n\n /**\n * Create an ExecutionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ExecutionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IExecutionDump;\n return new ExecutionDump(parsed);\n }\n\n /**\n * Create an ExecutionDump instance from a plain object\n */\n static fromJSON(data: IExecutionDump): ExecutionDump {\n return new ExecutionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from tasks.\n * Scans through uiContext and recorder items to find screenshots.\n *\n * @returns Array of ScreenshotItem instances\n */\n collectScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n\n for (const task of this.tasks) {\n // Collect uiContext.screenshot if present\n if (task.uiContext?.screenshot instanceof ScreenshotItem) {\n screenshots.push(task.uiContext.screenshot);\n }\n\n // Collect recorder screenshots\n if (task.recorder) {\n for (const record of task.recorder) {\n if (record.screenshot instanceof ScreenshotItem) {\n screenshots.push(record.screenshot);\n }\n }\n }\n }\n\n return screenshots;\n }\n}\n\n/*\ntask - service-locate\n*/\nexport type ExecutionTaskInsightLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n {\n userInstruction: string;\n aiActContext?: string;\n },\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport type ExecutionTaskPlanningLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nGrouped dump\n*/\nexport interface IGroupedActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: string[];\n executions: IExecutionDump[];\n}\n\n/**\n * GroupedActionDump class for serializing and deserializing grouped action dumps\n */\nexport class GroupedActionDump implements IGroupedActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: string[];\n executions: ExecutionDump[];\n\n constructor(data: IGroupedActionDump) {\n this.sdkVersion = data.sdkVersion;\n this.groupName = data.groupName;\n this.groupDescription = data.groupDescription;\n this.modelBriefs = data.modelBriefs;\n this.executions = data.executions.map((exec) =>\n exec instanceof ExecutionDump ? exec : ExecutionDump.fromJSON(exec),\n );\n }\n\n /**\n * Serialize the GroupedActionDump to a JSON string\n * Uses compact { $screenshot: id } format\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Serialize the GroupedActionDump with inline screenshots to a JSON string.\n * Each ScreenshotItem is replaced with { base64: \"...\" }.\n */\n serializeWithInlineScreenshots(indents?: number): string {\n const processValue = (obj: unknown): unknown => {\n if (obj instanceof ScreenshotItem) {\n return { base64: obj.base64 };\n }\n if (Array.isArray(obj)) {\n return obj.map(processValue);\n }\n if (obj && typeof obj === 'object') {\n const entries = Object.entries(obj).map(([key, value]) => [\n key,\n processValue(value),\n ]);\n return Object.fromEntries(entries);\n }\n return obj;\n };\n\n const data = processValue(this.toJSON());\n return JSON.stringify(data, null, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IGroupedActionDump {\n return {\n sdkVersion: this.sdkVersion,\n groupName: this.groupName,\n groupDescription: this.groupDescription,\n modelBriefs: this.modelBriefs,\n executions: this.executions.map((exec) => exec.toJSON()),\n };\n }\n\n /**\n * Create a GroupedActionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): GroupedActionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IGroupedActionDump;\n return new GroupedActionDump(parsed);\n }\n\n /**\n * Create a GroupedActionDump instance from a plain object\n */\n static fromJSON(data: IGroupedActionDump): GroupedActionDump {\n return new GroupedActionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from all executions.\n *\n * @returns Array of all ScreenshotItem instances across all executions\n */\n collectAllScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n for (const execution of this.executions) {\n screenshots.push(...execution.collectScreenshots());\n }\n return screenshots;\n }\n\n /**\n * Serialize the dump to files with screenshots as separate PNG files.\n * Creates:\n * - {basePath} - dump JSON with { $screenshot: id } references\n * - {basePath}.screenshots/ - PNG files\n * - {basePath}.screenshots.json - ID to path mapping\n *\n * @param basePath - Base path for the dump file\n */\n serializeToFiles(basePath: string): void {\n const screenshotsDir = `${basePath}.screenshots`;\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n // Write screenshots to separate files\n const screenshotMap: Record<string, string> = {};\n const screenshots = this.collectAllScreenshots();\n\n for (const screenshot of screenshots) {\n if (screenshot.hasBase64()) {\n const imagePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n const rawBase64 = screenshot.rawBase64;\n writeFileSync(imagePath, Buffer.from(rawBase64, 'base64'));\n screenshotMap[screenshot.id] = imagePath;\n }\n }\n\n // Write screenshot map file\n writeFileSync(\n `${basePath}.screenshots.json`,\n JSON.stringify(screenshotMap),\n 'utf-8',\n );\n\n // Write dump JSON with references\n writeFileSync(basePath, this.serialize(), 'utf-8');\n }\n\n /**\n * Read dump from files and return JSON string with inline screenshots.\n * Reads the dump JSON and screenshot files, then inlines the base64 data.\n *\n * @param basePath - Base path for the dump file\n * @returns JSON string with inline screenshots ({ base64: \"...\" } format)\n */\n static fromFilesAsInlineJson(basePath: string): string {\n const dumpString = readFileSync(basePath, 'utf-8');\n const screenshotsMapPath = `${basePath}.screenshots.json`;\n\n if (!existsSync(screenshotsMapPath)) {\n return dumpString;\n }\n\n // Read screenshot map and build imageMap from files\n const screenshotMap: Record<string, string> = JSON.parse(\n readFileSync(screenshotsMapPath, 'utf-8'),\n );\n\n const imageMap: Record<string, string> = {};\n for (const [id, filePath] of Object.entries(screenshotMap)) {\n if (existsSync(filePath)) {\n const data = readFileSync(filePath);\n const mime =\n filePath.endsWith('.jpeg') || filePath.endsWith('.jpg')\n ? 'jpeg'\n : 'png';\n imageMap[id] = `data:image/${mime};base64,${data.toString('base64')}`;\n }\n }\n\n // Restore image references\n const dumpData = JSON.parse(dumpString);\n const processedData = restoreImageReferences(dumpData, imageMap);\n return JSON.stringify(processedData);\n }\n\n /**\n * Clean up all files associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n */\n static cleanupFiles(basePath: string): void {\n const filesToClean = [\n basePath,\n `${basePath}.screenshots.json`,\n `${basePath}.screenshots`,\n ];\n\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true, recursive: true });\n } catch {\n // Ignore errors - file may already be deleted\n }\n }\n }\n\n /**\n * Get all file paths associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n * @returns Array of all associated file paths\n */\n static getFilePaths(basePath: string): string[] {\n return [\n basePath,\n `${basePath}.screenshots.json`,\n `${basePath}.screenshots`,\n ];\n }\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Token usage information if available */\n usage?: AIUsageInfo;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n call: (param: TParam, context: ExecutorContext) => Promise<TReturn> | TReturn;\n delayAfterRunner?: number;\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface AgentOpt {\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n aiActContext?: string;\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults to 20 (40 for `vlm-ui-tars`) when not provided.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * When set to true, Midscene will use the target device's time (Android/iOS)\n * instead of the system time. Useful when the device time differs from the\n * host machine. Default: false\n */\n useDeviceTimestamp?: boolean;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileWithAttributes {\n reportFilePath: string;\n reportAttributes: {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n };\n}\n"],"names":["UIContext","ServiceError","Error","message","dump","replacerForDumpSerialization","_key","value","reviverForDumpDeserialization","key","ScreenshotItem","ExecutionDump","indents","JSON","task","serialized","parsed","data","screenshots","record","GroupedActionDump","processValue","obj","Array","entries","Object","exec","execution","basePath","screenshotsDir","existsSync","mkdirSync","screenshotMap","screenshot","imagePath","join","rawBase64","writeFileSync","Buffer","dumpString","readFileSync","screenshotsMapPath","imageMap","id","filePath","mime","dumpData","processedData","restoreImageReferences","filesToClean","rmSync"],"mappings":"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"}
|