@donggui/core 1.6.9 → 1.6.10

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.
@@ -542,7 +542,7 @@ class Agent {
542
542
  afterScreenshot,
543
543
  assertion: textPrompt,
544
544
  businessContext: opt?.businessContext,
545
- enableSystemCheck: opt?.enableSystemCheck ?? false,
545
+ enableSystemCheck: opt?.enableSystemCheck ?? true,
546
546
  customSystemCheckRules: opt?.customSystemCheckRules,
547
547
  modelConfig
548
548
  });
@@ -1 +1 @@
1
- {"version":3,"file":"agent/agent.mjs","sources":["../../../src/agent/agent.ts"],"sourcesContent":["import { AiAssertElement } from '../ai-model/assert';\nimport type { TUserPrompt } from '../ai-model/index';\nimport { ScreenshotItem } from '../screenshot-item';\nimport Service from '../service/index';\n// Import types and values directly from their source files to avoid circular dependency\n// DO NOT import from '../index' as it creates a circular dependency:\n// index.ts -> agent/index.ts -> agent/agent.ts -> index.ts\nimport {\n type ActionParam,\n type ActionReturn,\n type ActionScreenshotContext,\n type AgentAssertOpt,\n type AgentDescribeElementAtPointResult,\n type AgentOpt,\n type AgentWaitForOpt,\n type CacheConfig,\n type DeepThinkOption,\n type DeviceAction,\n ExecutionDump,\n type ExecutionRecorderItem,\n type ExecutionTask,\n type ExecutionTaskLog,\n GroupedActionDump,\n type LocateOption,\n type LocateResultElement,\n type LocateValidatorResult,\n type LocatorValidatorOption,\n type OnTaskStartTip,\n type PlanningAction,\n type Rect,\n type ScrollParam,\n type ServiceAction,\n type ServiceDump,\n type ServiceExtractOption,\n type ServiceExtractParam,\n type SystemCheckResults,\n type UIContext,\n} from '../types';\nimport type { MidsceneYamlScript } from '../yaml';\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\nimport { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport yaml from 'js-yaml';\n\nimport type { IReportGenerator } from '@/report-generator';\nimport { ReportGenerator } from '@/report-generator';\nimport { getVersion, processCacheConfig, reportHTMLContent } from '@/utils';\nimport {\n ScriptPlayer,\n buildDetailedLocateParam,\n parseYamlScript,\n} from '../yaml/index';\n\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { AbstractInterface } from '@/device';\nimport type { TaskRunner } from '@/task-runner';\nimport {\n type IModelConfig,\n MIDSCENE_REPLANNING_CYCLE_LIMIT,\n ModelConfigManager,\n globalConfigManager,\n globalModelConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { defineActionSleep } from '../device';\nimport { TaskCache } from './task-cache';\nimport {\n TaskExecutionError,\n TaskExecutor,\n locatePlanForLocate,\n withFileChooser,\n} from './tasks';\nimport { locateParamStr, paramStr, taskTitleStr, typeStr } from './ui-utils';\nimport { commonContextParser, getReportFileName, parsePrompt } from './utils';\n\nconst debug = getDebug('agent');\n\nconst distanceOfTwoPoints = (p1: [number, number], p2: [number, number]) => {\n const [x1, y1] = p1;\n const [x2, y2] = p2;\n return Math.round(Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2));\n};\n\nconst includedInRect = (point: [number, number], rect: Rect) => {\n const [x, y] = point;\n const { left, top, width, height } = rect;\n return x >= left && x <= left + width && y >= top && y <= top + height;\n};\n\nconst defaultServiceExtractOption: ServiceExtractOption = {\n domIncluded: false,\n screenshotIncluded: true,\n};\n\ntype CacheStrategy = NonNullable<CacheConfig['strategy']>;\n\nconst CACHE_STRATEGIES: readonly CacheStrategy[] = [\n 'read-only',\n 'read-write',\n 'write-only',\n];\n\nconst isValidCacheStrategy = (strategy: string): strategy is CacheStrategy =>\n CACHE_STRATEGIES.some((value) => value === strategy);\n\nconst CACHE_STRATEGY_VALUES = CACHE_STRATEGIES.map(\n (value) => `\"${value}\"`,\n).join(', ');\n\nconst legacyScrollTypeMap = {\n once: 'singleAction',\n untilBottom: 'scrollToBottom',\n untilTop: 'scrollToTop',\n untilRight: 'scrollToRight',\n untilLeft: 'scrollToLeft',\n} as const;\n\ntype LegacyScrollType = keyof typeof legacyScrollTypeMap;\n\nconst normalizeScrollType = (\n scrollType: ScrollParam['scrollType'] | LegacyScrollType | undefined,\n): ScrollParam['scrollType'] | undefined => {\n if (!scrollType) {\n return scrollType;\n }\n\n if (scrollType in legacyScrollTypeMap) {\n return legacyScrollTypeMap[scrollType as LegacyScrollType];\n }\n\n return scrollType as ScrollParam['scrollType'];\n};\n\nconst defaultReplanningCycleLimit = 20;\nconst defaultVlmUiTarsReplanningCycleLimit = 40;\nconst defaultAutoGlmReplanningCycleLimit = 100;\n\nexport type AiActOptions = {\n cacheable?: boolean;\n fileChooserAccept?: string | string[];\n deepThink?: DeepThinkOption;\n deepLocate?: boolean;\n abortSignal?: AbortSignal;\n};\n\nexport class Agent<\n InterfaceType extends AbstractInterface = AbstractInterface,\n> {\n interface: InterfaceType;\n\n service: Service;\n\n dump: GroupedActionDump;\n\n reportFile?: string | null;\n\n reportFileName?: string;\n\n taskExecutor: TaskExecutor;\n\n opts: AgentOpt;\n\n /**\n * If true, the agent will not perform any actions\n */\n dryMode = false;\n\n onTaskStartTip?: OnTaskStartTip;\n\n taskCache?: TaskCache;\n\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n get onDumpUpdate():\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined {\n return this.dumpUpdateListeners[0];\n }\n\n set onDumpUpdate(callback:\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined) {\n // Clear existing listeners\n this.dumpUpdateListeners = [];\n // Add callback to array if provided\n if (callback) {\n this.dumpUpdateListeners.push(callback);\n }\n }\n\n destroyed = false;\n\n modelConfigManager: ModelConfigManager;\n\n /**\n * Frozen page context for consistent AI operations\n */\n private frozenUIContext?: UIContext;\n\n private get aiActContext(): string | undefined {\n return this.opts.aiActContext ?? this.opts.aiActionContext;\n }\n\n /**\n * Flag to track if VL model warning has been shown\n */\n private hasWarnedNonVLModel = false;\n\n private executionDumpIndexByRunner = new WeakMap<TaskRunner, number>();\n\n private fullActionSpace: DeviceAction[];\n\n private reportGenerator: IReportGenerator;\n\n /**\n * Action screenshot context history for assertion\n * Stores the last N action screenshots for comparison in assertions\n */\n private actionScreenshotHistory: ActionScreenshotContext[] = [];\n\n /**\n * Maximum number of action screenshots to keep in history\n */\n private readonly maxScreenshotHistoryLength = 5;\n\n /**\n * Screenshot at the start of aiAct for flow-level assertion\n */\n private aiActStartScreenshot: string | null = null;\n\n // @deprecated use .interface instead\n get page() {\n return this.interface;\n }\n\n /**\n * Ensures VL model warning is shown once when needed\n */\n private ensureVLModelWarning() {\n if (\n !this.hasWarnedNonVLModel &&\n this.interface.interfaceType !== 'puppeteer' &&\n this.interface.interfaceType !== 'playwright' &&\n this.interface.interfaceType !== 'static' &&\n this.interface.interfaceType !== 'chrome-extension-proxy' &&\n this.interface.interfaceType !== 'page-over-chrome-extension-bridge'\n ) {\n this.modelConfigManager.throwErrorIfNonVLModel();\n this.hasWarnedNonVLModel = true;\n }\n }\n\n private resolveReplanningCycleLimit(\n modelConfigForPlanning: IModelConfig,\n ): number {\n if (this.opts.replanningCycleLimit !== undefined) {\n return this.opts.replanningCycleLimit;\n }\n\n return isUITars(modelConfigForPlanning.modelFamily)\n ? defaultVlmUiTarsReplanningCycleLimit\n : isAutoGLM(modelConfigForPlanning.modelFamily)\n ? defaultAutoGlmReplanningCycleLimit\n : defaultReplanningCycleLimit;\n }\n\n constructor(interfaceInstance: InterfaceType, opts?: AgentOpt) {\n this.interface = interfaceInstance;\n\n const envReplanningCycleLimit =\n globalConfigManager.getEnvConfigValueAsNumber(\n MIDSCENE_REPLANNING_CYCLE_LIMIT,\n );\n\n this.opts = Object.assign(\n {\n generateReport: true,\n autoPrintReportMsg: true,\n groupName: 'Midscene Report',\n groupDescription: '',\n },\n opts || {},\n opts?.replanningCycleLimit === undefined &&\n envReplanningCycleLimit !== undefined &&\n !Number.isNaN(envReplanningCycleLimit)\n ? { replanningCycleLimit: envReplanningCycleLimit }\n : {},\n );\n\n const resolvedAiActContext =\n this.opts.aiActContext ?? this.opts.aiActionContext;\n if (resolvedAiActContext !== undefined) {\n this.opts.aiActContext = resolvedAiActContext;\n this.opts.aiActionContext ??= resolvedAiActContext;\n }\n\n if (\n opts?.modelConfig &&\n (typeof opts?.modelConfig !== 'object' || Array.isArray(opts.modelConfig))\n ) {\n throw new Error(\n `opts.modelConfig must be a plain object map of env keys to values, but got ${typeof opts?.modelConfig}`,\n );\n }\n // Create ModelConfigManager if modelConfig or createOpenAIClient is provided\n // Otherwise, use the global config manager\n const hasCustomConfig = opts?.modelConfig || opts?.createOpenAIClient;\n this.modelConfigManager = hasCustomConfig\n ? new ModelConfigManager(opts?.modelConfig, opts?.createOpenAIClient)\n : globalModelConfigManager;\n\n this.onTaskStartTip = this.opts.onTaskStartTip;\n\n this.service = new Service(async () => {\n return this.getUIContext();\n });\n\n // Process cache configuration\n const cacheConfigObj = this.processCacheConfig(opts || {});\n if (cacheConfigObj) {\n this.taskCache = new TaskCache(\n cacheConfigObj.id,\n cacheConfigObj.enabled,\n cacheConfigObj.cacheAdapter,\n {\n readOnly: cacheConfigObj.readOnly,\n writeOnly: cacheConfigObj.writeOnly,\n },\n );\n }\n\n const baseActionSpace = this.interface.actionSpace();\n this.fullActionSpace = [...baseActionSpace, defineActionSleep()];\n\n this.taskExecutor = new TaskExecutor(this.interface, this.service, {\n taskCache: this.taskCache,\n onTaskStart: this.callbackOnTaskStartTip.bind(this),\n replanningCycleLimit: this.opts.replanningCycleLimit,\n waitAfterAction: this.opts.waitAfterAction,\n useDeviceTimestamp: this.opts.useDeviceTimestamp,\n actionSpace: this.fullActionSpace,\n hooks: {\n onTaskUpdate: (runner) => {\n const executionDump = runner.dump();\n this.appendExecutionDump(executionDump, runner);\n\n // Call all registered dump update listeners\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n // Fire and forget - don't block task execution\n this.writeOutActionDumps();\n },\n },\n });\n this.dump = this.resetDump();\n this.reportFileName =\n opts?.reportFileName ||\n getReportFileName(opts?.testId || this.interface.interfaceType || 'web');\n\n this.reportGenerator = ReportGenerator.create(this.reportFileName!, {\n generateReport: this.opts.generateReport,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n });\n }\n\n async getActionSpace(): Promise<DeviceAction[]> {\n return this.fullActionSpace;\n }\n\n async getUIContext(action?: ServiceAction): Promise<UIContext> {\n // Check VL model configuration when UI context is first needed\n this.ensureVLModelWarning();\n\n // If page context is frozen, return the frozen context for all actions\n if (this.frozenUIContext) {\n debug('Using frozen page context for action:', action);\n return this.frozenUIContext;\n }\n\n // Get original context\n const context = await commonContextParser(this.interface, {\n uploadServerUrl: this.modelConfigManager.getUploadTestServerUrl(),\n screenshotShrinkFactor: this.opts.screenshotShrinkFactor,\n });\n\n return context;\n }\n\n async _snapshotContext(): Promise<UIContext> {\n return await this.getUIContext('locate');\n }\n\n /**\n * @deprecated Use {@link setAIActContext} instead.\n */\n async setAIActionContext(prompt: string) {\n await this.setAIActContext(prompt);\n }\n\n async setAIActContext(prompt: string) {\n if (this.aiActContext) {\n console.warn(\n 'aiActContext is already set, and it is called again, will override the previous setting',\n );\n }\n this.opts.aiActContext = prompt;\n this.opts.aiActionContext = prompt;\n }\n\n resetDump() {\n this.dump = new GroupedActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.interface.interfaceType,\n });\n this.executionDumpIndexByRunner = new WeakMap<TaskRunner, number>();\n\n return this.dump;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.executionDumpIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.executionDumpIndexByRunner.set(\n runner,\n currentDump.executions.length - 1,\n );\n return;\n }\n currentDump.executions.push(execution);\n }\n\n dumpDataString(opt?: { inlineScreenshots?: boolean }) {\n // update dump info\n this.dump.groupName = this.opts.groupName!;\n this.dump.groupDescription = this.opts.groupDescription;\n // In browser environment, use inline screenshots since file system is not available\n if (ifInBrowser || opt?.inlineScreenshots) {\n return this.dump.serializeWithInlineScreenshots();\n }\n return this.dump.serialize();\n }\n\n reportHTMLString(opt?: { inlineScreenshots?: boolean }) {\n // dumpDataString() handles browser environment with inline screenshots\n return reportHTMLContent(this.dumpDataString(opt));\n }\n\n writeOutActionDumps() {\n this.reportGenerator.onDumpUpdate(this.dump);\n this.reportFile = this.reportGenerator.getReportPath();\n }\n\n private async callbackOnTaskStartTip(task: ExecutionTask) {\n const param = paramStr(task);\n const tip = param ? `${typeStr(task)} - ${param}` : typeStr(task);\n\n if (this.onTaskStartTip) {\n await this.onTaskStartTip(tip);\n }\n }\n\n wrapActionInActionSpace<T extends DeviceAction>(\n name: string,\n ): (param: ActionParam<T>) => Promise<ActionReturn<T>> {\n return async (param: ActionParam<T>) => {\n return await this.callActionInActionSpace<ActionReturn<T>>(name, param);\n };\n }\n\n async callActionInActionSpace<T = any>(\n type: string,\n opt?: T, // and all other action params\n ) {\n debug('callActionInActionSpace', type, ',', opt);\n\n const beforeScreenshot = await this.interface.screenshotBase64();\n\n const actionPlan: PlanningAction<T> = {\n type: type as any,\n param: (opt as any) || {},\n thought: '',\n };\n debug('actionPlan', actionPlan);\n\n const plans: PlanningAction[] = [actionPlan].filter(\n Boolean,\n ) as PlanningAction[];\n\n const title = taskTitleStr(\n type as any,\n locateParamStr((opt as any)?.locate || {}),\n );\n\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n\n const { output } = await this.taskExecutor.runPlans(\n title,\n plans,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n );\n\n const afterScreenshot = await this.interface.screenshotBase64();\n\n this.actionScreenshotHistory.push({\n beforeScreenshot,\n afterScreenshot,\n actionType: type,\n actionParam: opt,\n timestamp: Date.now(),\n });\n\n if (this.actionScreenshotHistory.length > this.maxScreenshotHistoryLength) {\n this.actionScreenshotHistory.shift();\n }\n\n return output;\n }\n\n async aiTap(\n locatePrompt: TUserPrompt,\n opt?: LocateOption & { fileChooserAccept?: string | string[] },\n ) {\n assert(locatePrompt, 'missing locate prompt for tap');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n const fileChooserAccept = opt?.fileChooserAccept\n ? this.normalizeFileInput(opt.fileChooserAccept)\n : undefined;\n\n return withFileChooser(this.interface, fileChooserAccept, async () => {\n return this.callActionInActionSpace('Tap', {\n locate: detailedLocateParam,\n });\n });\n }\n\n async aiRightClick(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for right click');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('RightClick', {\n locate: detailedLocateParam,\n });\n }\n\n async aiDoubleClick(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for double click');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('DoubleClick', {\n locate: detailedLocateParam,\n });\n }\n\n async aiHover(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for hover');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('Hover', {\n locate: detailedLocateParam,\n });\n }\n\n // New signature, always use locatePrompt as the first param\n async aiInput(\n locatePrompt: TUserPrompt,\n opt: LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' },\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiInput(locatePrompt, opt) instead where opt contains the value\n */\n async aiInput(\n value: string | number,\n locatePrompt: TUserPrompt,\n opt?: LocateOption & { autoDismissKeyboard?: boolean } & {\n mode?: 'replace' | 'clear' | 'typeOnly' | 'append';\n }, // AndroidDeviceInputOpt &\n ): Promise<any>;\n\n // Implementation\n async aiInput(\n locatePromptOrValue: TUserPrompt | string | number,\n locatePromptOrOpt:\n | TUserPrompt\n | (LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' }) // AndroidDeviceInputOpt &\n | undefined,\n optOrUndefined?: LocateOption, // AndroidDeviceInputOpt &\n ) {\n let value: string | number;\n let locatePrompt: TUserPrompt;\n let opt:\n | (LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' }) // AndroidDeviceInputOpt &\n | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has value)\n if (\n typeof locatePromptOrOpt === 'object' &&\n locatePromptOrOpt !== null &&\n 'value' in locatePromptOrOpt\n ) {\n // New signature: aiInput(locatePrompt, opt)\n locatePrompt = locatePromptOrValue as TUserPrompt;\n const optWithValue = locatePromptOrOpt as LocateOption & {\n // AndroidDeviceInputOpt &\n value: string | number;\n autoDismissKeyboard?: boolean;\n };\n value = optWithValue.value;\n opt = optWithValue;\n } else {\n // Legacy signature: aiInput(value, locatePrompt, opt)\n value = locatePromptOrValue as string | number;\n locatePrompt = locatePromptOrOpt as TUserPrompt;\n opt = {\n ...optOrUndefined,\n value,\n };\n }\n\n assert(\n typeof value === 'string' || typeof value === 'number',\n 'input value must be a string or number, use empty string if you want to clear the input',\n );\n assert(locatePrompt, 'missing locate prompt for input');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n // Convert value to string to ensure consistency\n const stringValue = typeof value === 'number' ? String(value) : value;\n\n // backward compat: convert deprecated 'append' to 'typeOnly'\n const mode = opt?.mode === 'append' ? 'typeOnly' : opt?.mode;\n\n return this.callActionInActionSpace('Input', {\n ...(opt || {}),\n value: stringValue,\n locate: detailedLocateParam,\n mode,\n });\n }\n\n // New signature\n async aiKeyboardPress(\n locatePrompt: TUserPrompt,\n opt: LocateOption & { keyName: string },\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiKeyboardPress(locatePrompt, opt) instead where opt contains the keyName\n */\n async aiKeyboardPress(\n keyName: string,\n locatePrompt?: TUserPrompt,\n opt?: LocateOption,\n ): Promise<any>;\n\n // Implementation\n async aiKeyboardPress(\n locatePromptOrKeyName: TUserPrompt | string,\n locatePromptOrOpt:\n | TUserPrompt\n | (LocateOption & { keyName: string })\n | undefined,\n optOrUndefined?: LocateOption,\n ) {\n let keyName: string;\n let locatePrompt: TUserPrompt | undefined;\n let opt: (LocateOption & { keyName: string }) | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has keyName)\n if (\n typeof locatePromptOrOpt === 'object' &&\n locatePromptOrOpt !== null &&\n 'keyName' in locatePromptOrOpt\n ) {\n // New signature: aiKeyboardPress(locatePrompt, opt)\n locatePrompt = locatePromptOrKeyName as TUserPrompt;\n opt = locatePromptOrOpt as LocateOption & {\n keyName: string;\n };\n } else {\n // Legacy signature: aiKeyboardPress(keyName, locatePrompt, opt)\n keyName = locatePromptOrKeyName as string;\n locatePrompt = locatePromptOrOpt as TUserPrompt | undefined;\n opt = {\n ...(optOrUndefined || {}),\n keyName,\n };\n }\n\n assert(opt?.keyName, 'missing keyName for keyboard press');\n\n const detailedLocateParam = locatePrompt\n ? buildDetailedLocateParam(locatePrompt, opt)\n : undefined;\n\n return this.callActionInActionSpace('KeyboardPress', {\n ...(opt || {}),\n locate: detailedLocateParam,\n });\n }\n\n // New signature\n async aiScroll(\n locatePrompt: TUserPrompt | undefined,\n opt: LocateOption & ScrollParam,\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiScroll(locatePrompt, opt) instead where opt contains the scroll parameters\n */\n async aiScroll(\n scrollParam: ScrollParam,\n locatePrompt?: TUserPrompt,\n opt?: LocateOption,\n ): Promise<any>;\n\n // Implementation\n async aiScroll(\n locatePromptOrScrollParam: TUserPrompt | ScrollParam | undefined,\n locatePromptOrOpt: TUserPrompt | (LocateOption & ScrollParam) | undefined,\n optOrUndefined?: LocateOption,\n ) {\n let scrollParam: ScrollParam;\n let locatePrompt: TUserPrompt | undefined;\n let opt: LocateOption | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has scroll params)\n if (\n typeof locatePromptOrOpt === 'object' &&\n ('direction' in locatePromptOrOpt ||\n 'scrollType' in locatePromptOrOpt ||\n 'distance' in locatePromptOrOpt)\n ) {\n // New signature: aiScroll(locatePrompt, opt)\n locatePrompt = locatePromptOrScrollParam as TUserPrompt;\n opt = locatePromptOrOpt as LocateOption & ScrollParam;\n } else {\n // Legacy signature: aiScroll(scrollParam, locatePrompt, opt)\n scrollParam = locatePromptOrScrollParam as ScrollParam;\n locatePrompt = locatePromptOrOpt as TUserPrompt | undefined;\n opt = {\n ...(optOrUndefined || {}),\n ...(scrollParam || {}),\n };\n }\n\n if (opt) {\n const normalizedScrollType = normalizeScrollType(\n (opt as ScrollParam).scrollType as\n | ScrollParam['scrollType']\n | LegacyScrollType\n | undefined,\n );\n\n if (normalizedScrollType !== (opt as ScrollParam).scrollType) {\n (opt as ScrollParam) = {\n ...(opt || {}),\n scrollType: normalizedScrollType as ScrollParam['scrollType'],\n };\n }\n }\n\n const detailedLocateParam = buildDetailedLocateParam(\n locatePrompt || '',\n opt,\n );\n\n return this.callActionInActionSpace('Scroll', {\n ...(opt || {}),\n locate: detailedLocateParam,\n });\n }\n\n async aiAct(\n taskPrompt: string,\n opt?: AiActOptions,\n ): Promise<string | undefined> {\n const fileChooserAccept = opt?.fileChooserAccept\n ? this.normalizeFileInput(opt.fileChooserAccept)\n : undefined;\n\n const abortSignal = opt?.abortSignal;\n if (abortSignal?.aborted) {\n throw new Error(\n `aiAct aborted: ${abortSignal.reason || 'signal already aborted'}`,\n );\n }\n\n const runAiAct = async () => {\n this.aiActStartScreenshot = await this.interface.screenshotBase64();\n this.actionScreenshotHistory = [];\n\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const deepThink = opt?.deepThink === 'unset' ? undefined : opt?.deepThink;\n\n const deepLocate = opt?.deepLocate;\n\n const noIndividualLocateModel =\n modelConfigForPlanning.modelName ===\n defaultIntentModelConfig.modelName &&\n modelConfigForPlanning.openaiBaseURL ===\n defaultIntentModelConfig.openaiBaseURL;\n\n const includeBboxInPlanning =\n !deepThink && noIndividualLocateModel && !deepLocate;\n\n debug('setting includeBboxInPlanning to', includeBboxInPlanning, {\n deepThink,\n noIndividualLocateModel,\n deepLocate,\n });\n\n if (deepLocate && includeBboxInPlanning) {\n console.warn(\n 'deepLocate option is ignored when includeBboxInPlanning is true (same model for planning and default intent without deepThink). Locate is already done during planning.',\n );\n }\n\n const cacheable = opt?.cacheable;\n const replanningCycleLimit = this.resolveReplanningCycleLimit(\n modelConfigForPlanning,\n );\n // if vlm-ui-tars or auto-glm, plan cache is not used\n const isVlmUiTars = isUITars(modelConfigForPlanning.modelFamily);\n const isAutoGlm = isAutoGLM(modelConfigForPlanning.modelFamily);\n const matchedCache =\n isVlmUiTars || isAutoGlm || cacheable === false\n ? undefined\n : await this.taskCache?.matchPlanCache(taskPrompt);\n if (\n matchedCache &&\n this.taskCache?.isCacheResultUsed &&\n matchedCache.cacheContent?.yamlWorkflow?.trim()\n ) {\n // log into report file\n await this.taskExecutor.loadYamlFlowAsPlanning(\n taskPrompt,\n matchedCache.cacheContent.yamlWorkflow,\n );\n\n debug('matched cache, will call .runYaml to run the action');\n const yaml = matchedCache.cacheContent.yamlWorkflow;\n await this.runYaml(yaml);\n return;\n }\n\n // If cache matched but yamlWorkflow is empty, fall through to normal execution\n const imagesIncludeCount: number = deepThink ? 2 : 1;\n const { output: actionOutput } = await this.taskExecutor.action(\n taskPrompt,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n includeBboxInPlanning,\n this.aiActContext,\n cacheable,\n replanningCycleLimit,\n imagesIncludeCount,\n deepThink,\n fileChooserAccept,\n includeBboxInPlanning ? undefined : deepLocate,\n abortSignal,\n );\n\n // update cache\n if (this.taskCache && actionOutput?.yamlFlow && cacheable !== false) {\n const yamlContent: MidsceneYamlScript = {\n tasks: [\n {\n name: taskPrompt,\n flow: actionOutput.yamlFlow,\n },\n ],\n };\n const yamlFlowStr = yaml.dump(yamlContent);\n this.taskCache.updateOrAppendCacheRecord(\n {\n type: 'plan',\n prompt: taskPrompt,\n yamlWorkflow: yamlFlowStr,\n },\n matchedCache,\n );\n }\n\n return actionOutput?.output;\n };\n\n return await runAiAct();\n }\n\n /**\n * @deprecated Use {@link Agent.aiAct} instead.\n */\n async aiAction(taskPrompt: string, opt?: AiActOptions) {\n return this.aiAct(taskPrompt, opt);\n }\n\n async aiQuery<ReturnType = any>(\n demand: ServiceExtractParam,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<ReturnType> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Query',\n demand,\n modelConfig,\n opt,\n );\n return output as ReturnType;\n }\n\n async aiBoolean(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<boolean> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Boolean',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as boolean;\n }\n\n async aiNumber(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<number> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Number',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as number;\n }\n\n async aiString(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<string> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'String',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as string;\n }\n\n async aiAsk(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<string> {\n return this.aiString(prompt, opt);\n }\n\n async describeElementAtPoint(\n center: [number, number],\n opt?: {\n verifyPrompt?: boolean;\n retryLimit?: number;\n deepLocate?: boolean;\n } & LocatorValidatorOption,\n ): Promise<AgentDescribeElementAtPointResult> {\n const { verifyPrompt = true, retryLimit = 3 } = opt || {};\n\n let success = false;\n let retryCount = 0;\n let resultPrompt = '';\n let deepLocate = opt?.deepLocate || false;\n let verifyResult: LocateValidatorResult | undefined;\n\n while (!success && retryCount < retryLimit) {\n if (retryCount >= 2) {\n deepLocate = true;\n }\n debug(\n 'aiDescribe',\n center,\n 'verifyPrompt',\n verifyPrompt,\n 'retryCount',\n retryCount,\n 'deepLocate',\n deepLocate,\n );\n // use same intent as aiLocate\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const text = await this.service.describe(center, modelConfig, {\n deepLocate,\n });\n debug('aiDescribe text', text);\n assert(text.description, `failed to describe element at [${center}]`);\n resultPrompt = text.description;\n\n // Don't pass deepLocate to verification locate — the description was generated\n // from a cropped view (deepLocate describe), but verification should use regular\n // locate on the full screenshot to confirm the description works universally.\n // Passing deepLocate here would trigger AiLocateSection with an element-level\n // description as a section prompt, which is semantically incorrect.\n verifyResult = await this.verifyLocator(\n resultPrompt,\n undefined,\n center,\n opt,\n );\n if (verifyResult.pass) {\n success = true;\n } else {\n retryCount++;\n }\n }\n\n return {\n prompt: resultPrompt,\n deepLocate,\n verifyResult,\n };\n }\n\n async verifyLocator(\n prompt: string,\n locateOpt: LocateOption | undefined,\n expectCenter: [number, number],\n verifyLocateOption?: LocatorValidatorOption,\n ): Promise<LocateValidatorResult> {\n debug('verifyLocator', prompt, locateOpt, expectCenter, verifyLocateOption);\n\n const { center: verifyCenter, rect: verifyRect } = await this.aiLocate(\n prompt,\n locateOpt,\n );\n const distance = distanceOfTwoPoints(expectCenter, verifyCenter);\n const included = includedInRect(expectCenter, verifyRect);\n const pass =\n distance <= (verifyLocateOption?.centerDistanceThreshold || 20) ||\n included;\n const verifyResult = {\n pass,\n rect: verifyRect,\n center: verifyCenter,\n centerDistance: distance,\n };\n debug('aiDescribe verifyResult', verifyResult);\n return verifyResult;\n }\n\n async aiLocate(prompt: TUserPrompt, opt?: LocateOption) {\n const locateParam = buildDetailedLocateParam(prompt, opt);\n assert(locateParam, 'cannot get locate param for aiLocate');\n const locatePlan = locatePlanForLocate(locateParam);\n const plans = [locatePlan];\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n\n const { output } = await this.taskExecutor.runPlans(\n taskTitleStr('Locate', locateParamStr(locateParam)),\n plans,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n );\n\n const { element } = output;\n\n return {\n rect: element?.rect,\n center: element?.center,\n dpr: element?.dpr,\n } as Pick<LocateResultElement, 'rect' | 'center'>;\n }\n\n async aiAssert(\n assertion: TUserPrompt,\n msg?: string,\n opt?: AgentAssertOpt & ServiceExtractOption,\n ) {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n const { textPrompt } = parsePrompt(assertion);\n const assertionText =\n typeof assertion === 'string' ? assertion : assertion.prompt;\n\n let beforeScreenshot: string | undefined;\n let afterScreenshot: string;\n\n const mode = opt?.screenshotMode || 'auto';\n\n switch (mode) {\n case 'manual':\n beforeScreenshot = opt?.beforeScreenshot;\n afterScreenshot = opt?.afterScreenshot!;\n break;\n\n case 'flow':\n beforeScreenshot =\n this.aiActStartScreenshot ||\n this.actionScreenshotHistory[0]?.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'currentOnly':\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'lastAction':\n if (this.actionScreenshotHistory.length > 0) {\n const lastContext =\n this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n beforeScreenshot = lastContext.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n } else {\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n }\n break;\n\n case 'diff':\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'video':\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n default:\n if (opt?.beforeScreenshot && opt?.afterScreenshot) {\n beforeScreenshot = opt.beforeScreenshot;\n afterScreenshot = opt.afterScreenshot;\n } else if (this.actionScreenshotHistory.length > 0) {\n const lastContext =\n this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n beforeScreenshot = lastContext.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n } else {\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n }\n break;\n }\n\n const executeAssertion = async () => {\n if (\n mode === 'diff' &&\n opt?.referenceImages &&\n opt.referenceImages.length > 0\n ) {\n const { AiAssertDiff } = await import('../ai-model/assert.js');\n const diffResult = await AiAssertDiff({\n currentScreenshot: afterScreenshot,\n referenceImages: opt.referenceImages,\n assertion: textPrompt,\n businessContext: opt.businessContext,\n diffThreshold: opt.diffThreshold,\n ignoreRegions: opt.ignoreRegions,\n ignoreDynamicContent: opt.ignoreDynamicContent,\n strictMode: opt.strictMode,\n modelConfig,\n });\n return {\n pass: diffResult.pass,\n thought: diffResult.thought,\n reason: diffResult.reason,\n systemCheckResults: undefined,\n diffDetails: diffResult.diffDetails,\n videoDetails: undefined,\n };\n }\n\n if (mode === 'video' && opt?.currentVideo) {\n const { AiAssertVideo } = await import('../ai-model/assert.js');\n const MAX_DURATION = 5;\n const DEFAULT_FPS = 30;\n const MAX_FRAMES = MAX_DURATION * DEFAULT_FPS;\n\n let videoFrames: string[] = [];\n\n if (opt.currentVideo.frames && opt.currentVideo.frames.length > 0) {\n videoFrames = opt.currentVideo.frames.slice(0, MAX_FRAMES);\n } else if (opt.currentVideo.format === 'url' && opt.currentVideo.url) {\n throw new Error(\n 'Video URL format is not yet supported. Please provide video frames directly.',\n );\n } else {\n throw new Error(\n 'currentVideo.frames is required for video assertion mode',\n );\n }\n\n if (videoFrames.length === 0) {\n throw new Error('No video frames provided for video assertion');\n }\n\n const videoResult = await AiAssertVideo({\n currentVideoFrames: videoFrames,\n assertion: textPrompt,\n businessContext: opt.businessContext,\n videoOptions: opt.videoOptions,\n modelConfig,\n });\n return {\n pass: videoResult.pass,\n thought: videoResult.thought,\n reason: videoResult.reason,\n systemCheckResults: undefined,\n diffDetails: undefined,\n videoDetails: videoResult.videoDetails,\n };\n }\n\n const assertResult = await AiAssertElement({\n beforeScreenshot,\n afterScreenshot,\n assertion: textPrompt,\n businessContext: opt?.businessContext,\n enableSystemCheck: opt?.enableSystemCheck ?? false,\n customSystemCheckRules: opt?.customSystemCheckRules,\n modelConfig,\n });\n return {\n pass: assertResult.pass,\n thought: assertResult.thought,\n reason: assertResult.reason,\n systemCheckResults: assertResult.systemCheckResults,\n diffDetails: undefined,\n };\n };\n\n const runWithRetry = async (): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n systemCheckResults?: any;\n diffDetails?: any;\n }> => {\n const retryOptions = opt?.retryOptions;\n let lastError: Error | undefined;\n let attempts = 0;\n const maxAttempts = retryOptions?.maxRetries\n ? retryOptions.maxRetries + 1\n : 1;\n\n while (attempts < maxAttempts) {\n try {\n attempts++;\n const result = await executeAssertion();\n return {\n pass: result.pass,\n thought: result.thought,\n reason: result.reason,\n systemCheckResults: result.systemCheckResults,\n diffDetails: result.diffDetails,\n };\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempts < maxAttempts && retryOptions?.retryInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, retryOptions.retryInterval),\n );\n }\n }\n }\n\n throw lastError || new Error('Assertion failed after retries');\n };\n\n const startTime = Date.now();\n try {\n const result = await runWithRetry();\n\n const { pass, thought, reason, systemCheckResults, diffDetails } = result;\n\n if (opt?.saveSnapshot) {\n const snapshot = {\n beforeScreenshot,\n afterScreenshot,\n timestamp: Date.now(),\n assertion: assertionText,\n };\n if (opt?.snapshotPath) {\n const fs = await import('node:fs');\n const path = await import('node:path');\n const snapshotDir = path.dirname(opt.snapshotPath);\n if (!fs.existsSync(snapshotDir)) {\n fs.mkdirSync(snapshotDir, { recursive: true });\n }\n fs.writeFileSync(opt.snapshotPath, JSON.stringify(snapshot, null, 2));\n }\n }\n\n const endTime = Date.now();\n const now = Date.now();\n\n const beforeScreenshotItem = beforeScreenshot\n ? ScreenshotItem.create(beforeScreenshot, now)\n : undefined;\n const afterScreenshotItem = ScreenshotItem.create(afterScreenshot, now);\n\n const serviceDump: ServiceDump = {\n type: 'assert',\n logTime: now,\n logId: `assert-${now}`,\n userQuery: {\n assertion: assertion,\n },\n matchedElement: [],\n data: null,\n assertionPass: pass,\n assertionThought: thought,\n assertionReason: reason,\n beforeScreenshot: beforeScreenshot,\n systemCheckResults: systemCheckResults,\n taskInfo: {\n durationMs: endTime - startTime,\n },\n };\n\n const recorder: ExecutionRecorderItem[] = [];\n if (beforeScreenshotItem) {\n recorder.push({\n type: 'screenshot',\n ts: now,\n screenshot: beforeScreenshotItem,\n timing: 'before',\n });\n }\n recorder.push({\n type: 'screenshot',\n ts: now,\n screenshot: afterScreenshotItem,\n timing: 'after',\n });\n\n const assertionTask: ExecutionTask = {\n taskId: uuid(),\n type: 'Insight',\n subType: 'Assertion',\n status: 'finished',\n recorder,\n timing: {\n start: startTime,\n end: endTime,\n cost: endTime - startTime,\n },\n param: {\n assertion: assertionText,\n },\n output: {\n pass,\n thought,\n },\n log: serviceDump,\n executor: async () => {},\n };\n\n const executionDump = new ExecutionDump({\n logTime: now,\n name: taskTitleStr('Assert', assertionText),\n tasks: [assertionTask],\n });\n\n this.appendExecutionDump(executionDump);\n\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n this.writeOutActionDumps();\n\n const message = pass\n ? undefined\n : `Assertion failed: ${msg || assertionText}\\nReason: ${reason || thought || '(no_reason)'}`;\n\n if (opt?.keepRawResponse) {\n return {\n pass,\n thought,\n reason,\n systemCheckResults,\n diffDetails,\n message,\n };\n }\n\n if (!pass) {\n throw new Error(message);\n }\n } catch (error) {\n if (error instanceof TaskExecutionError) {\n const errorTask = error.errorTask;\n const thought = errorTask?.thought;\n const rawError = errorTask?.error;\n const rawMessage =\n errorTask?.errorMessage ||\n (rawError instanceof Error\n ? rawError.message\n : rawError\n ? String(rawError)\n : undefined);\n const reason = thought || rawMessage || '(no_reason)';\n const message = `Assertion failed: ${msg || assertionText}\\nReason: ${reason}`;\n\n if (opt?.keepRawResponse) {\n return {\n pass: false,\n thought,\n reason,\n message,\n };\n }\n\n throw new Error(message, {\n cause: rawError ?? error,\n });\n }\n\n throw error;\n }\n }\n\n /**\n * Clear the action screenshot history\n * Useful when starting a new test scenario\n */\n clearActionScreenshotHistory() {\n this.actionScreenshotHistory = [];\n this.aiActStartScreenshot = null;\n }\n\n /**\n * Get the last action screenshot context\n */\n getLastActionScreenshotContext(): ActionScreenshotContext | undefined {\n return this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n }\n\n /**\n * Get the flow start screenshot (from aiAct)\n */\n getFlowStartScreenshot(): string | null {\n return this.aiActStartScreenshot;\n }\n\n async aiWaitFor(assertion: TUserPrompt, opt?: AgentWaitForOpt) {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n await this.taskExecutor.waitFor(\n assertion,\n {\n ...opt,\n timeoutMs: opt?.timeoutMs || 15 * 1000,\n checkIntervalMs: opt?.checkIntervalMs || 3 * 1000,\n },\n modelConfig,\n );\n }\n\n async ai(...args: Parameters<typeof this.aiAct>) {\n return this.aiAct(...args);\n }\n\n async runYaml(yamlScriptContent: string): Promise<{\n result: Record<string, any>;\n }> {\n const script = parseYamlScript(yamlScriptContent, 'yaml');\n const player = new ScriptPlayer(script, async () => {\n return { agent: this, freeFn: [] };\n });\n await player.run();\n\n if (player.status === 'error') {\n const errors = player.taskStatusList\n .filter((task) => task.status === 'error')\n .map((task) => {\n return `task - ${task.name}: ${task.error?.message}`;\n })\n .join('\\n');\n throw new Error(`Error(s) occurred in running yaml script:\\n${errors}`);\n }\n\n return {\n result: player.result,\n };\n }\n\n async evaluateJavaScript(script: string) {\n assert(\n this.interface.evaluateJavaScript,\n 'evaluateJavaScript is not supported in current agent',\n );\n return this.interface.evaluateJavaScript(script);\n }\n\n /**\n * Add a dump update listener\n * @param listener Listener function\n * @returns A remove function that can be called to remove this listener\n */\n addDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): () => void {\n this.dumpUpdateListeners.push(listener);\n\n // Return remove function\n return () => {\n this.removeDumpUpdateListener(listener);\n };\n }\n\n /**\n * Remove a dump update listener\n * @param listener The listener function to remove\n */\n removeDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n const index = this.dumpUpdateListeners.indexOf(listener);\n if (index > -1) {\n this.dumpUpdateListeners.splice(index, 1);\n }\n }\n\n /**\n * Clear all dump update listeners\n */\n clearDumpUpdateListeners(): void {\n this.dumpUpdateListeners = [];\n }\n\n async destroy() {\n // Early return if already destroyed\n if (this.destroyed) {\n return;\n }\n\n // Wait for all queued write operations to complete\n await this.reportGenerator.flush();\n\n await this.reportGenerator.finalize(this.dump);\n this.reportFile = this.reportGenerator.getReportPath();\n\n await this.interface.destroy?.();\n this.resetDump(); // reset dump to release memory\n this.destroyed = true;\n }\n\n async recordToReport(\n title?: string,\n opt?: {\n content: string;\n },\n ) {\n // 1. screenshot\n const base64 = await this.interface.screenshotBase64();\n const now = Date.now();\n const screenshot = ScreenshotItem.create(base64, now);\n // 2. build recorder\n const recorder: ExecutionRecorderItem[] = [\n {\n type: 'screenshot',\n ts: now,\n screenshot,\n },\n ];\n // 3. build ExecutionTaskLog\n const task: ExecutionTaskLog = {\n taskId: uuid(),\n type: 'Log',\n subType: 'Screenshot',\n status: 'finished',\n recorder,\n timing: {\n start: now,\n end: now,\n cost: 0,\n },\n param: {\n content: opt?.content || '',\n },\n executor: async () => {},\n };\n // 4. build ExecutionDump\n const executionDump = new ExecutionDump({\n logTime: now,\n name: `Log - ${title || 'untitled'}`,\n description: opt?.content || '',\n tasks: [task],\n });\n // 5. append to execution dump\n this.appendExecutionDump(executionDump);\n\n // Call all registered dump update listeners\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n this.writeOutActionDumps();\n await this.reportGenerator.flush();\n }\n\n /**\n * @deprecated Use {@link Agent.recordToReport} instead.\n */\n async logScreenshot(\n title?: string,\n opt?: {\n content: string;\n },\n ) {\n await this.recordToReport(title, opt);\n }\n\n _unstableLogContent() {\n const { groupName, groupDescription, executions } = this.dump;\n return {\n groupName,\n groupDescription,\n executions: executions || [],\n };\n }\n\n /**\n * Freezes the current page context to be reused in subsequent AI operations\n * This avoids recalculating page context for each operation\n */\n async freezePageContext(): Promise<void> {\n debug('Freezing page context');\n const context = await this._snapshotContext();\n // Mark the context as frozen\n context._isFrozen = true;\n this.frozenUIContext = context;\n debug('Page context frozen successfully');\n }\n\n /**\n * Unfreezes the page context, allowing AI operations to calculate context dynamically\n */\n async unfreezePageContext(): Promise<void> {\n debug('Unfreezing page context');\n this.frozenUIContext = undefined;\n debug('Page context unfrozen successfully');\n }\n\n /**\n * Process cache configuration and return normalized cache settings\n */\n private processCacheConfig(opts: AgentOpt): {\n id: string;\n enabled: boolean;\n readOnly: boolean;\n writeOnly: boolean;\n cacheAdapter?: import('./cache-adapter').CacheAdapter;\n } | null {\n // Validate original cache config before processing\n // Agent requires explicit IDs - don't allow auto-generation\n if (opts.cache === true) {\n throw new Error(\n 'cache: true requires an explicit cache ID. Please provide:\\n' +\n 'Example: cache: { id: \"my-cache-id\" }',\n );\n }\n\n // Check if cache config object is missing ID\n if (\n opts.cache &&\n typeof opts.cache === 'object' &&\n opts.cache !== null &&\n !opts.cache.id\n ) {\n throw new Error(\n 'cache configuration requires an explicit id.\\n' +\n 'Example: cache: { id: \"my-cache-id\" }',\n );\n }\n\n // Use the unified utils function to process cache configuration\n const cacheConfig = processCacheConfig(\n opts.cache,\n opts.cacheId || opts.testId || 'default',\n );\n\n if (!cacheConfig) {\n return null;\n }\n\n // Handle cache configuration object\n if (typeof cacheConfig === 'object' && cacheConfig !== null) {\n const id = cacheConfig.id;\n const rawStrategy = cacheConfig.strategy as unknown;\n let strategyValue: string;\n\n if (rawStrategy === undefined) {\n strategyValue = 'read-write';\n } else if (typeof rawStrategy === 'string') {\n strategyValue = rawStrategy;\n } else {\n throw new Error(\n `cache.strategy must be a string when provided, but received type ${typeof rawStrategy}`,\n );\n }\n\n if (!isValidCacheStrategy(strategyValue)) {\n throw new Error(\n `cache.strategy must be one of ${CACHE_STRATEGY_VALUES}, but received \"${strategyValue}\"`,\n );\n }\n\n const isReadOnly = strategyValue === 'read-only';\n const isWriteOnly = strategyValue === 'write-only';\n\n return {\n id,\n enabled: !isWriteOnly,\n readOnly: isReadOnly,\n writeOnly: isWriteOnly,\n cacheAdapter: cacheConfig.cacheAdapter,\n };\n }\n\n return null;\n }\n\n private normalizeFilePaths(files: string[]): string[] {\n if (ifInBrowser) {\n throw new Error('File chooser is not supported in browser environment');\n }\n\n return files.map((file) => {\n const absolutePath = resolve(file);\n if (!existsSync(absolutePath)) {\n throw new Error(`File not found: ${file}`);\n }\n return absolutePath;\n });\n }\n\n private normalizeFileInput(files: string | string[]): string[] {\n const filesArray = Array.isArray(files) ? files : [files];\n return this.normalizeFilePaths(filesArray);\n }\n\n /**\n * Manually flush cache to file\n * @param options - Optional configuration\n * @param options.cleanUnused - If true, removes unused cache records before flushing\n */\n async flushCache(options?: { cleanUnused?: boolean }): Promise<void> {\n if (!this.taskCache) {\n throw new Error('Cache is not configured');\n }\n\n this.taskCache.flushCacheToFile(options);\n }\n}\n\nexport const createAgent = (\n interfaceInstance: AbstractInterface,\n opts?: AgentOpt,\n) => {\n return new Agent(interfaceInstance, opts);\n};\n"],"names":["debug","getDebug","distanceOfTwoPoints","p1","p2","x1","y1","x2","y2","Math","includedInRect","point","rect","x","y","left","top","width","height","defaultServiceExtractOption","CACHE_STRATEGIES","isValidCacheStrategy","strategy","value","CACHE_STRATEGY_VALUES","legacyScrollTypeMap","normalizeScrollType","scrollType","defaultReplanningCycleLimit","defaultVlmUiTarsReplanningCycleLimit","defaultAutoGlmReplanningCycleLimit","Agent","callback","modelConfigForPlanning","undefined","isUITars","isAutoGLM","action","context","commonContextParser","prompt","console","GroupedActionDump","getVersion","WeakMap","execution","runner","currentDump","existingIndex","opt","ifInBrowser","reportHTMLContent","task","param","paramStr","tip","typeStr","name","type","beforeScreenshot","actionPlan","plans","Boolean","title","taskTitleStr","locateParamStr","defaultIntentModelConfig","output","afterScreenshot","Date","locatePrompt","assert","detailedLocateParam","buildDetailedLocateParam","fileChooserAccept","withFileChooser","locatePromptOrValue","locatePromptOrOpt","optOrUndefined","optWithValue","stringValue","String","mode","locatePromptOrKeyName","keyName","locatePromptOrScrollParam","scrollParam","normalizedScrollType","taskPrompt","abortSignal","Error","runAiAct","deepThink","deepLocate","noIndividualLocateModel","includeBboxInPlanning","cacheable","replanningCycleLimit","isVlmUiTars","isAutoGlm","matchedCache","yaml","imagesIncludeCount","actionOutput","yamlContent","yamlFlowStr","demand","modelConfig","textPrompt","multimodalPrompt","parsePrompt","center","verifyPrompt","retryLimit","success","retryCount","resultPrompt","verifyResult","text","locateOpt","expectCenter","verifyLocateOption","verifyCenter","verifyRect","distance","included","pass","locateParam","locatePlan","locatePlanForLocate","element","assertion","msg","assertionText","lastContext","executeAssertion","AiAssertDiff","diffResult","AiAssertVideo","MAX_DURATION","DEFAULT_FPS","MAX_FRAMES","videoFrames","videoResult","assertResult","AiAssertElement","runWithRetry","retryOptions","lastError","attempts","maxAttempts","result","error","Promise","resolve","setTimeout","startTime","thought","reason","systemCheckResults","diffDetails","snapshot","fs","path","snapshotDir","JSON","endTime","now","beforeScreenshotItem","ScreenshotItem","afterScreenshotItem","serviceDump","recorder","assertionTask","uuid","executionDump","ExecutionDump","dumpString","listener","message","TaskExecutionError","errorTask","rawError","rawMessage","args","yamlScriptContent","script","parseYamlScript","player","ScriptPlayer","errors","index","base64","screenshot","groupName","groupDescription","executions","opts","cacheConfig","processCacheConfig","id","rawStrategy","strategyValue","isReadOnly","isWriteOnly","files","file","absolutePath","existsSync","filesArray","Array","options","interfaceInstance","envReplanningCycleLimit","globalConfigManager","MIDSCENE_REPLANNING_CYCLE_LIMIT","Object","Number","resolvedAiActContext","hasCustomConfig","ModelConfigManager","globalModelConfigManager","Service","cacheConfigObj","TaskCache","baseActionSpace","defineActionSleep","TaskExecutor","getReportFileName","ReportGenerator","createAgent"],"mappings":"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"}
1
+ {"version":3,"file":"agent/agent.mjs","sources":["../../../src/agent/agent.ts"],"sourcesContent":["import { AiAssertElement } from '../ai-model/assert';\nimport type { TUserPrompt } from '../ai-model/index';\nimport { ScreenshotItem } from '../screenshot-item';\nimport Service from '../service/index';\n// Import types and values directly from their source files to avoid circular dependency\n// DO NOT import from '../index' as it creates a circular dependency:\n// index.ts -> agent/index.ts -> agent/agent.ts -> index.ts\nimport {\n type ActionParam,\n type ActionReturn,\n type ActionScreenshotContext,\n type AgentAssertOpt,\n type AgentDescribeElementAtPointResult,\n type AgentOpt,\n type AgentWaitForOpt,\n type CacheConfig,\n type DeepThinkOption,\n type DeviceAction,\n ExecutionDump,\n type ExecutionRecorderItem,\n type ExecutionTask,\n type ExecutionTaskLog,\n GroupedActionDump,\n type LocateOption,\n type LocateResultElement,\n type LocateValidatorResult,\n type LocatorValidatorOption,\n type OnTaskStartTip,\n type PlanningAction,\n type Rect,\n type ScrollParam,\n type ServiceAction,\n type ServiceDump,\n type ServiceExtractOption,\n type ServiceExtractParam,\n type SystemCheckResults,\n type UIContext,\n} from '../types';\nimport type { MidsceneYamlScript } from '../yaml';\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\nimport { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport yaml from 'js-yaml';\n\nimport type { IReportGenerator } from '@/report-generator';\nimport { ReportGenerator } from '@/report-generator';\nimport { getVersion, processCacheConfig, reportHTMLContent } from '@/utils';\nimport {\n ScriptPlayer,\n buildDetailedLocateParam,\n parseYamlScript,\n} from '../yaml/index';\n\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { AbstractInterface } from '@/device';\nimport type { TaskRunner } from '@/task-runner';\nimport {\n type IModelConfig,\n MIDSCENE_REPLANNING_CYCLE_LIMIT,\n ModelConfigManager,\n globalConfigManager,\n globalModelConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { defineActionSleep } from '../device';\nimport { TaskCache } from './task-cache';\nimport {\n TaskExecutionError,\n TaskExecutor,\n locatePlanForLocate,\n withFileChooser,\n} from './tasks';\nimport { locateParamStr, paramStr, taskTitleStr, typeStr } from './ui-utils';\nimport { commonContextParser, getReportFileName, parsePrompt } from './utils';\n\nconst debug = getDebug('agent');\n\nconst distanceOfTwoPoints = (p1: [number, number], p2: [number, number]) => {\n const [x1, y1] = p1;\n const [x2, y2] = p2;\n return Math.round(Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2));\n};\n\nconst includedInRect = (point: [number, number], rect: Rect) => {\n const [x, y] = point;\n const { left, top, width, height } = rect;\n return x >= left && x <= left + width && y >= top && y <= top + height;\n};\n\nconst defaultServiceExtractOption: ServiceExtractOption = {\n domIncluded: false,\n screenshotIncluded: true,\n};\n\ntype CacheStrategy = NonNullable<CacheConfig['strategy']>;\n\nconst CACHE_STRATEGIES: readonly CacheStrategy[] = [\n 'read-only',\n 'read-write',\n 'write-only',\n];\n\nconst isValidCacheStrategy = (strategy: string): strategy is CacheStrategy =>\n CACHE_STRATEGIES.some((value) => value === strategy);\n\nconst CACHE_STRATEGY_VALUES = CACHE_STRATEGIES.map(\n (value) => `\"${value}\"`,\n).join(', ');\n\nconst legacyScrollTypeMap = {\n once: 'singleAction',\n untilBottom: 'scrollToBottom',\n untilTop: 'scrollToTop',\n untilRight: 'scrollToRight',\n untilLeft: 'scrollToLeft',\n} as const;\n\ntype LegacyScrollType = keyof typeof legacyScrollTypeMap;\n\nconst normalizeScrollType = (\n scrollType: ScrollParam['scrollType'] | LegacyScrollType | undefined,\n): ScrollParam['scrollType'] | undefined => {\n if (!scrollType) {\n return scrollType;\n }\n\n if (scrollType in legacyScrollTypeMap) {\n return legacyScrollTypeMap[scrollType as LegacyScrollType];\n }\n\n return scrollType as ScrollParam['scrollType'];\n};\n\nconst defaultReplanningCycleLimit = 20;\nconst defaultVlmUiTarsReplanningCycleLimit = 40;\nconst defaultAutoGlmReplanningCycleLimit = 100;\n\nexport type AiActOptions = {\n cacheable?: boolean;\n fileChooserAccept?: string | string[];\n deepThink?: DeepThinkOption;\n deepLocate?: boolean;\n abortSignal?: AbortSignal;\n};\n\nexport class Agent<\n InterfaceType extends AbstractInterface = AbstractInterface,\n> {\n interface: InterfaceType;\n\n service: Service;\n\n dump: GroupedActionDump;\n\n reportFile?: string | null;\n\n reportFileName?: string;\n\n taskExecutor: TaskExecutor;\n\n opts: AgentOpt;\n\n /**\n * If true, the agent will not perform any actions\n */\n dryMode = false;\n\n onTaskStartTip?: OnTaskStartTip;\n\n taskCache?: TaskCache;\n\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n get onDumpUpdate():\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined {\n return this.dumpUpdateListeners[0];\n }\n\n set onDumpUpdate(callback:\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined) {\n // Clear existing listeners\n this.dumpUpdateListeners = [];\n // Add callback to array if provided\n if (callback) {\n this.dumpUpdateListeners.push(callback);\n }\n }\n\n destroyed = false;\n\n modelConfigManager: ModelConfigManager;\n\n /**\n * Frozen page context for consistent AI operations\n */\n private frozenUIContext?: UIContext;\n\n private get aiActContext(): string | undefined {\n return this.opts.aiActContext ?? this.opts.aiActionContext;\n }\n\n /**\n * Flag to track if VL model warning has been shown\n */\n private hasWarnedNonVLModel = false;\n\n private executionDumpIndexByRunner = new WeakMap<TaskRunner, number>();\n\n private fullActionSpace: DeviceAction[];\n\n private reportGenerator: IReportGenerator;\n\n /**\n * Action screenshot context history for assertion\n * Stores the last N action screenshots for comparison in assertions\n */\n private actionScreenshotHistory: ActionScreenshotContext[] = [];\n\n /**\n * Maximum number of action screenshots to keep in history\n */\n private readonly maxScreenshotHistoryLength = 5;\n\n /**\n * Screenshot at the start of aiAct for flow-level assertion\n */\n private aiActStartScreenshot: string | null = null;\n\n // @deprecated use .interface instead\n get page() {\n return this.interface;\n }\n\n /**\n * Ensures VL model warning is shown once when needed\n */\n private ensureVLModelWarning() {\n if (\n !this.hasWarnedNonVLModel &&\n this.interface.interfaceType !== 'puppeteer' &&\n this.interface.interfaceType !== 'playwright' &&\n this.interface.interfaceType !== 'static' &&\n this.interface.interfaceType !== 'chrome-extension-proxy' &&\n this.interface.interfaceType !== 'page-over-chrome-extension-bridge'\n ) {\n this.modelConfigManager.throwErrorIfNonVLModel();\n this.hasWarnedNonVLModel = true;\n }\n }\n\n private resolveReplanningCycleLimit(\n modelConfigForPlanning: IModelConfig,\n ): number {\n if (this.opts.replanningCycleLimit !== undefined) {\n return this.opts.replanningCycleLimit;\n }\n\n return isUITars(modelConfigForPlanning.modelFamily)\n ? defaultVlmUiTarsReplanningCycleLimit\n : isAutoGLM(modelConfigForPlanning.modelFamily)\n ? defaultAutoGlmReplanningCycleLimit\n : defaultReplanningCycleLimit;\n }\n\n constructor(interfaceInstance: InterfaceType, opts?: AgentOpt) {\n this.interface = interfaceInstance;\n\n const envReplanningCycleLimit =\n globalConfigManager.getEnvConfigValueAsNumber(\n MIDSCENE_REPLANNING_CYCLE_LIMIT,\n );\n\n this.opts = Object.assign(\n {\n generateReport: true,\n autoPrintReportMsg: true,\n groupName: 'Midscene Report',\n groupDescription: '',\n },\n opts || {},\n opts?.replanningCycleLimit === undefined &&\n envReplanningCycleLimit !== undefined &&\n !Number.isNaN(envReplanningCycleLimit)\n ? { replanningCycleLimit: envReplanningCycleLimit }\n : {},\n );\n\n const resolvedAiActContext =\n this.opts.aiActContext ?? this.opts.aiActionContext;\n if (resolvedAiActContext !== undefined) {\n this.opts.aiActContext = resolvedAiActContext;\n this.opts.aiActionContext ??= resolvedAiActContext;\n }\n\n if (\n opts?.modelConfig &&\n (typeof opts?.modelConfig !== 'object' || Array.isArray(opts.modelConfig))\n ) {\n throw new Error(\n `opts.modelConfig must be a plain object map of env keys to values, but got ${typeof opts?.modelConfig}`,\n );\n }\n // Create ModelConfigManager if modelConfig or createOpenAIClient is provided\n // Otherwise, use the global config manager\n const hasCustomConfig = opts?.modelConfig || opts?.createOpenAIClient;\n this.modelConfigManager = hasCustomConfig\n ? new ModelConfigManager(opts?.modelConfig, opts?.createOpenAIClient)\n : globalModelConfigManager;\n\n this.onTaskStartTip = this.opts.onTaskStartTip;\n\n this.service = new Service(async () => {\n return this.getUIContext();\n });\n\n // Process cache configuration\n const cacheConfigObj = this.processCacheConfig(opts || {});\n if (cacheConfigObj) {\n this.taskCache = new TaskCache(\n cacheConfigObj.id,\n cacheConfigObj.enabled,\n cacheConfigObj.cacheAdapter,\n {\n readOnly: cacheConfigObj.readOnly,\n writeOnly: cacheConfigObj.writeOnly,\n },\n );\n }\n\n const baseActionSpace = this.interface.actionSpace();\n this.fullActionSpace = [...baseActionSpace, defineActionSleep()];\n\n this.taskExecutor = new TaskExecutor(this.interface, this.service, {\n taskCache: this.taskCache,\n onTaskStart: this.callbackOnTaskStartTip.bind(this),\n replanningCycleLimit: this.opts.replanningCycleLimit,\n waitAfterAction: this.opts.waitAfterAction,\n useDeviceTimestamp: this.opts.useDeviceTimestamp,\n actionSpace: this.fullActionSpace,\n hooks: {\n onTaskUpdate: (runner) => {\n const executionDump = runner.dump();\n this.appendExecutionDump(executionDump, runner);\n\n // Call all registered dump update listeners\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n // Fire and forget - don't block task execution\n this.writeOutActionDumps();\n },\n },\n });\n this.dump = this.resetDump();\n this.reportFileName =\n opts?.reportFileName ||\n getReportFileName(opts?.testId || this.interface.interfaceType || 'web');\n\n this.reportGenerator = ReportGenerator.create(this.reportFileName!, {\n generateReport: this.opts.generateReport,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n });\n }\n\n async getActionSpace(): Promise<DeviceAction[]> {\n return this.fullActionSpace;\n }\n\n async getUIContext(action?: ServiceAction): Promise<UIContext> {\n // Check VL model configuration when UI context is first needed\n this.ensureVLModelWarning();\n\n // If page context is frozen, return the frozen context for all actions\n if (this.frozenUIContext) {\n debug('Using frozen page context for action:', action);\n return this.frozenUIContext;\n }\n\n // Get original context\n const context = await commonContextParser(this.interface, {\n uploadServerUrl: this.modelConfigManager.getUploadTestServerUrl(),\n screenshotShrinkFactor: this.opts.screenshotShrinkFactor,\n });\n\n return context;\n }\n\n async _snapshotContext(): Promise<UIContext> {\n return await this.getUIContext('locate');\n }\n\n /**\n * @deprecated Use {@link setAIActContext} instead.\n */\n async setAIActionContext(prompt: string) {\n await this.setAIActContext(prompt);\n }\n\n async setAIActContext(prompt: string) {\n if (this.aiActContext) {\n console.warn(\n 'aiActContext is already set, and it is called again, will override the previous setting',\n );\n }\n this.opts.aiActContext = prompt;\n this.opts.aiActionContext = prompt;\n }\n\n resetDump() {\n this.dump = new GroupedActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.interface.interfaceType,\n });\n this.executionDumpIndexByRunner = new WeakMap<TaskRunner, number>();\n\n return this.dump;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.executionDumpIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.executionDumpIndexByRunner.set(\n runner,\n currentDump.executions.length - 1,\n );\n return;\n }\n currentDump.executions.push(execution);\n }\n\n dumpDataString(opt?: { inlineScreenshots?: boolean }) {\n // update dump info\n this.dump.groupName = this.opts.groupName!;\n this.dump.groupDescription = this.opts.groupDescription;\n // In browser environment, use inline screenshots since file system is not available\n if (ifInBrowser || opt?.inlineScreenshots) {\n return this.dump.serializeWithInlineScreenshots();\n }\n return this.dump.serialize();\n }\n\n reportHTMLString(opt?: { inlineScreenshots?: boolean }) {\n // dumpDataString() handles browser environment with inline screenshots\n return reportHTMLContent(this.dumpDataString(opt));\n }\n\n writeOutActionDumps() {\n this.reportGenerator.onDumpUpdate(this.dump);\n this.reportFile = this.reportGenerator.getReportPath();\n }\n\n private async callbackOnTaskStartTip(task: ExecutionTask) {\n const param = paramStr(task);\n const tip = param ? `${typeStr(task)} - ${param}` : typeStr(task);\n\n if (this.onTaskStartTip) {\n await this.onTaskStartTip(tip);\n }\n }\n\n wrapActionInActionSpace<T extends DeviceAction>(\n name: string,\n ): (param: ActionParam<T>) => Promise<ActionReturn<T>> {\n return async (param: ActionParam<T>) => {\n return await this.callActionInActionSpace<ActionReturn<T>>(name, param);\n };\n }\n\n async callActionInActionSpace<T = any>(\n type: string,\n opt?: T, // and all other action params\n ) {\n debug('callActionInActionSpace', type, ',', opt);\n\n const beforeScreenshot = await this.interface.screenshotBase64();\n\n const actionPlan: PlanningAction<T> = {\n type: type as any,\n param: (opt as any) || {},\n thought: '',\n };\n debug('actionPlan', actionPlan);\n\n const plans: PlanningAction[] = [actionPlan].filter(\n Boolean,\n ) as PlanningAction[];\n\n const title = taskTitleStr(\n type as any,\n locateParamStr((opt as any)?.locate || {}),\n );\n\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n\n const { output } = await this.taskExecutor.runPlans(\n title,\n plans,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n );\n\n const afterScreenshot = await this.interface.screenshotBase64();\n\n this.actionScreenshotHistory.push({\n beforeScreenshot,\n afterScreenshot,\n actionType: type,\n actionParam: opt,\n timestamp: Date.now(),\n });\n\n if (this.actionScreenshotHistory.length > this.maxScreenshotHistoryLength) {\n this.actionScreenshotHistory.shift();\n }\n\n return output;\n }\n\n async aiTap(\n locatePrompt: TUserPrompt,\n opt?: LocateOption & { fileChooserAccept?: string | string[] },\n ) {\n assert(locatePrompt, 'missing locate prompt for tap');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n const fileChooserAccept = opt?.fileChooserAccept\n ? this.normalizeFileInput(opt.fileChooserAccept)\n : undefined;\n\n return withFileChooser(this.interface, fileChooserAccept, async () => {\n return this.callActionInActionSpace('Tap', {\n locate: detailedLocateParam,\n });\n });\n }\n\n async aiRightClick(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for right click');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('RightClick', {\n locate: detailedLocateParam,\n });\n }\n\n async aiDoubleClick(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for double click');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('DoubleClick', {\n locate: detailedLocateParam,\n });\n }\n\n async aiHover(locatePrompt: TUserPrompt, opt?: LocateOption) {\n assert(locatePrompt, 'missing locate prompt for hover');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n return this.callActionInActionSpace('Hover', {\n locate: detailedLocateParam,\n });\n }\n\n // New signature, always use locatePrompt as the first param\n async aiInput(\n locatePrompt: TUserPrompt,\n opt: LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' },\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiInput(locatePrompt, opt) instead where opt contains the value\n */\n async aiInput(\n value: string | number,\n locatePrompt: TUserPrompt,\n opt?: LocateOption & { autoDismissKeyboard?: boolean } & {\n mode?: 'replace' | 'clear' | 'typeOnly' | 'append';\n }, // AndroidDeviceInputOpt &\n ): Promise<any>;\n\n // Implementation\n async aiInput(\n locatePromptOrValue: TUserPrompt | string | number,\n locatePromptOrOpt:\n | TUserPrompt\n | (LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' }) // AndroidDeviceInputOpt &\n | undefined,\n optOrUndefined?: LocateOption, // AndroidDeviceInputOpt &\n ) {\n let value: string | number;\n let locatePrompt: TUserPrompt;\n let opt:\n | (LocateOption & { value: string | number } & {\n autoDismissKeyboard?: boolean;\n } & { mode?: 'replace' | 'clear' | 'typeOnly' | 'append' }) // AndroidDeviceInputOpt &\n | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has value)\n if (\n typeof locatePromptOrOpt === 'object' &&\n locatePromptOrOpt !== null &&\n 'value' in locatePromptOrOpt\n ) {\n // New signature: aiInput(locatePrompt, opt)\n locatePrompt = locatePromptOrValue as TUserPrompt;\n const optWithValue = locatePromptOrOpt as LocateOption & {\n // AndroidDeviceInputOpt &\n value: string | number;\n autoDismissKeyboard?: boolean;\n };\n value = optWithValue.value;\n opt = optWithValue;\n } else {\n // Legacy signature: aiInput(value, locatePrompt, opt)\n value = locatePromptOrValue as string | number;\n locatePrompt = locatePromptOrOpt as TUserPrompt;\n opt = {\n ...optOrUndefined,\n value,\n };\n }\n\n assert(\n typeof value === 'string' || typeof value === 'number',\n 'input value must be a string or number, use empty string if you want to clear the input',\n );\n assert(locatePrompt, 'missing locate prompt for input');\n\n const detailedLocateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n // Convert value to string to ensure consistency\n const stringValue = typeof value === 'number' ? String(value) : value;\n\n // backward compat: convert deprecated 'append' to 'typeOnly'\n const mode = opt?.mode === 'append' ? 'typeOnly' : opt?.mode;\n\n return this.callActionInActionSpace('Input', {\n ...(opt || {}),\n value: stringValue,\n locate: detailedLocateParam,\n mode,\n });\n }\n\n // New signature\n async aiKeyboardPress(\n locatePrompt: TUserPrompt,\n opt: LocateOption & { keyName: string },\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiKeyboardPress(locatePrompt, opt) instead where opt contains the keyName\n */\n async aiKeyboardPress(\n keyName: string,\n locatePrompt?: TUserPrompt,\n opt?: LocateOption,\n ): Promise<any>;\n\n // Implementation\n async aiKeyboardPress(\n locatePromptOrKeyName: TUserPrompt | string,\n locatePromptOrOpt:\n | TUserPrompt\n | (LocateOption & { keyName: string })\n | undefined,\n optOrUndefined?: LocateOption,\n ) {\n let keyName: string;\n let locatePrompt: TUserPrompt | undefined;\n let opt: (LocateOption & { keyName: string }) | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has keyName)\n if (\n typeof locatePromptOrOpt === 'object' &&\n locatePromptOrOpt !== null &&\n 'keyName' in locatePromptOrOpt\n ) {\n // New signature: aiKeyboardPress(locatePrompt, opt)\n locatePrompt = locatePromptOrKeyName as TUserPrompt;\n opt = locatePromptOrOpt as LocateOption & {\n keyName: string;\n };\n } else {\n // Legacy signature: aiKeyboardPress(keyName, locatePrompt, opt)\n keyName = locatePromptOrKeyName as string;\n locatePrompt = locatePromptOrOpt as TUserPrompt | undefined;\n opt = {\n ...(optOrUndefined || {}),\n keyName,\n };\n }\n\n assert(opt?.keyName, 'missing keyName for keyboard press');\n\n const detailedLocateParam = locatePrompt\n ? buildDetailedLocateParam(locatePrompt, opt)\n : undefined;\n\n return this.callActionInActionSpace('KeyboardPress', {\n ...(opt || {}),\n locate: detailedLocateParam,\n });\n }\n\n // New signature\n async aiScroll(\n locatePrompt: TUserPrompt | undefined,\n opt: LocateOption & ScrollParam,\n ): Promise<any>;\n\n // Legacy signature - deprecated\n /**\n * @deprecated Use aiScroll(locatePrompt, opt) instead where opt contains the scroll parameters\n */\n async aiScroll(\n scrollParam: ScrollParam,\n locatePrompt?: TUserPrompt,\n opt?: LocateOption,\n ): Promise<any>;\n\n // Implementation\n async aiScroll(\n locatePromptOrScrollParam: TUserPrompt | ScrollParam | undefined,\n locatePromptOrOpt: TUserPrompt | (LocateOption & ScrollParam) | undefined,\n optOrUndefined?: LocateOption,\n ) {\n let scrollParam: ScrollParam;\n let locatePrompt: TUserPrompt | undefined;\n let opt: LocateOption | undefined;\n\n // Check if using new signature (first param is locatePrompt, second has scroll params)\n if (\n typeof locatePromptOrOpt === 'object' &&\n ('direction' in locatePromptOrOpt ||\n 'scrollType' in locatePromptOrOpt ||\n 'distance' in locatePromptOrOpt)\n ) {\n // New signature: aiScroll(locatePrompt, opt)\n locatePrompt = locatePromptOrScrollParam as TUserPrompt;\n opt = locatePromptOrOpt as LocateOption & ScrollParam;\n } else {\n // Legacy signature: aiScroll(scrollParam, locatePrompt, opt)\n scrollParam = locatePromptOrScrollParam as ScrollParam;\n locatePrompt = locatePromptOrOpt as TUserPrompt | undefined;\n opt = {\n ...(optOrUndefined || {}),\n ...(scrollParam || {}),\n };\n }\n\n if (opt) {\n const normalizedScrollType = normalizeScrollType(\n (opt as ScrollParam).scrollType as\n | ScrollParam['scrollType']\n | LegacyScrollType\n | undefined,\n );\n\n if (normalizedScrollType !== (opt as ScrollParam).scrollType) {\n (opt as ScrollParam) = {\n ...(opt || {}),\n scrollType: normalizedScrollType as ScrollParam['scrollType'],\n };\n }\n }\n\n const detailedLocateParam = buildDetailedLocateParam(\n locatePrompt || '',\n opt,\n );\n\n return this.callActionInActionSpace('Scroll', {\n ...(opt || {}),\n locate: detailedLocateParam,\n });\n }\n\n async aiAct(\n taskPrompt: string,\n opt?: AiActOptions,\n ): Promise<string | undefined> {\n const fileChooserAccept = opt?.fileChooserAccept\n ? this.normalizeFileInput(opt.fileChooserAccept)\n : undefined;\n\n const abortSignal = opt?.abortSignal;\n if (abortSignal?.aborted) {\n throw new Error(\n `aiAct aborted: ${abortSignal.reason || 'signal already aborted'}`,\n );\n }\n\n const runAiAct = async () => {\n this.aiActStartScreenshot = await this.interface.screenshotBase64();\n this.actionScreenshotHistory = [];\n\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const deepThink = opt?.deepThink === 'unset' ? undefined : opt?.deepThink;\n\n const deepLocate = opt?.deepLocate;\n\n const noIndividualLocateModel =\n modelConfigForPlanning.modelName ===\n defaultIntentModelConfig.modelName &&\n modelConfigForPlanning.openaiBaseURL ===\n defaultIntentModelConfig.openaiBaseURL;\n\n const includeBboxInPlanning =\n !deepThink && noIndividualLocateModel && !deepLocate;\n\n debug('setting includeBboxInPlanning to', includeBboxInPlanning, {\n deepThink,\n noIndividualLocateModel,\n deepLocate,\n });\n\n if (deepLocate && includeBboxInPlanning) {\n console.warn(\n 'deepLocate option is ignored when includeBboxInPlanning is true (same model for planning and default intent without deepThink). Locate is already done during planning.',\n );\n }\n\n const cacheable = opt?.cacheable;\n const replanningCycleLimit = this.resolveReplanningCycleLimit(\n modelConfigForPlanning,\n );\n // if vlm-ui-tars or auto-glm, plan cache is not used\n const isVlmUiTars = isUITars(modelConfigForPlanning.modelFamily);\n const isAutoGlm = isAutoGLM(modelConfigForPlanning.modelFamily);\n const matchedCache =\n isVlmUiTars || isAutoGlm || cacheable === false\n ? undefined\n : await this.taskCache?.matchPlanCache(taskPrompt);\n if (\n matchedCache &&\n this.taskCache?.isCacheResultUsed &&\n matchedCache.cacheContent?.yamlWorkflow?.trim()\n ) {\n // log into report file\n await this.taskExecutor.loadYamlFlowAsPlanning(\n taskPrompt,\n matchedCache.cacheContent.yamlWorkflow,\n );\n\n debug('matched cache, will call .runYaml to run the action');\n const yaml = matchedCache.cacheContent.yamlWorkflow;\n await this.runYaml(yaml);\n return;\n }\n\n // If cache matched but yamlWorkflow is empty, fall through to normal execution\n const imagesIncludeCount: number = deepThink ? 2 : 1;\n const { output: actionOutput } = await this.taskExecutor.action(\n taskPrompt,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n includeBboxInPlanning,\n this.aiActContext,\n cacheable,\n replanningCycleLimit,\n imagesIncludeCount,\n deepThink,\n fileChooserAccept,\n includeBboxInPlanning ? undefined : deepLocate,\n abortSignal,\n );\n\n // update cache\n if (this.taskCache && actionOutput?.yamlFlow && cacheable !== false) {\n const yamlContent: MidsceneYamlScript = {\n tasks: [\n {\n name: taskPrompt,\n flow: actionOutput.yamlFlow,\n },\n ],\n };\n const yamlFlowStr = yaml.dump(yamlContent);\n this.taskCache.updateOrAppendCacheRecord(\n {\n type: 'plan',\n prompt: taskPrompt,\n yamlWorkflow: yamlFlowStr,\n },\n matchedCache,\n );\n }\n\n return actionOutput?.output;\n };\n\n return await runAiAct();\n }\n\n /**\n * @deprecated Use {@link Agent.aiAct} instead.\n */\n async aiAction(taskPrompt: string, opt?: AiActOptions) {\n return this.aiAct(taskPrompt, opt);\n }\n\n async aiQuery<ReturnType = any>(\n demand: ServiceExtractParam,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<ReturnType> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Query',\n demand,\n modelConfig,\n opt,\n );\n return output as ReturnType;\n }\n\n async aiBoolean(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<boolean> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Boolean',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as boolean;\n }\n\n async aiNumber(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<number> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'Number',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as number;\n }\n\n async aiString(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<string> {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const { textPrompt, multimodalPrompt } = parsePrompt(prompt);\n const { output } = await this.taskExecutor.createTypeQueryExecution(\n 'String',\n textPrompt,\n modelConfig,\n opt,\n multimodalPrompt,\n );\n return output as string;\n }\n\n async aiAsk(\n prompt: TUserPrompt,\n opt: ServiceExtractOption = defaultServiceExtractOption,\n ): Promise<string> {\n return this.aiString(prompt, opt);\n }\n\n async describeElementAtPoint(\n center: [number, number],\n opt?: {\n verifyPrompt?: boolean;\n retryLimit?: number;\n deepLocate?: boolean;\n } & LocatorValidatorOption,\n ): Promise<AgentDescribeElementAtPointResult> {\n const { verifyPrompt = true, retryLimit = 3 } = opt || {};\n\n let success = false;\n let retryCount = 0;\n let resultPrompt = '';\n let deepLocate = opt?.deepLocate || false;\n let verifyResult: LocateValidatorResult | undefined;\n\n while (!success && retryCount < retryLimit) {\n if (retryCount >= 2) {\n deepLocate = true;\n }\n debug(\n 'aiDescribe',\n center,\n 'verifyPrompt',\n verifyPrompt,\n 'retryCount',\n retryCount,\n 'deepLocate',\n deepLocate,\n );\n // use same intent as aiLocate\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n\n const text = await this.service.describe(center, modelConfig, {\n deepLocate,\n });\n debug('aiDescribe text', text);\n assert(text.description, `failed to describe element at [${center}]`);\n resultPrompt = text.description;\n\n // Don't pass deepLocate to verification locate — the description was generated\n // from a cropped view (deepLocate describe), but verification should use regular\n // locate on the full screenshot to confirm the description works universally.\n // Passing deepLocate here would trigger AiLocateSection with an element-level\n // description as a section prompt, which is semantically incorrect.\n verifyResult = await this.verifyLocator(\n resultPrompt,\n undefined,\n center,\n opt,\n );\n if (verifyResult.pass) {\n success = true;\n } else {\n retryCount++;\n }\n }\n\n return {\n prompt: resultPrompt,\n deepLocate,\n verifyResult,\n };\n }\n\n async verifyLocator(\n prompt: string,\n locateOpt: LocateOption | undefined,\n expectCenter: [number, number],\n verifyLocateOption?: LocatorValidatorOption,\n ): Promise<LocateValidatorResult> {\n debug('verifyLocator', prompt, locateOpt, expectCenter, verifyLocateOption);\n\n const { center: verifyCenter, rect: verifyRect } = await this.aiLocate(\n prompt,\n locateOpt,\n );\n const distance = distanceOfTwoPoints(expectCenter, verifyCenter);\n const included = includedInRect(expectCenter, verifyRect);\n const pass =\n distance <= (verifyLocateOption?.centerDistanceThreshold || 20) ||\n included;\n const verifyResult = {\n pass,\n rect: verifyRect,\n center: verifyCenter,\n centerDistance: distance,\n };\n debug('aiDescribe verifyResult', verifyResult);\n return verifyResult;\n }\n\n async aiLocate(prompt: TUserPrompt, opt?: LocateOption) {\n const locateParam = buildDetailedLocateParam(prompt, opt);\n assert(locateParam, 'cannot get locate param for aiLocate');\n const locatePlan = locatePlanForLocate(locateParam);\n const plans = [locatePlan];\n const defaultIntentModelConfig =\n this.modelConfigManager.getModelConfig('default');\n const modelConfigForPlanning =\n this.modelConfigManager.getModelConfig('planning');\n\n const { output } = await this.taskExecutor.runPlans(\n taskTitleStr('Locate', locateParamStr(locateParam)),\n plans,\n modelConfigForPlanning,\n defaultIntentModelConfig,\n );\n\n const { element } = output;\n\n return {\n rect: element?.rect,\n center: element?.center,\n dpr: element?.dpr,\n } as Pick<LocateResultElement, 'rect' | 'center'>;\n }\n\n async aiAssert(\n assertion: TUserPrompt,\n msg?: string,\n opt?: AgentAssertOpt & ServiceExtractOption,\n ) {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n const { textPrompt } = parsePrompt(assertion);\n const assertionText =\n typeof assertion === 'string' ? assertion : assertion.prompt;\n\n let beforeScreenshot: string | undefined;\n let afterScreenshot: string;\n\n const mode = opt?.screenshotMode || 'auto';\n\n switch (mode) {\n case 'manual':\n beforeScreenshot = opt?.beforeScreenshot;\n afterScreenshot = opt?.afterScreenshot!;\n break;\n\n case 'flow':\n beforeScreenshot =\n this.aiActStartScreenshot ||\n this.actionScreenshotHistory[0]?.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'currentOnly':\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'lastAction':\n if (this.actionScreenshotHistory.length > 0) {\n const lastContext =\n this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n beforeScreenshot = lastContext.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n } else {\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n }\n break;\n\n case 'diff':\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n case 'video':\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n break;\n\n default:\n if (opt?.beforeScreenshot && opt?.afterScreenshot) {\n beforeScreenshot = opt.beforeScreenshot;\n afterScreenshot = opt.afterScreenshot;\n } else if (this.actionScreenshotHistory.length > 0) {\n const lastContext =\n this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n beforeScreenshot = lastContext.beforeScreenshot;\n afterScreenshot = await this.interface.screenshotBase64();\n } else {\n beforeScreenshot = undefined;\n afterScreenshot = await this.interface.screenshotBase64();\n }\n break;\n }\n\n const executeAssertion = async () => {\n if (\n mode === 'diff' &&\n opt?.referenceImages &&\n opt.referenceImages.length > 0\n ) {\n const { AiAssertDiff } = await import('../ai-model/assert.js');\n const diffResult = await AiAssertDiff({\n currentScreenshot: afterScreenshot,\n referenceImages: opt.referenceImages,\n assertion: textPrompt,\n businessContext: opt.businessContext,\n diffThreshold: opt.diffThreshold,\n ignoreRegions: opt.ignoreRegions,\n ignoreDynamicContent: opt.ignoreDynamicContent,\n strictMode: opt.strictMode,\n modelConfig,\n });\n return {\n pass: diffResult.pass,\n thought: diffResult.thought,\n reason: diffResult.reason,\n systemCheckResults: undefined,\n diffDetails: diffResult.diffDetails,\n videoDetails: undefined,\n };\n }\n\n if (mode === 'video' && opt?.currentVideo) {\n const { AiAssertVideo } = await import('../ai-model/assert.js');\n const MAX_DURATION = 5;\n const DEFAULT_FPS = 30;\n const MAX_FRAMES = MAX_DURATION * DEFAULT_FPS;\n\n let videoFrames: string[] = [];\n\n if (opt.currentVideo.frames && opt.currentVideo.frames.length > 0) {\n videoFrames = opt.currentVideo.frames.slice(0, MAX_FRAMES);\n } else if (opt.currentVideo.format === 'url' && opt.currentVideo.url) {\n throw new Error(\n 'Video URL format is not yet supported. Please provide video frames directly.',\n );\n } else {\n throw new Error(\n 'currentVideo.frames is required for video assertion mode',\n );\n }\n\n if (videoFrames.length === 0) {\n throw new Error('No video frames provided for video assertion');\n }\n\n const videoResult = await AiAssertVideo({\n currentVideoFrames: videoFrames,\n assertion: textPrompt,\n businessContext: opt.businessContext,\n videoOptions: opt.videoOptions,\n modelConfig,\n });\n return {\n pass: videoResult.pass,\n thought: videoResult.thought,\n reason: videoResult.reason,\n systemCheckResults: undefined,\n diffDetails: undefined,\n videoDetails: videoResult.videoDetails,\n };\n }\n\n const assertResult = await AiAssertElement({\n beforeScreenshot,\n afterScreenshot,\n assertion: textPrompt,\n businessContext: opt?.businessContext,\n enableSystemCheck: opt?.enableSystemCheck ?? true,\n customSystemCheckRules: opt?.customSystemCheckRules,\n modelConfig,\n });\n return {\n pass: assertResult.pass,\n thought: assertResult.thought,\n reason: assertResult.reason,\n systemCheckResults: assertResult.systemCheckResults,\n diffDetails: undefined,\n };\n };\n\n const runWithRetry = async (): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n systemCheckResults?: any;\n diffDetails?: any;\n }> => {\n const retryOptions = opt?.retryOptions;\n let lastError: Error | undefined;\n let attempts = 0;\n const maxAttempts = retryOptions?.maxRetries\n ? retryOptions.maxRetries + 1\n : 1;\n\n while (attempts < maxAttempts) {\n try {\n attempts++;\n const result = await executeAssertion();\n return {\n pass: result.pass,\n thought: result.thought,\n reason: result.reason,\n systemCheckResults: result.systemCheckResults,\n diffDetails: result.diffDetails,\n };\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempts < maxAttempts && retryOptions?.retryInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, retryOptions.retryInterval),\n );\n }\n }\n }\n\n throw lastError || new Error('Assertion failed after retries');\n };\n\n const startTime = Date.now();\n try {\n const result = await runWithRetry();\n\n const { pass, thought, reason, systemCheckResults, diffDetails } = result;\n\n if (opt?.saveSnapshot) {\n const snapshot = {\n beforeScreenshot,\n afterScreenshot,\n timestamp: Date.now(),\n assertion: assertionText,\n };\n if (opt?.snapshotPath) {\n const fs = await import('node:fs');\n const path = await import('node:path');\n const snapshotDir = path.dirname(opt.snapshotPath);\n if (!fs.existsSync(snapshotDir)) {\n fs.mkdirSync(snapshotDir, { recursive: true });\n }\n fs.writeFileSync(opt.snapshotPath, JSON.stringify(snapshot, null, 2));\n }\n }\n\n const endTime = Date.now();\n const now = Date.now();\n\n const beforeScreenshotItem = beforeScreenshot\n ? ScreenshotItem.create(beforeScreenshot, now)\n : undefined;\n const afterScreenshotItem = ScreenshotItem.create(afterScreenshot, now);\n\n const serviceDump: ServiceDump = {\n type: 'assert',\n logTime: now,\n logId: `assert-${now}`,\n userQuery: {\n assertion: assertion,\n },\n matchedElement: [],\n data: null,\n assertionPass: pass,\n assertionThought: thought,\n assertionReason: reason,\n beforeScreenshot: beforeScreenshot,\n systemCheckResults: systemCheckResults,\n taskInfo: {\n durationMs: endTime - startTime,\n },\n };\n\n const recorder: ExecutionRecorderItem[] = [];\n if (beforeScreenshotItem) {\n recorder.push({\n type: 'screenshot',\n ts: now,\n screenshot: beforeScreenshotItem,\n timing: 'before',\n });\n }\n recorder.push({\n type: 'screenshot',\n ts: now,\n screenshot: afterScreenshotItem,\n timing: 'after',\n });\n\n const assertionTask: ExecutionTask = {\n taskId: uuid(),\n type: 'Insight',\n subType: 'Assertion',\n status: 'finished',\n recorder,\n timing: {\n start: startTime,\n end: endTime,\n cost: endTime - startTime,\n },\n param: {\n assertion: assertionText,\n },\n output: {\n pass,\n thought,\n },\n log: serviceDump,\n executor: async () => {},\n };\n\n const executionDump = new ExecutionDump({\n logTime: now,\n name: taskTitleStr('Assert', assertionText),\n tasks: [assertionTask],\n });\n\n this.appendExecutionDump(executionDump);\n\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n this.writeOutActionDumps();\n\n const message = pass\n ? undefined\n : `Assertion failed: ${msg || assertionText}\\nReason: ${reason || thought || '(no_reason)'}`;\n\n if (opt?.keepRawResponse) {\n return {\n pass,\n thought,\n reason,\n systemCheckResults,\n diffDetails,\n message,\n };\n }\n\n if (!pass) {\n throw new Error(message);\n }\n } catch (error) {\n if (error instanceof TaskExecutionError) {\n const errorTask = error.errorTask;\n const thought = errorTask?.thought;\n const rawError = errorTask?.error;\n const rawMessage =\n errorTask?.errorMessage ||\n (rawError instanceof Error\n ? rawError.message\n : rawError\n ? String(rawError)\n : undefined);\n const reason = thought || rawMessage || '(no_reason)';\n const message = `Assertion failed: ${msg || assertionText}\\nReason: ${reason}`;\n\n if (opt?.keepRawResponse) {\n return {\n pass: false,\n thought,\n reason,\n message,\n };\n }\n\n throw new Error(message, {\n cause: rawError ?? error,\n });\n }\n\n throw error;\n }\n }\n\n /**\n * Clear the action screenshot history\n * Useful when starting a new test scenario\n */\n clearActionScreenshotHistory() {\n this.actionScreenshotHistory = [];\n this.aiActStartScreenshot = null;\n }\n\n /**\n * Get the last action screenshot context\n */\n getLastActionScreenshotContext(): ActionScreenshotContext | undefined {\n return this.actionScreenshotHistory[\n this.actionScreenshotHistory.length - 1\n ];\n }\n\n /**\n * Get the flow start screenshot (from aiAct)\n */\n getFlowStartScreenshot(): string | null {\n return this.aiActStartScreenshot;\n }\n\n async aiWaitFor(assertion: TUserPrompt, opt?: AgentWaitForOpt) {\n const modelConfig = this.modelConfigManager.getModelConfig('insight');\n await this.taskExecutor.waitFor(\n assertion,\n {\n ...opt,\n timeoutMs: opt?.timeoutMs || 15 * 1000,\n checkIntervalMs: opt?.checkIntervalMs || 3 * 1000,\n },\n modelConfig,\n );\n }\n\n async ai(...args: Parameters<typeof this.aiAct>) {\n return this.aiAct(...args);\n }\n\n async runYaml(yamlScriptContent: string): Promise<{\n result: Record<string, any>;\n }> {\n const script = parseYamlScript(yamlScriptContent, 'yaml');\n const player = new ScriptPlayer(script, async () => {\n return { agent: this, freeFn: [] };\n });\n await player.run();\n\n if (player.status === 'error') {\n const errors = player.taskStatusList\n .filter((task) => task.status === 'error')\n .map((task) => {\n return `task - ${task.name}: ${task.error?.message}`;\n })\n .join('\\n');\n throw new Error(`Error(s) occurred in running yaml script:\\n${errors}`);\n }\n\n return {\n result: player.result,\n };\n }\n\n async evaluateJavaScript(script: string) {\n assert(\n this.interface.evaluateJavaScript,\n 'evaluateJavaScript is not supported in current agent',\n );\n return this.interface.evaluateJavaScript(script);\n }\n\n /**\n * Add a dump update listener\n * @param listener Listener function\n * @returns A remove function that can be called to remove this listener\n */\n addDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): () => void {\n this.dumpUpdateListeners.push(listener);\n\n // Return remove function\n return () => {\n this.removeDumpUpdateListener(listener);\n };\n }\n\n /**\n * Remove a dump update listener\n * @param listener The listener function to remove\n */\n removeDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n const index = this.dumpUpdateListeners.indexOf(listener);\n if (index > -1) {\n this.dumpUpdateListeners.splice(index, 1);\n }\n }\n\n /**\n * Clear all dump update listeners\n */\n clearDumpUpdateListeners(): void {\n this.dumpUpdateListeners = [];\n }\n\n async destroy() {\n // Early return if already destroyed\n if (this.destroyed) {\n return;\n }\n\n // Wait for all queued write operations to complete\n await this.reportGenerator.flush();\n\n await this.reportGenerator.finalize(this.dump);\n this.reportFile = this.reportGenerator.getReportPath();\n\n await this.interface.destroy?.();\n this.resetDump(); // reset dump to release memory\n this.destroyed = true;\n }\n\n async recordToReport(\n title?: string,\n opt?: {\n content: string;\n },\n ) {\n // 1. screenshot\n const base64 = await this.interface.screenshotBase64();\n const now = Date.now();\n const screenshot = ScreenshotItem.create(base64, now);\n // 2. build recorder\n const recorder: ExecutionRecorderItem[] = [\n {\n type: 'screenshot',\n ts: now,\n screenshot,\n },\n ];\n // 3. build ExecutionTaskLog\n const task: ExecutionTaskLog = {\n taskId: uuid(),\n type: 'Log',\n subType: 'Screenshot',\n status: 'finished',\n recorder,\n timing: {\n start: now,\n end: now,\n cost: 0,\n },\n param: {\n content: opt?.content || '',\n },\n executor: async () => {},\n };\n // 4. build ExecutionDump\n const executionDump = new ExecutionDump({\n logTime: now,\n name: `Log - ${title || 'untitled'}`,\n description: opt?.content || '',\n tasks: [task],\n });\n // 5. append to execution dump\n this.appendExecutionDump(executionDump);\n\n // Call all registered dump update listeners\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n\n this.writeOutActionDumps();\n await this.reportGenerator.flush();\n }\n\n /**\n * @deprecated Use {@link Agent.recordToReport} instead.\n */\n async logScreenshot(\n title?: string,\n opt?: {\n content: string;\n },\n ) {\n await this.recordToReport(title, opt);\n }\n\n _unstableLogContent() {\n const { groupName, groupDescription, executions } = this.dump;\n return {\n groupName,\n groupDescription,\n executions: executions || [],\n };\n }\n\n /**\n * Freezes the current page context to be reused in subsequent AI operations\n * This avoids recalculating page context for each operation\n */\n async freezePageContext(): Promise<void> {\n debug('Freezing page context');\n const context = await this._snapshotContext();\n // Mark the context as frozen\n context._isFrozen = true;\n this.frozenUIContext = context;\n debug('Page context frozen successfully');\n }\n\n /**\n * Unfreezes the page context, allowing AI operations to calculate context dynamically\n */\n async unfreezePageContext(): Promise<void> {\n debug('Unfreezing page context');\n this.frozenUIContext = undefined;\n debug('Page context unfrozen successfully');\n }\n\n /**\n * Process cache configuration and return normalized cache settings\n */\n private processCacheConfig(opts: AgentOpt): {\n id: string;\n enabled: boolean;\n readOnly: boolean;\n writeOnly: boolean;\n cacheAdapter?: import('./cache-adapter').CacheAdapter;\n } | null {\n // Validate original cache config before processing\n // Agent requires explicit IDs - don't allow auto-generation\n if (opts.cache === true) {\n throw new Error(\n 'cache: true requires an explicit cache ID. Please provide:\\n' +\n 'Example: cache: { id: \"my-cache-id\" }',\n );\n }\n\n // Check if cache config object is missing ID\n if (\n opts.cache &&\n typeof opts.cache === 'object' &&\n opts.cache !== null &&\n !opts.cache.id\n ) {\n throw new Error(\n 'cache configuration requires an explicit id.\\n' +\n 'Example: cache: { id: \"my-cache-id\" }',\n );\n }\n\n // Use the unified utils function to process cache configuration\n const cacheConfig = processCacheConfig(\n opts.cache,\n opts.cacheId || opts.testId || 'default',\n );\n\n if (!cacheConfig) {\n return null;\n }\n\n // Handle cache configuration object\n if (typeof cacheConfig === 'object' && cacheConfig !== null) {\n const id = cacheConfig.id;\n const rawStrategy = cacheConfig.strategy as unknown;\n let strategyValue: string;\n\n if (rawStrategy === undefined) {\n strategyValue = 'read-write';\n } else if (typeof rawStrategy === 'string') {\n strategyValue = rawStrategy;\n } else {\n throw new Error(\n `cache.strategy must be a string when provided, but received type ${typeof rawStrategy}`,\n );\n }\n\n if (!isValidCacheStrategy(strategyValue)) {\n throw new Error(\n `cache.strategy must be one of ${CACHE_STRATEGY_VALUES}, but received \"${strategyValue}\"`,\n );\n }\n\n const isReadOnly = strategyValue === 'read-only';\n const isWriteOnly = strategyValue === 'write-only';\n\n return {\n id,\n enabled: !isWriteOnly,\n readOnly: isReadOnly,\n writeOnly: isWriteOnly,\n cacheAdapter: cacheConfig.cacheAdapter,\n };\n }\n\n return null;\n }\n\n private normalizeFilePaths(files: string[]): string[] {\n if (ifInBrowser) {\n throw new Error('File chooser is not supported in browser environment');\n }\n\n return files.map((file) => {\n const absolutePath = resolve(file);\n if (!existsSync(absolutePath)) {\n throw new Error(`File not found: ${file}`);\n }\n return absolutePath;\n });\n }\n\n private normalizeFileInput(files: string | string[]): string[] {\n const filesArray = Array.isArray(files) ? files : [files];\n return this.normalizeFilePaths(filesArray);\n }\n\n /**\n * Manually flush cache to file\n * @param options - Optional configuration\n * @param options.cleanUnused - If true, removes unused cache records before flushing\n */\n async flushCache(options?: { cleanUnused?: boolean }): Promise<void> {\n if (!this.taskCache) {\n throw new Error('Cache is not configured');\n }\n\n this.taskCache.flushCacheToFile(options);\n }\n}\n\nexport const createAgent = (\n interfaceInstance: AbstractInterface,\n opts?: AgentOpt,\n) => {\n return new Agent(interfaceInstance, opts);\n};\n"],"names":["debug","getDebug","distanceOfTwoPoints","p1","p2","x1","y1","x2","y2","Math","includedInRect","point","rect","x","y","left","top","width","height","defaultServiceExtractOption","CACHE_STRATEGIES","isValidCacheStrategy","strategy","value","CACHE_STRATEGY_VALUES","legacyScrollTypeMap","normalizeScrollType","scrollType","defaultReplanningCycleLimit","defaultVlmUiTarsReplanningCycleLimit","defaultAutoGlmReplanningCycleLimit","Agent","callback","modelConfigForPlanning","undefined","isUITars","isAutoGLM","action","context","commonContextParser","prompt","console","GroupedActionDump","getVersion","WeakMap","execution","runner","currentDump","existingIndex","opt","ifInBrowser","reportHTMLContent","task","param","paramStr","tip","typeStr","name","type","beforeScreenshot","actionPlan","plans","Boolean","title","taskTitleStr","locateParamStr","defaultIntentModelConfig","output","afterScreenshot","Date","locatePrompt","assert","detailedLocateParam","buildDetailedLocateParam","fileChooserAccept","withFileChooser","locatePromptOrValue","locatePromptOrOpt","optOrUndefined","optWithValue","stringValue","String","mode","locatePromptOrKeyName","keyName","locatePromptOrScrollParam","scrollParam","normalizedScrollType","taskPrompt","abortSignal","Error","runAiAct","deepThink","deepLocate","noIndividualLocateModel","includeBboxInPlanning","cacheable","replanningCycleLimit","isVlmUiTars","isAutoGlm","matchedCache","yaml","imagesIncludeCount","actionOutput","yamlContent","yamlFlowStr","demand","modelConfig","textPrompt","multimodalPrompt","parsePrompt","center","verifyPrompt","retryLimit","success","retryCount","resultPrompt","verifyResult","text","locateOpt","expectCenter","verifyLocateOption","verifyCenter","verifyRect","distance","included","pass","locateParam","locatePlan","locatePlanForLocate","element","assertion","msg","assertionText","lastContext","executeAssertion","AiAssertDiff","diffResult","AiAssertVideo","MAX_DURATION","DEFAULT_FPS","MAX_FRAMES","videoFrames","videoResult","assertResult","AiAssertElement","runWithRetry","retryOptions","lastError","attempts","maxAttempts","result","error","Promise","resolve","setTimeout","startTime","thought","reason","systemCheckResults","diffDetails","snapshot","fs","path","snapshotDir","JSON","endTime","now","beforeScreenshotItem","ScreenshotItem","afterScreenshotItem","serviceDump","recorder","assertionTask","uuid","executionDump","ExecutionDump","dumpString","listener","message","TaskExecutionError","errorTask","rawError","rawMessage","args","yamlScriptContent","script","parseYamlScript","player","ScriptPlayer","errors","index","base64","screenshot","groupName","groupDescription","executions","opts","cacheConfig","processCacheConfig","id","rawStrategy","strategyValue","isReadOnly","isWriteOnly","files","file","absolutePath","existsSync","filesArray","Array","options","interfaceInstance","envReplanningCycleLimit","globalConfigManager","MIDSCENE_REPLANNING_CYCLE_LIMIT","Object","Number","resolvedAiActContext","hasCustomConfig","ModelConfigManager","globalModelConfigManager","Service","cacheConfigObj","TaskCache","baseActionSpace","defineActionSleep","TaskExecutor","getReportFileName","ReportGenerator","createAgent"],"mappings":"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"}
@@ -152,7 +152,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
152
152
  return;
153
153
  }
154
154
  }
155
- const getMidsceneVersion = ()=>"1.6.9";
155
+ const getMidsceneVersion = ()=>"1.6.10";
156
156
  const parsePrompt = (prompt)=>{
157
157
  if ('string' == typeof prompt) return {
158
158
  textPrompt: prompt,
@@ -23,7 +23,7 @@ ${systemCheckPrompt}`;
23
23
  prompt += `
24
24
 
25
25
  ## 输出要求
26
- 请以 JSON 格式输出断言结果:
26
+ 请严格以 标准JSON 格式输出断言结果,不要携带md的标签:
27
27
  {
28
28
  "pass": boolean, // 断言是否通过
29
29
  "thought": string, // 思考过程
@@ -46,7 +46,7 @@ async function AiAssertElement(options) {
46
46
  1. 分析操作前后的截图变化(如果提供了操作前截图)
47
47
  2. 验证用户的断言描述是否成立
48
48
  3. 检查是否存在系统级问题(如果启用)
49
- 4. 给出详细的思考过程和判断结果`;
49
+ 4. 给出详细的思考过程和判断结果(思考不要太久)`;
50
50
  const systemCheckPrompt = enableSystemCheck ? customSystemCheckRules || DEFAULT_SYSTEM_CHECK_PROMPT : '';
51
51
  const userContent = [];
52
52
  if (beforeScreenshot) {
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/assert.mjs","sources":["../../../src/ai-model/assert.ts"],"sourcesContent":["import type {\n AIUsageInfo,\n DiffDetails,\n IgnoreRegion,\n ReferenceImage,\n SystemCheckResults,\n VideoAssertionOptions,\n VideoDetails,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { ChatCompletionUserMessageParam } from 'openai/resources/index';\nimport { callAIWithObjectResponse } from './service-caller/index';\n\nconst debug = getDebug('ai:assert');\n\nexport interface AiAssertOptions {\n beforeScreenshot?: string;\n afterScreenshot: string;\n assertion: string;\n businessContext?: string;\n enableSystemCheck?: boolean;\n customSystemCheckRules?: string;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n referenceImages?: ReferenceImage[];\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n reason?: string;\n systemCheckResults?: SystemCheckResults;\n diffDetails?: DiffDetails;\n}\n\nconst DEFAULT_SYSTEM_CHECK_PROMPT = `请检查以下系统级问题:\n\n1. **白屏检测**:页面是否完全白屏或大面积空白\n4. **错误提示**:页面是否显示错误信息、异常提示\n5. **后端错误**:是否有后端请求失败的提示,如\"活动太火爆了\"、报错 Toast、错误码 等\n\n如果发现以上问题,请在 systemCheckResults 中标注对应字段为 true。`;\n\nfunction buildAssertionPrompt(options: {\n assertion: string;\n businessContext?: string;\n systemCheckPrompt?: string;\n}): string {\n const { assertion, businessContext, systemCheckPrompt } = options;\n\n let prompt = `## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n if (systemCheckPrompt) {\n prompt += `\n\n## 系统校验规则\n${systemCheckPrompt}`;\n }\n\n prompt += `\n\n## 输出要求\n请以 JSON 格式输出断言结果:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 思考过程\n \"reason\": string, // 失败原因(如果失败)\n \"systemCheckResults\": { // 系统校验结果(可选)\n \"whiteScreen\": boolean,\n \"layoutBlocked\": boolean,\n \"loadingContent\": boolean,\n \"errorPrompt\": boolean,\n \"backendError\": boolean\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertElement(options: AiAssertOptions): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n usage?: AIUsageInfo;\n systemCheckResults?: SystemCheckResults;\n rawResponse?: string;\n}> {\n const {\n beforeScreenshot,\n afterScreenshot,\n assertion,\n businessContext,\n enableSystemCheck = false,\n customSystemCheckRules,\n modelConfig,\n abortSignal,\n } = options;\n\n const systemPrompt = `你是一个自动化测试断言专家。请根据以下信息判断断言是否通过。\n\n你需要:\n1. 分析操作前后的截图变化(如果提供了操作前截图)\n2. 验证用户的断言描述是否成立\n3. 检查是否存在系统级问题(如果启用)\n4. 给出详细的思考过程和判断结果`;\n\n const systemCheckPrompt = enableSystemCheck\n ? customSystemCheckRules || DEFAULT_SYSTEM_CHECK_PROMPT\n : '';\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n if (beforeScreenshot) {\n userContent.push({\n type: 'text',\n text: '## 操作前截图(执行操作前的页面状态)',\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: beforeScreenshot, detail: 'high' },\n });\n userContent.push({\n type: 'text',\n text: '## 操作后截图(执行操作后的页面状态)',\n });\n } else {\n userContent.push({\n type: 'text',\n text: '## 当前页面截图',\n });\n }\n\n userContent.push({\n type: 'image_url',\n image_url: { url: afterScreenshot, detail: 'high' },\n });\n\n userContent.push({\n type: 'text',\n text: buildAssertionPrompt({\n assertion,\n businessContext,\n systemCheckPrompt,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: systemPrompt },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<AIAssertionResponse>(\n msgs,\n modelConfig,\n { abortSignal },\n );\n\n debug('assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n usage: result.usage,\n systemCheckResults: result.content.systemCheckResults,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('assertion error:', error);\n throw error;\n }\n}\n\nconst DIFF_SYSTEM_PROMPT = `你是一个自动化测试的图像对比专家。你的任务是对比当前页面截图与基准图片,判断页面样式是否符合预期。\n\n你需要:\n1. 仔细对比两张图片的布局、颜色、元素位置\n2. 分析差异是否在可接受范围内\n3. 识别动态内容(如时间、日期)导致的差异\n4. 给出详细的对比分析和判断结果`;\n\nfunction buildDiffPrompt(options: {\n assertion: string;\n businessContext?: string;\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n}): string {\n const {\n assertion,\n businessContext,\n diffThreshold = 0.1,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n } = options;\n\n let prompt = `## 图像对比任务\n\n请对比基准图片(预期样式)和当前截图(实际样式),判断页面是否符合预期。\n\n## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n prompt += `\n\n## 对比要求\n\n请从以下维度进行对比分析:\n\n1. **布局一致性**:页面布局是否与基准图片一致\n2. **颜色一致性**:主要颜色是否与基准图片一致\n3. **元素位置**:关键元素的位置是否与基准图片一致\n4. **文字内容**:文字内容是否与基准图片一致\n5. **图片资源**:图片、图标等资源是否正确加载\n\n## 差异阈值\n允许的差异阈值:${diffThreshold * 100}%`;\n\n if (ignoreRegions && ignoreRegions.length > 0) {\n prompt += `\n\n## 忽略对比的区域\n以下区域不参与对比:`;\n ignoreRegions.forEach((region, index) => {\n prompt += `\n${index + 1}. 位置 (${region.x}, ${region.y}),尺寸 ${region.width}x${region.height}`;\n });\n }\n\n if (ignoreDynamicContent) {\n prompt += `\n\n## 动态内容处理\n请忽略以下动态内容导致的差异:\n- 时间显示\n- 日期显示\n- 随机验证码\n- 动画效果`;\n }\n\n if (strictMode) {\n prompt += `\n\n## 严格模式\n当前为严格模式,任何差异都视为失败。`;\n }\n\n prompt += `\n\n## 输出要求\n请以 JSON 格式输出对比结果:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 对比分析过程\n \"reason\": string, // 失败原因(如果失败)\n \"diffDetails\": { // 差异详情\n \"layoutMatch\": boolean, // 布局是否匹配\n \"colorMatch\": boolean, // 颜色是否匹配\n \"elementPositionMatch\": boolean, // 元素位置是否匹配\n \"textContentMatch\": boolean, // 文字内容是否匹配\n \"resourceMatch\": boolean, // 资源是否匹配\n \"acceptableDifferences\": string[], // 可接受的差异列表\n \"unacceptableDifferences\": string[] // 不可接受的差异列表\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertDiff(options: {\n currentScreenshot: string;\n referenceImages: ReferenceImage[];\n assertion: string;\n businessContext?: string;\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n diffDetails?: DiffDetails;\n usage?: AIUsageInfo;\n rawResponse?: string;\n}> {\n const {\n currentScreenshot,\n referenceImages,\n assertion,\n businessContext,\n diffThreshold,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n modelConfig,\n abortSignal,\n } = options;\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n // 添加基准图片\n for (const refImage of referenceImages) {\n userContent.push({\n type: 'text',\n text: `## 基准图片:${refImage.name}`,\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: refImage.url, detail: 'high' },\n });\n }\n\n // 添加当前截图\n userContent.push({\n type: 'text',\n text: '## 当前截图(实际样式)',\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: currentScreenshot, detail: 'high' },\n });\n\n // 添加对比提示\n userContent.push({\n type: 'text',\n text: buildDiffPrompt({\n assertion,\n businessContext,\n diffThreshold,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: DIFF_SYSTEM_PROMPT },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for diff assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<AIAssertionResponse>(\n msgs,\n modelConfig,\n { abortSignal },\n );\n\n debug('diff assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n diffDetails: result.content.diffDetails,\n usage: result.usage,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('diff assertion error:', error);\n throw error;\n }\n}\n\nconst VIDEO_SYSTEM_PROMPT = `你是一个自动化测试的视频/动画分析专家。你的任务是对比当前录制的视频与基准视频,判断动画效果是否符合预期。\n\n你需要:\n1. 分析视频中的动画流畅度、时长、完整性\n2. 对比基准视频与当前视频的差异\n3. 识别动画类型(过渡动画、加载动画、交互动画等)\n4. 检测动画质量问题(卡顿、跳帧等)\n5. 给出详细的分析过程和判断结果`;\n\nfunction buildVideoPrompt(options: {\n assertion: string;\n businessContext?: string;\n videoOptions?: VideoAssertionOptions;\n}): string {\n const { assertion, businessContext, videoOptions } = options;\n\n let prompt = `## 视频/动画对比任务\n\n请对比基准视频(预期动画效果)和当前录制的视频(实际动画效果),判断动画是否符合预期。\n\n## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n prompt += `\n\n## 分析要求\n\n请从以下维度进行动画分析:\n\n1. **动画流畅度**:动画是否流畅,有无卡顿、跳帧\n2. **动画时长**:动画时长是否在预期范围内\n3. **动画完整性**:动画是否完整执行,有无中断\n4. **动画类型**:识别动画类型(过渡、加载、交互等)\n5. **关键帧匹配**:关键时间点的画面是否符合预期`;\n\n if (videoOptions) {\n if (videoOptions.checkSmoothness) {\n prompt += `\n\n## 流畅度检测\n流畅度阈值:${videoOptions.smoothnessThreshold || 60}/100\n请评估动画流畅度并给出评分。`;\n }\n\n if (videoOptions.checkDuration && videoOptions.expectedDuration) {\n prompt += `\n\n## 时长检测\n预期时长范围:${videoOptions.expectedDuration.min || 0}秒 - ${videoOptions.expectedDuration.max || '无限制'}秒`;\n }\n\n if (\n videoOptions.keyframes &&\n videoOptions.keyframes.timestamps.length > 0\n ) {\n prompt += `\n\n## 关键帧验证\n需要在以下时间点验证画面:`;\n videoOptions.keyframes.timestamps.forEach((ts, index) => {\n const desc = videoOptions.keyframes?.descriptions?.[index] || '未描述';\n prompt += `\n- ${ts}秒:${desc}`;\n });\n }\n\n if (videoOptions.animationType && videoOptions.animationType !== 'auto') {\n prompt += `\n\n## 动画类型\n预期动画类型:${videoOptions.animationType}`;\n }\n }\n\n prompt += `\n\n## 输出要求\n请以 JSON 格式输出对比结果:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 分析过程\n \"reason\": string, // 失败原因(如果失败)\n \"videoDetails\": { // 视频详情\n \"smoothnessScore\": number, // 流畅度评分(0-100)\n \"duration\": number, // 动画时长(秒)\n \"isComplete\": boolean, // 动画是否完整\n \"keyframeMatches\": [ // 关键帧匹配结果\n {\n \"timestamp\": number,\n \"matched\": boolean,\n \"description\": string\n }\n ],\n \"detectedAnimationType\": string, // 检测到的动画类型\n \"qualityAssessment\": { // 质量评估\n \"hasStuttering\": boolean, // 是否有卡顿\n \"hasFrameDropping\": boolean, // 是否有跳帧\n \"averageFrameInterval\": number, // 平均帧间隔(ms)\n \"frameIntervalStdDev\": number // 帧间隔标准差\n },\n \"acceptableDifferences\": string[], // 可接受的差异列表\n \"unacceptableDifferences\": string[] // 不可接受的差异列表\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertVideo(options: {\n currentVideoFrames: string[];\n assertion: string;\n businessContext?: string;\n videoOptions?: VideoAssertionOptions;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n videoDetails?: VideoDetails;\n usage?: AIUsageInfo;\n rawResponse?: string;\n}> {\n const {\n currentVideoFrames,\n assertion,\n businessContext,\n videoOptions,\n modelConfig,\n abortSignal,\n } = options;\n\n const MAX_DURATION = 5;\n const DEFAULT_FPS = 30;\n const MAX_FRAMES = MAX_DURATION * DEFAULT_FPS;\n\n if (currentVideoFrames.length > MAX_FRAMES) {\n throw new Error(\n `Video frames exceed maximum limit. Maximum allowed: ${MAX_FRAMES} frames (${MAX_DURATION}s at ${DEFAULT_FPS}fps), got: ${currentVideoFrames.length} frames`,\n );\n }\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n userContent.push({\n type: 'text',\n text: `## 当前视频(共 ${currentVideoFrames.length} 帧,最大限制 ${MAX_FRAMES} 帧)\n\n以下为当前录制视频的关键帧截图:`,\n });\n\n const frameInterval = Math.max(1, Math.floor(currentVideoFrames.length / 10));\n for (let i = 0; i < currentVideoFrames.length; i += frameInterval) {\n userContent.push({\n type: 'text',\n text: `帧 ${i + 1}/${currentVideoFrames.length}`,\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: currentVideoFrames[i], detail: 'low' },\n });\n }\n\n userContent.push({\n type: 'text',\n text: buildVideoPrompt({\n assertion,\n businessContext,\n videoOptions,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: VIDEO_SYSTEM_PROMPT },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for video assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<{\n pass: boolean;\n thought: string;\n reason?: string;\n videoDetails?: VideoDetails;\n }>(msgs, modelConfig, { abortSignal });\n\n debug('video assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n videoDetails: result.content.videoDetails,\n usage: result.usage,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('video assertion error:', error);\n throw error;\n }\n}\n"],"names":["debug","getDebug","DEFAULT_SYSTEM_CHECK_PROMPT","buildAssertionPrompt","options","assertion","businessContext","systemCheckPrompt","prompt","AiAssertElement","beforeScreenshot","afterScreenshot","enableSystemCheck","customSystemCheckRules","modelConfig","abortSignal","systemPrompt","userContent","msgs","result","callAIWithObjectResponse","JSON","error","DIFF_SYSTEM_PROMPT","buildDiffPrompt","diffThreshold","ignoreRegions","ignoreDynamicContent","strictMode","region","index","AiAssertDiff","currentScreenshot","referenceImages","refImage","VIDEO_SYSTEM_PROMPT","buildVideoPrompt","videoOptions","ts","desc","AiAssertVideo","currentVideoFrames","MAX_DURATION","DEFAULT_FPS","MAX_FRAMES","Error","frameInterval","Math","i"],"mappings":"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"}
1
+ {"version":3,"file":"ai-model/assert.mjs","sources":["../../../src/ai-model/assert.ts"],"sourcesContent":["import type {\n AIUsageInfo,\n DiffDetails,\n IgnoreRegion,\n ReferenceImage,\n SystemCheckResults,\n VideoAssertionOptions,\n VideoDetails,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { ChatCompletionUserMessageParam } from 'openai/resources/index';\nimport { callAIWithObjectResponse } from './service-caller/index';\n\nconst debug = getDebug('ai:assert');\n\nexport interface AiAssertOptions {\n beforeScreenshot?: string;\n afterScreenshot: string;\n assertion: string;\n businessContext?: string;\n enableSystemCheck?: boolean;\n customSystemCheckRules?: string;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n referenceImages?: ReferenceImage[];\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n reason?: string;\n systemCheckResults?: SystemCheckResults;\n diffDetails?: DiffDetails;\n}\n\nconst DEFAULT_SYSTEM_CHECK_PROMPT = `请检查以下系统级问题:\n\n1. **白屏检测**:页面是否完全白屏或大面积空白\n4. **错误提示**:页面是否显示错误信息、异常提示\n5. **后端错误**:是否有后端请求失败的提示,如\"活动太火爆了\"、报错 Toast、错误码 等\n\n如果发现以上问题,请在 systemCheckResults 中标注对应字段为 true。`;\n\nfunction buildAssertionPrompt(options: {\n assertion: string;\n businessContext?: string;\n systemCheckPrompt?: string;\n}): string {\n const { assertion, businessContext, systemCheckPrompt } = options;\n\n let prompt = `## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n if (systemCheckPrompt) {\n prompt += `\n\n## 系统校验规则\n${systemCheckPrompt}`;\n }\n\n prompt += `\n\n## 输出要求\n请严格以 标准JSON 格式输出断言结果,不要携带md的标签:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 思考过程\n \"reason\": string, // 失败原因(如果失败)\n \"systemCheckResults\": { // 系统校验结果(可选)\n \"whiteScreen\": boolean,\n \"layoutBlocked\": boolean,\n \"loadingContent\": boolean,\n \"errorPrompt\": boolean,\n \"backendError\": boolean\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertElement(options: AiAssertOptions): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n usage?: AIUsageInfo;\n systemCheckResults?: SystemCheckResults;\n rawResponse?: string;\n}> {\n const {\n beforeScreenshot,\n afterScreenshot,\n assertion,\n businessContext,\n enableSystemCheck = false,\n customSystemCheckRules,\n modelConfig,\n abortSignal,\n } = options;\n\n const systemPrompt = `你是一个自动化测试断言专家。请根据以下信息判断断言是否通过。\n\n你需要:\n1. 分析操作前后的截图变化(如果提供了操作前截图)\n2. 验证用户的断言描述是否成立\n3. 检查是否存在系统级问题(如果启用)\n4. 给出详细的思考过程和判断结果(思考不要太久)`;\n\n const systemCheckPrompt = enableSystemCheck\n ? customSystemCheckRules || DEFAULT_SYSTEM_CHECK_PROMPT\n : '';\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n if (beforeScreenshot) {\n userContent.push({\n type: 'text',\n text: '## 操作前截图(执行操作前的页面状态)',\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: beforeScreenshot, detail: 'high' },\n });\n userContent.push({\n type: 'text',\n text: '## 操作后截图(执行操作后的页面状态)',\n });\n } else {\n userContent.push({\n type: 'text',\n text: '## 当前页面截图',\n });\n }\n\n userContent.push({\n type: 'image_url',\n image_url: { url: afterScreenshot, detail: 'high' },\n });\n\n userContent.push({\n type: 'text',\n text: buildAssertionPrompt({\n assertion,\n businessContext,\n systemCheckPrompt,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: systemPrompt },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<AIAssertionResponse>(\n msgs,\n modelConfig,\n { abortSignal },\n );\n\n debug('assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n usage: result.usage,\n systemCheckResults: result.content.systemCheckResults,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('assertion error:', error);\n throw error;\n }\n}\n\nconst DIFF_SYSTEM_PROMPT = `你是一个自动化测试的图像对比专家。你的任务是对比当前页面截图与基准图片,判断页面样式是否符合预期。\n\n你需要:\n1. 仔细对比两张图片的布局、颜色、元素位置\n2. 分析差异是否在可接受范围内\n3. 识别动态内容(如时间、日期)导致的差异\n4. 给出详细的对比分析和判断结果`;\n\nfunction buildDiffPrompt(options: {\n assertion: string;\n businessContext?: string;\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n}): string {\n const {\n assertion,\n businessContext,\n diffThreshold = 0.1,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n } = options;\n\n let prompt = `## 图像对比任务\n\n请对比基准图片(预期样式)和当前截图(实际样式),判断页面是否符合预期。\n\n## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n prompt += `\n\n## 对比要求\n\n请从以下维度进行对比分析:\n\n1. **布局一致性**:页面布局是否与基准图片一致\n2. **颜色一致性**:主要颜色是否与基准图片一致\n3. **元素位置**:关键元素的位置是否与基准图片一致\n4. **文字内容**:文字内容是否与基准图片一致\n5. **图片资源**:图片、图标等资源是否正确加载\n\n## 差异阈值\n允许的差异阈值:${diffThreshold * 100}%`;\n\n if (ignoreRegions && ignoreRegions.length > 0) {\n prompt += `\n\n## 忽略对比的区域\n以下区域不参与对比:`;\n ignoreRegions.forEach((region, index) => {\n prompt += `\n${index + 1}. 位置 (${region.x}, ${region.y}),尺寸 ${region.width}x${region.height}`;\n });\n }\n\n if (ignoreDynamicContent) {\n prompt += `\n\n## 动态内容处理\n请忽略以下动态内容导致的差异:\n- 时间显示\n- 日期显示\n- 随机验证码\n- 动画效果`;\n }\n\n if (strictMode) {\n prompt += `\n\n## 严格模式\n当前为严格模式,任何差异都视为失败。`;\n }\n\n prompt += `\n\n## 输出要求\n请以 JSON 格式输出对比结果:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 对比分析过程\n \"reason\": string, // 失败原因(如果失败)\n \"diffDetails\": { // 差异详情\n \"layoutMatch\": boolean, // 布局是否匹配\n \"colorMatch\": boolean, // 颜色是否匹配\n \"elementPositionMatch\": boolean, // 元素位置是否匹配\n \"textContentMatch\": boolean, // 文字内容是否匹配\n \"resourceMatch\": boolean, // 资源是否匹配\n \"acceptableDifferences\": string[], // 可接受的差异列表\n \"unacceptableDifferences\": string[] // 不可接受的差异列表\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertDiff(options: {\n currentScreenshot: string;\n referenceImages: ReferenceImage[];\n assertion: string;\n businessContext?: string;\n diffThreshold?: number;\n ignoreRegions?: IgnoreRegion[];\n ignoreDynamicContent?: boolean;\n strictMode?: boolean;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n diffDetails?: DiffDetails;\n usage?: AIUsageInfo;\n rawResponse?: string;\n}> {\n const {\n currentScreenshot,\n referenceImages,\n assertion,\n businessContext,\n diffThreshold,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n modelConfig,\n abortSignal,\n } = options;\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n // 添加基准图片\n for (const refImage of referenceImages) {\n userContent.push({\n type: 'text',\n text: `## 基准图片:${refImage.name}`,\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: refImage.url, detail: 'high' },\n });\n }\n\n // 添加当前截图\n userContent.push({\n type: 'text',\n text: '## 当前截图(实际样式)',\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: currentScreenshot, detail: 'high' },\n });\n\n // 添加对比提示\n userContent.push({\n type: 'text',\n text: buildDiffPrompt({\n assertion,\n businessContext,\n diffThreshold,\n ignoreRegions,\n ignoreDynamicContent,\n strictMode,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: DIFF_SYSTEM_PROMPT },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for diff assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<AIAssertionResponse>(\n msgs,\n modelConfig,\n { abortSignal },\n );\n\n debug('diff assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n diffDetails: result.content.diffDetails,\n usage: result.usage,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('diff assertion error:', error);\n throw error;\n }\n}\n\nconst VIDEO_SYSTEM_PROMPT = `你是一个自动化测试的视频/动画分析专家。你的任务是对比当前录制的视频与基准视频,判断动画效果是否符合预期。\n\n你需要:\n1. 分析视频中的动画流畅度、时长、完整性\n2. 对比基准视频与当前视频的差异\n3. 识别动画类型(过渡动画、加载动画、交互动画等)\n4. 检测动画质量问题(卡顿、跳帧等)\n5. 给出详细的分析过程和判断结果`;\n\nfunction buildVideoPrompt(options: {\n assertion: string;\n businessContext?: string;\n videoOptions?: VideoAssertionOptions;\n}): string {\n const { assertion, businessContext, videoOptions } = options;\n\n let prompt = `## 视频/动画对比任务\n\n请对比基准视频(预期动画效果)和当前录制的视频(实际动画效果),判断动画是否符合预期。\n\n## 用户断言描述\n${assertion}`;\n\n if (businessContext) {\n prompt += `\n\n## 业务知识上下文\n${businessContext}`;\n }\n\n prompt += `\n\n## 分析要求\n\n请从以下维度进行动画分析:\n\n1. **动画流畅度**:动画是否流畅,有无卡顿、跳帧\n2. **动画时长**:动画时长是否在预期范围内\n3. **动画完整性**:动画是否完整执行,有无中断\n4. **动画类型**:识别动画类型(过渡、加载、交互等)\n5. **关键帧匹配**:关键时间点的画面是否符合预期`;\n\n if (videoOptions) {\n if (videoOptions.checkSmoothness) {\n prompt += `\n\n## 流畅度检测\n流畅度阈值:${videoOptions.smoothnessThreshold || 60}/100\n请评估动画流畅度并给出评分。`;\n }\n\n if (videoOptions.checkDuration && videoOptions.expectedDuration) {\n prompt += `\n\n## 时长检测\n预期时长范围:${videoOptions.expectedDuration.min || 0}秒 - ${videoOptions.expectedDuration.max || '无限制'}秒`;\n }\n\n if (\n videoOptions.keyframes &&\n videoOptions.keyframes.timestamps.length > 0\n ) {\n prompt += `\n\n## 关键帧验证\n需要在以下时间点验证画面:`;\n videoOptions.keyframes.timestamps.forEach((ts, index) => {\n const desc = videoOptions.keyframes?.descriptions?.[index] || '未描述';\n prompt += `\n- ${ts}秒:${desc}`;\n });\n }\n\n if (videoOptions.animationType && videoOptions.animationType !== 'auto') {\n prompt += `\n\n## 动画类型\n预期动画类型:${videoOptions.animationType}`;\n }\n }\n\n prompt += `\n\n## 输出要求\n请以 JSON 格式输出对比结果:\n{\n \"pass\": boolean, // 断言是否通过\n \"thought\": string, // 分析过程\n \"reason\": string, // 失败原因(如果失败)\n \"videoDetails\": { // 视频详情\n \"smoothnessScore\": number, // 流畅度评分(0-100)\n \"duration\": number, // 动画时长(秒)\n \"isComplete\": boolean, // 动画是否完整\n \"keyframeMatches\": [ // 关键帧匹配结果\n {\n \"timestamp\": number,\n \"matched\": boolean,\n \"description\": string\n }\n ],\n \"detectedAnimationType\": string, // 检测到的动画类型\n \"qualityAssessment\": { // 质量评估\n \"hasStuttering\": boolean, // 是否有卡顿\n \"hasFrameDropping\": boolean, // 是否有跳帧\n \"averageFrameInterval\": number, // 平均帧间隔(ms)\n \"frameIntervalStdDev\": number // 帧间隔标准差\n },\n \"acceptableDifferences\": string[], // 可接受的差异列表\n \"unacceptableDifferences\": string[] // 不可接受的差异列表\n }\n}`;\n\n return prompt;\n}\n\nexport async function AiAssertVideo(options: {\n currentVideoFrames: string[];\n assertion: string;\n businessContext?: string;\n videoOptions?: VideoAssertionOptions;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n pass: boolean;\n thought: string;\n reason?: string;\n videoDetails?: VideoDetails;\n usage?: AIUsageInfo;\n rawResponse?: string;\n}> {\n const {\n currentVideoFrames,\n assertion,\n businessContext,\n videoOptions,\n modelConfig,\n abortSignal,\n } = options;\n\n const MAX_DURATION = 5;\n const DEFAULT_FPS = 30;\n const MAX_FRAMES = MAX_DURATION * DEFAULT_FPS;\n\n if (currentVideoFrames.length > MAX_FRAMES) {\n throw new Error(\n `Video frames exceed maximum limit. Maximum allowed: ${MAX_FRAMES} frames (${MAX_DURATION}s at ${DEFAULT_FPS}fps), got: ${currentVideoFrames.length} frames`,\n );\n }\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n userContent.push({\n type: 'text',\n text: `## 当前视频(共 ${currentVideoFrames.length} 帧,最大限制 ${MAX_FRAMES} 帧)\n\n以下为当前录制视频的关键帧截图:`,\n });\n\n const frameInterval = Math.max(1, Math.floor(currentVideoFrames.length / 10));\n for (let i = 0; i < currentVideoFrames.length; i += frameInterval) {\n userContent.push({\n type: 'text',\n text: `帧 ${i + 1}/${currentVideoFrames.length}`,\n });\n userContent.push({\n type: 'image_url',\n image_url: { url: currentVideoFrames[i], detail: 'low' },\n });\n }\n\n userContent.push({\n type: 'text',\n text: buildVideoPrompt({\n assertion,\n businessContext,\n videoOptions,\n }),\n });\n\n const msgs = [\n { role: 'system' as const, content: VIDEO_SYSTEM_PROMPT },\n { role: 'user' as const, content: userContent },\n ];\n\n debug('calling AI for video assertion:', assertion);\n\n try {\n const result = await callAIWithObjectResponse<{\n pass: boolean;\n thought: string;\n reason?: string;\n videoDetails?: VideoDetails;\n }>(msgs, modelConfig, { abortSignal });\n\n debug('video assertion result:', result.content);\n\n return {\n pass: result.content.pass,\n thought: result.content.thought || '',\n reason: result.content.reason,\n videoDetails: result.content.videoDetails,\n usage: result.usage,\n rawResponse: JSON.stringify(result.content),\n };\n } catch (error) {\n debug('video assertion error:', error);\n throw error;\n }\n}\n"],"names":["debug","getDebug","DEFAULT_SYSTEM_CHECK_PROMPT","buildAssertionPrompt","options","assertion","businessContext","systemCheckPrompt","prompt","AiAssertElement","beforeScreenshot","afterScreenshot","enableSystemCheck","customSystemCheckRules","modelConfig","abortSignal","systemPrompt","userContent","msgs","result","callAIWithObjectResponse","JSON","error","DIFF_SYSTEM_PROMPT","buildDiffPrompt","diffThreshold","ignoreRegions","ignoreDynamicContent","strictMode","region","index","AiAssertDiff","currentScreenshot","referenceImages","refImage","VIDEO_SYSTEM_PROMPT","buildVideoPrompt","videoOptions","ts","desc","AiAssertVideo","currentVideoFrames","MAX_DURATION","DEFAULT_FPS","MAX_FRAMES","Error","frameInterval","Math","i"],"mappings":"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"}