@midscene/core 1.17.6 → 1.17.8-beta-20261106101231.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent/agent-report-manager.mjs +1 -1
- package/dist/es/agent/agent-report-manager.mjs.map +1 -1
- package/dist/es/agent/test-runner-nodes.mjs +1 -1
- package/dist/es/agent/test-runner-nodes.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs +80 -0
- package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs.map +1 -0
- package/dist/es/ai-model/model-adapter/locate.mjs +2 -1
- package/dist/es/ai-model/model-adapter/locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/locate.mjs +1 -1
- package/dist/es/ai-model/workflows/grounding/locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs +1 -1
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/utils.mjs +8 -26
- package/dist/es/ai-model/workflows/grounding/utils.mjs.map +1 -1
- package/dist/es/dump/report-image-export.mjs +58 -0
- package/dist/es/dump/report-image-export.mjs.map +1 -0
- package/dist/es/dump/screenshot-store.mjs +5 -0
- package/dist/es/dump/screenshot-store.mjs.map +1 -1
- package/dist/es/report-cli.mjs +13 -29
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-generator.mjs +22 -8
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/report-markdown.mjs +50 -53
- package/dist/es/report-markdown.mjs.map +1 -1
- package/dist/es/report.mjs +8 -24
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/lib/agent/agent-report-manager.js +1 -1
- package/dist/lib/agent/agent-report-manager.js.map +1 -1
- package/dist/lib/agent/test-runner-nodes.js +1 -1
- package/dist/lib/agent/test-runner-nodes.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/model-adapter/locate-prompt-order.js +117 -0
- package/dist/lib/ai-model/model-adapter/locate-prompt-order.js.map +1 -0
- package/dist/lib/ai-model/model-adapter/locate.js +2 -1
- package/dist/lib/ai-model/model-adapter/locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/utils.js +8 -26
- package/dist/lib/ai-model/workflows/grounding/utils.js.map +1 -1
- package/dist/lib/dump/report-image-export.js +92 -0
- package/dist/lib/dump/report-image-export.js.map +1 -0
- package/dist/lib/dump/screenshot-store.js +5 -0
- package/dist/lib/dump/screenshot-store.js.map +1 -1
- package/dist/lib/report-cli.js +12 -28
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-generator.js +22 -8
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/report-markdown.js +50 -53
- package/dist/lib/report-markdown.js.map +1 -1
- package/dist/lib/report.js +6 -22
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/types/agent/test-runner-nodes.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts +24 -0
- package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts.map +1 -0
- package/dist/types/ai-model/model-adapter/locate.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/types.d.ts +9 -3
- package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/grounding/utils.d.ts +4 -9
- package/dist/types/ai-model/workflows/grounding/utils.d.ts.map +1 -1
- package/dist/types/dump/report-image-export.d.ts +12 -0
- package/dist/types/dump/report-image-export.d.ts.map +1 -0
- package/dist/types/dump/screenshot-store.d.ts +2 -0
- package/dist/types/dump/screenshot-store.d.ts.map +1 -1
- package/dist/types/report-cli.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts +4 -2
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/report-markdown.d.ts +4 -2
- package/dist/types/report-markdown.d.ts.map +1 -1
- package/dist/types/report.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -58,7 +58,7 @@ class AgentReportManager {
|
|
|
58
58
|
report.session = session;
|
|
59
59
|
report.file = void 0;
|
|
60
60
|
}
|
|
61
|
-
report.generator.enableSourceSnapshots?.();
|
|
61
|
+
report.generator.enableSourceSnapshots?.(report.yamlScopeId);
|
|
62
62
|
}
|
|
63
63
|
async _createReportSource(scopeId, executionIds) {
|
|
64
64
|
return this.reportState.generator.createSourceSnapshot?.(scopeId, executionIds);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent/agent-report-manager.mjs","sources":["../../../src/agent/agent-report-manager.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { IS_REPORT_BUILD } from '../constants';\nimport { generateTestRunReportScriptTag } from '../dump/html-utils';\nimport {\n type ReportSession,\n currentReportSession,\n} from '../dump/report-session';\nimport {\n type IReportGenerator,\n ReportGenerator,\n assertReportGenerationOptions,\n nullReportGenerator,\n} from '../report-generator';\nimport type { TaskRunner } from '../task-runner';\nimport { WorkflowError } from '../test-runner/errors';\nimport type { WorkflowExecutionRecord } from '../test-runner/execution-record';\nimport { buildAgentTestRunReportDump } from '../test-runner/reporting/agent-report';\nimport { executionRecordsToReportInput } from '../test-runner/reporting/execution-record';\nimport {\n type AgentOpt,\n type ExecutionDump,\n ReportActionDump,\n type ReportMeta,\n} from '../types';\nimport { getVersion, reportHTMLContent } from '../utils';\nimport { getReportFileName } from './utils';\n\nconst warn = getDebug('agent', { console: true });\n\ntype ReportOptions = Pick<\n AgentOpt,\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'persistExecutionDump'\n | 'outputFormat'\n | 'autoPrintReportMsg'\n | 'reportAttributes'\n | 'reportFileName'\n | 'testId'\n>;\n\ninterface AgentReportContext {\n getOptions(): ReportOptions;\n getDeviceType(): ReportActionDump['deviceType'];\n isDestroyed(): boolean;\n isExternallyBusy(): boolean;\n}\n\ninterface AgentReportState {\n dump: ReportActionDump;\n file?: string | null;\n fileName?: string;\n generator: IReportGenerator;\n session?: ReportSession;\n finalization?: Promise<string | undefined>;\n lastExecution?: ExecutionDump;\n executionIndexByRunner: WeakMap<TaskRunner, number>;\n yamlScopeId: string;\n yamlRecords: WorkflowExecutionRecord[];\n activeOperations: Set<Promise<unknown>>;\n}\n\n/** Owns report state and persistence; execution and device ownership stay with Agent. */\nexport class AgentReportManager {\n private reportState: AgentReportState;\n private reportReset?: Promise<ReportActionDump>;\n private readonly reportByRunner = new WeakMap<TaskRunner, AgentReportState>();\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n constructor(private readonly context: AgentReportContext) {\n const opts = this.opts;\n const fileName =\n opts.reportFileName ??\n getReportFileName(opts.testId || context.getDeviceType() || 'web');\n this.reportState = this.createReportState(\n fileName,\n opts.reportAttributes?.['data-group-id'] === fileName,\n );\n }\n\n private get opts(): ReportOptions {\n return this.context.getOptions();\n }\n\n get dump(): ReportActionDump {\n return this.reportState.dump;\n }\n set dump(value: ReportActionDump) {\n this.reportState.dump = value;\n }\n\n get reportFile(): string | null | undefined {\n return this.reportState.file;\n }\n set reportFile(value: string | null | undefined) {\n this.reportState.file = value;\n }\n\n get reportFileName(): string | undefined {\n return this.reportState.fileName;\n }\n set reportFileName(value: string | undefined) {\n this.reportState.fileName = value;\n }\n\n /** @internal Runner snapshots retain immutable data and shared image files. */\n async _prepareForTestRunner(): Promise<void> {\n const report = this.reportState;\n const session = currentReportSession();\n if (session && session !== report.session) {\n if (report.session)\n throw new Error(\n 'An Agent cannot be shared by different Test report sessions. Await resetDump() between runs.',\n );\n this.assertReportIdle();\n await report.generator.finalize();\n const recorder = session.createRecorder(() => this.isReportBusy(report));\n // The previous standalone report owns its history. The Test stream starts\n // at acquisition, so previous script operations do not enter this run.\n this.clearDumpInMemory(report);\n report.generator = recorder;\n report.session = session;\n report.file = undefined;\n }\n report.generator.enableSourceSnapshots?.();\n }\n async _createReportSource(scopeId: string, executionIds?: readonly string[]) {\n return this.reportState.generator.createSourceSnapshot?.(\n scopeId,\n executionIds,\n );\n }\n\n /** @internal The Agent owns one report stream across root YAML invocations. */\n async _writeYamlExecutionReport(\n record: WorkflowExecutionRecord,\n ): Promise<string | undefined> {\n const report = this.reportState;\n const existingIndex = report.yamlRecords.findIndex(\n (entry) => entry.runId === record.runId,\n );\n const recordIndex =\n existingIndex < 0 ? report.yamlRecords.length : existingIndex;\n report.yamlRecords[recordIndex] = record;\n try {\n const path = await report.generator.writeRunnerReport?.(\n this.buildYamlReportInput(),\n report.yamlScopeId,\n this.getReportMeta(),\n );\n report.file = path ?? report.file;\n return path;\n } catch (error) {\n report.yamlRecords[recordIndex] = Object.freeze({\n ...record,\n status: 'failed',\n reportError: error,\n });\n throw error;\n }\n }\n\n private buildYamlReportInput() {\n const scopedRecords = this.reportState.yamlRecords.map((entry) => ({\n ...entry,\n ...(entry.execution\n ? {\n execution: {\n document: {\n ...entry.execution.document,\n reportScopeId: this.reportState.yamlScopeId,\n },\n cases: entry.execution.cases.map((outcome) => ({\n ...outcome,\n ...(outcome.run\n ? {\n run: {\n ...outcome.run,\n reportScopeId: this.reportState.yamlScopeId,\n },\n }\n : {}),\n ...(outcome.attempts\n ? {\n attempts: outcome.attempts.map((attempt) => ({\n ...attempt,\n reportScopeId: this.reportState.yamlScopeId,\n })),\n }\n : {}),\n })),\n },\n }\n : {}),\n }));\n return executionRecordsToReportInput(scopedRecords, {\n runId: this.reportState.yamlScopeId,\n });\n }\n\n private createEmptyDump(): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.context.getDeviceType(),\n });\n }\n\n private createReportState(\n fileName: string,\n reuseExistingReport = false,\n ): AgentReportState {\n assertReportGenerationOptions(this.opts);\n const session = currentReportSession();\n const state: AgentReportState = {\n dump: this.createEmptyDump(),\n fileName,\n generator: session\n ? nullReportGenerator\n : ReportGenerator.create(fileName, {\n generateReport: this.opts.generateReport,\n persistExecutionDump: this.opts.persistExecutionDump,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n reuseExistingReport,\n }),\n executionIndexByRunner: new WeakMap(),\n yamlScopeId: uuid(),\n yamlRecords: [],\n activeOperations: new Set(),\n };\n if (session) {\n state.generator = session.createRecorder(() => this.isReportBusy(state));\n state.session = session;\n }\n return state;\n }\n\n private isReportBusy(report: AgentReportState): boolean {\n return report.activeOperations.size > 0 || this.context.isExternallyBusy();\n }\n\n private clearDumpInMemory(report = this.reportState): void {\n report.dump = this.createEmptyDump();\n report.executionIndexByRunner = new WeakMap();\n report.lastExecution = undefined;\n report.yamlRecords.length = 0;\n }\n\n private assertReportIdle(): void {\n assert(\n !this.reportState.activeOperations.size &&\n !this.context.isExternallyBusy(),\n 'Cannot reset or finalize a report while the Agent is executing. Await the active operation first.',\n );\n }\n\n assertReportWritable(): AgentReportState {\n const report = this.reportState;\n assert(\n !this.context.isDestroyed(),\n 'Cannot execute using a destroyed Agent.',\n );\n assert(\n !this.reportReset,\n 'Report reset is in progress. Await resetDump() first.',\n );\n assert(\n !report.finalization,\n 'The report has been finalized. Call and await resetDump() before executing again.',\n );\n return report;\n }\n\n async withReportOperation<T>(operation: () => Promise<T>): Promise<T> {\n const report = this.assertReportWritable();\n const pending = Promise.resolve().then(operation);\n report.activeOperations.add(pending);\n try {\n return await pending;\n } finally {\n report.activeOperations.delete(pending);\n }\n }\n\n /**\n * Finalize the preceding report and reset execution records for a new report.\n * Historical artifacts are retained; this Agent and its device stay connected.\n * Await this method before executing again. Concurrent resets share one reset.\n */\n async resetDump(): Promise<ReportActionDump> {\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n if (this.reportReset) return this.reportReset;\n this.assertReportIdle();\n this.reportReset = (async () => {\n try {\n await this.finalizeReport();\n } catch (error) {\n // Failure belongs to the preceding report, not to the next write queue.\n warn(\n `Failed to finalize previous report; resetting dump: ${String(error)}`,\n );\n }\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n const fileName = getReportFileName(\n this.opts.testId || this.context.getDeviceType() || 'web',\n );\n this.reportState = this.createReportState(fileName);\n return this.dump;\n })();\n try {\n return await this.reportReset;\n } finally {\n this.reportReset = undefined;\n }\n }\n\n /**\n * Seal the current report and release its in-memory records without\n * disconnecting the device. Export in-memory data before calling this method.\n * Repeated calls return the same result and never rewrite published artifacts.\n */\n async finalizeReport(): Promise<string | undefined> {\n const report = this.reportState;\n if (!report.finalization) {\n this.assertReportIdle();\n report.finalization = (async () => {\n await report.generator.flush();\n const path = await report.generator.finalize();\n report.file = path;\n this.clearDumpInMemory(report);\n return path;\n })();\n }\n return report.finalization;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n assert(\n !this.reportState.finalization,\n 'Cannot append to a finalized report. Await resetDump() first.',\n );\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.reportState.executionIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.reportState.executionIndexByRunner.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(getDumpString: () => string) {\n // Short-circuit at the call site because JavaScript evaluates function\n // arguments first. This avoids serializing the dump (including inline\n // screenshots) when the Report Viewer build does not need report HTML.\n if (IS_REPORT_BUILD) {\n return '';\n }\n\n // dumpDataString() handles browser environment with inline screenshots\n const dumpString = getDumpString();\n if (!this.reportState.yamlRecords.length)\n return reportHTMLContent(dumpString);\n\n // Browser exports have no file writer, so the execution record must own\n // the hierarchy independently of whether file report generation is enabled.\n const reportId = this.reportState.yamlScopeId;\n const runnerDump = buildAgentTestRunReportDump(\n this.buildYamlReportInput(),\n this.dump,\n {\n reportId,\n scopeId: this.reportState.yamlScopeId,\n sourcePath: this.reportFile ?? '<inline-agent-report>',\n },\n );\n return `${reportHTMLContent({\n dumpString,\n attributes: { 'data-group-id': reportId, 'data-report-id': reportId },\n })}\\n${generateTestRunReportScriptTag(runnerDump)}`;\n }\n\n writeOutActionDumps(executionDump?: ExecutionDump) {\n const report = this.reportState;\n if (report.finalization) {\n if (executionDump) {\n throw new WorkflowError(\n 'Cannot append to a finalized report. Await resetDump() first.',\n {\n code: 'REPORT_FINALIZED',\n },\n );\n }\n return;\n }\n const exec = executionDump || report.lastExecution;\n if (exec) {\n report.lastExecution = exec;\n report.generator.onExecutionUpdate(\n exec,\n this.getReportMeta(),\n this.opts.reportAttributes,\n );\n }\n report.file = report.generator.getReportPath();\n }\n\n /** Persist current report data without disposing this Agent or its device. */\n async flushReport(): Promise<string | undefined> {\n if (this.reportReset) await this.reportReset;\n const report = this.reportState;\n if (report.finalization) return report.finalization;\n this.writeOutActionDumps();\n await report.generator.flush();\n return report.lastExecution ? (report.file ?? undefined) : undefined;\n }\n\n private getReportMeta(): ReportMeta {\n return {\n groupName: this.dump.groupName,\n groupDescription: this.dump.groupDescription,\n sdkVersion: this.dump.sdkVersion,\n modelBriefs: this.dump.modelBriefs,\n deviceType: this.dump.deviceType,\n };\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 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 notifyDumpUpdateListeners(executionDump?: ExecutionDump) {\n if (this.dumpUpdateListeners.length === 0) return;\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\n bindRunner(runner: TaskRunner): void {\n this.reportByRunner.set(runner, this.reportState);\n }\n\n assertRunnerCurrent(runner: TaskRunner): void {\n const report = this.reportByRunner.get(runner);\n assert(\n report === this.reportState && !report.finalization,\n 'Cannot write a late execution update into a different or finalized report.',\n );\n }\n\n async flushPendingWrites(): Promise<void> {\n await this.reportState.generator.flush();\n }\n\n async waitForOperations(): Promise<void> {\n while (this.reportState.activeOperations.size) {\n await Promise.allSettled([...this.reportState.activeOperations]);\n }\n }\n\n async waitForReset(): Promise<void> {\n if (this.reportReset) await this.reportReset.catch(() => {});\n }\n}\n"],"names":["warn","getDebug","AgentReportManager","value","report","session","currentReportSession","Error","recorder","undefined","scopeId","executionIds","record","existingIndex","entry","recordIndex","path","error","Object","scopedRecords","outcome","attempt","executionRecordsToReportInput","ReportActionDump","getVersion","fileName","reuseExistingReport","assertReportGenerationOptions","state","nullReportGenerator","ReportGenerator","WeakMap","uuid","Set","assert","operation","pending","Promise","String","getReportFileName","execution","runner","currentDump","opt","ifInBrowser","getDumpString","IS_REPORT_BUILD","dumpString","reportHTMLContent","reportId","runnerDump","buildAgentTestRunReportDump","generateTestRunReportScriptTag","executionDump","WorkflowError","exec","callback","listener","index","console","context","opts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent/agent-report-manager.mjs","sources":["../../../src/agent/agent-report-manager.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { IS_REPORT_BUILD } from '../constants';\nimport { generateTestRunReportScriptTag } from '../dump/html-utils';\nimport {\n type ReportSession,\n currentReportSession,\n} from '../dump/report-session';\nimport {\n type IReportGenerator,\n ReportGenerator,\n assertReportGenerationOptions,\n nullReportGenerator,\n} from '../report-generator';\nimport type { TaskRunner } from '../task-runner';\nimport { WorkflowError } from '../test-runner/errors';\nimport type { WorkflowExecutionRecord } from '../test-runner/execution-record';\nimport { buildAgentTestRunReportDump } from '../test-runner/reporting/agent-report';\nimport { executionRecordsToReportInput } from '../test-runner/reporting/execution-record';\nimport {\n type AgentOpt,\n type ExecutionDump,\n ReportActionDump,\n type ReportMeta,\n} from '../types';\nimport { getVersion, reportHTMLContent } from '../utils';\nimport { getReportFileName } from './utils';\n\nconst warn = getDebug('agent', { console: true });\n\ntype ReportOptions = Pick<\n AgentOpt,\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'persistExecutionDump'\n | 'outputFormat'\n | 'autoPrintReportMsg'\n | 'reportAttributes'\n | 'reportFileName'\n | 'testId'\n>;\n\ninterface AgentReportContext {\n getOptions(): ReportOptions;\n getDeviceType(): ReportActionDump['deviceType'];\n isDestroyed(): boolean;\n isExternallyBusy(): boolean;\n}\n\ninterface AgentReportState {\n dump: ReportActionDump;\n file?: string | null;\n fileName?: string;\n generator: IReportGenerator;\n session?: ReportSession;\n finalization?: Promise<string | undefined>;\n lastExecution?: ExecutionDump;\n executionIndexByRunner: WeakMap<TaskRunner, number>;\n yamlScopeId: string;\n yamlRecords: WorkflowExecutionRecord[];\n activeOperations: Set<Promise<unknown>>;\n}\n\n/** Owns report state and persistence; execution and device ownership stay with Agent. */\nexport class AgentReportManager {\n private reportState: AgentReportState;\n private reportReset?: Promise<ReportActionDump>;\n private readonly reportByRunner = new WeakMap<TaskRunner, AgentReportState>();\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n constructor(private readonly context: AgentReportContext) {\n const opts = this.opts;\n const fileName =\n opts.reportFileName ??\n getReportFileName(opts.testId || context.getDeviceType() || 'web');\n this.reportState = this.createReportState(\n fileName,\n opts.reportAttributes?.['data-group-id'] === fileName,\n );\n }\n\n private get opts(): ReportOptions {\n return this.context.getOptions();\n }\n\n get dump(): ReportActionDump {\n return this.reportState.dump;\n }\n set dump(value: ReportActionDump) {\n this.reportState.dump = value;\n }\n\n get reportFile(): string | null | undefined {\n return this.reportState.file;\n }\n set reportFile(value: string | null | undefined) {\n this.reportState.file = value;\n }\n\n get reportFileName(): string | undefined {\n return this.reportState.fileName;\n }\n set reportFileName(value: string | undefined) {\n this.reportState.fileName = value;\n }\n\n /** @internal Runner snapshots retain immutable data and shared image files. */\n async _prepareForTestRunner(): Promise<void> {\n const report = this.reportState;\n const session = currentReportSession();\n if (session && session !== report.session) {\n if (report.session)\n throw new Error(\n 'An Agent cannot be shared by different Test report sessions. Await resetDump() between runs.',\n );\n this.assertReportIdle();\n await report.generator.finalize();\n const recorder = session.createRecorder(() => this.isReportBusy(report));\n // The previous standalone report owns its history. The Test stream starts\n // at acquisition, so previous script operations do not enter this run.\n this.clearDumpInMemory(report);\n report.generator = recorder;\n report.session = session;\n report.file = undefined;\n }\n report.generator.enableSourceSnapshots?.(report.yamlScopeId);\n }\n async _createReportSource(scopeId: string, executionIds?: readonly string[]) {\n return this.reportState.generator.createSourceSnapshot?.(\n scopeId,\n executionIds,\n );\n }\n\n /** @internal The Agent owns one report stream across root YAML invocations. */\n async _writeYamlExecutionReport(\n record: WorkflowExecutionRecord,\n ): Promise<string | undefined> {\n const report = this.reportState;\n const existingIndex = report.yamlRecords.findIndex(\n (entry) => entry.runId === record.runId,\n );\n const recordIndex =\n existingIndex < 0 ? report.yamlRecords.length : existingIndex;\n report.yamlRecords[recordIndex] = record;\n try {\n const path = await report.generator.writeRunnerReport?.(\n this.buildYamlReportInput(),\n report.yamlScopeId,\n this.getReportMeta(),\n );\n report.file = path ?? report.file;\n return path;\n } catch (error) {\n report.yamlRecords[recordIndex] = Object.freeze({\n ...record,\n status: 'failed',\n reportError: error,\n });\n throw error;\n }\n }\n\n private buildYamlReportInput() {\n const scopedRecords = this.reportState.yamlRecords.map((entry) => ({\n ...entry,\n ...(entry.execution\n ? {\n execution: {\n document: {\n ...entry.execution.document,\n reportScopeId: this.reportState.yamlScopeId,\n },\n cases: entry.execution.cases.map((outcome) => ({\n ...outcome,\n ...(outcome.run\n ? {\n run: {\n ...outcome.run,\n reportScopeId: this.reportState.yamlScopeId,\n },\n }\n : {}),\n ...(outcome.attempts\n ? {\n attempts: outcome.attempts.map((attempt) => ({\n ...attempt,\n reportScopeId: this.reportState.yamlScopeId,\n })),\n }\n : {}),\n })),\n },\n }\n : {}),\n }));\n return executionRecordsToReportInput(scopedRecords, {\n runId: this.reportState.yamlScopeId,\n });\n }\n\n private createEmptyDump(): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.context.getDeviceType(),\n });\n }\n\n private createReportState(\n fileName: string,\n reuseExistingReport = false,\n ): AgentReportState {\n assertReportGenerationOptions(this.opts);\n const session = currentReportSession();\n const state: AgentReportState = {\n dump: this.createEmptyDump(),\n fileName,\n generator: session\n ? nullReportGenerator\n : ReportGenerator.create(fileName, {\n generateReport: this.opts.generateReport,\n persistExecutionDump: this.opts.persistExecutionDump,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n reuseExistingReport,\n }),\n executionIndexByRunner: new WeakMap(),\n yamlScopeId: uuid(),\n yamlRecords: [],\n activeOperations: new Set(),\n };\n if (session) {\n state.generator = session.createRecorder(() => this.isReportBusy(state));\n state.session = session;\n }\n return state;\n }\n\n private isReportBusy(report: AgentReportState): boolean {\n return report.activeOperations.size > 0 || this.context.isExternallyBusy();\n }\n\n private clearDumpInMemory(report = this.reportState): void {\n report.dump = this.createEmptyDump();\n report.executionIndexByRunner = new WeakMap();\n report.lastExecution = undefined;\n report.yamlRecords.length = 0;\n }\n\n private assertReportIdle(): void {\n assert(\n !this.reportState.activeOperations.size &&\n !this.context.isExternallyBusy(),\n 'Cannot reset or finalize a report while the Agent is executing. Await the active operation first.',\n );\n }\n\n assertReportWritable(): AgentReportState {\n const report = this.reportState;\n assert(\n !this.context.isDestroyed(),\n 'Cannot execute using a destroyed Agent.',\n );\n assert(\n !this.reportReset,\n 'Report reset is in progress. Await resetDump() first.',\n );\n assert(\n !report.finalization,\n 'The report has been finalized. Call and await resetDump() before executing again.',\n );\n return report;\n }\n\n async withReportOperation<T>(operation: () => Promise<T>): Promise<T> {\n const report = this.assertReportWritable();\n const pending = Promise.resolve().then(operation);\n report.activeOperations.add(pending);\n try {\n return await pending;\n } finally {\n report.activeOperations.delete(pending);\n }\n }\n\n /**\n * Finalize the preceding report and reset execution records for a new report.\n * Historical artifacts are retained; this Agent and its device stay connected.\n * Await this method before executing again. Concurrent resets share one reset.\n */\n async resetDump(): Promise<ReportActionDump> {\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n if (this.reportReset) return this.reportReset;\n this.assertReportIdle();\n this.reportReset = (async () => {\n try {\n await this.finalizeReport();\n } catch (error) {\n // Failure belongs to the preceding report, not to the next write queue.\n warn(\n `Failed to finalize previous report; resetting dump: ${String(error)}`,\n );\n }\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n const fileName = getReportFileName(\n this.opts.testId || this.context.getDeviceType() || 'web',\n );\n this.reportState = this.createReportState(fileName);\n return this.dump;\n })();\n try {\n return await this.reportReset;\n } finally {\n this.reportReset = undefined;\n }\n }\n\n /**\n * Seal the current report and release its in-memory records without\n * disconnecting the device. Export in-memory data before calling this method.\n * Repeated calls return the same result and never rewrite published artifacts.\n */\n async finalizeReport(): Promise<string | undefined> {\n const report = this.reportState;\n if (!report.finalization) {\n this.assertReportIdle();\n report.finalization = (async () => {\n await report.generator.flush();\n const path = await report.generator.finalize();\n report.file = path;\n this.clearDumpInMemory(report);\n return path;\n })();\n }\n return report.finalization;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n assert(\n !this.reportState.finalization,\n 'Cannot append to a finalized report. Await resetDump() first.',\n );\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.reportState.executionIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.reportState.executionIndexByRunner.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(getDumpString: () => string) {\n // Short-circuit at the call site because JavaScript evaluates function\n // arguments first. This avoids serializing the dump (including inline\n // screenshots) when the Report Viewer build does not need report HTML.\n if (IS_REPORT_BUILD) {\n return '';\n }\n\n // dumpDataString() handles browser environment with inline screenshots\n const dumpString = getDumpString();\n if (!this.reportState.yamlRecords.length)\n return reportHTMLContent(dumpString);\n\n // Browser exports have no file writer, so the execution record must own\n // the hierarchy independently of whether file report generation is enabled.\n const reportId = this.reportState.yamlScopeId;\n const runnerDump = buildAgentTestRunReportDump(\n this.buildYamlReportInput(),\n this.dump,\n {\n reportId,\n scopeId: this.reportState.yamlScopeId,\n sourcePath: this.reportFile ?? '<inline-agent-report>',\n },\n );\n return `${reportHTMLContent({\n dumpString,\n attributes: { 'data-group-id': reportId, 'data-report-id': reportId },\n })}\\n${generateTestRunReportScriptTag(runnerDump)}`;\n }\n\n writeOutActionDumps(executionDump?: ExecutionDump) {\n const report = this.reportState;\n if (report.finalization) {\n if (executionDump) {\n throw new WorkflowError(\n 'Cannot append to a finalized report. Await resetDump() first.',\n {\n code: 'REPORT_FINALIZED',\n },\n );\n }\n return;\n }\n const exec = executionDump || report.lastExecution;\n if (exec) {\n report.lastExecution = exec;\n report.generator.onExecutionUpdate(\n exec,\n this.getReportMeta(),\n this.opts.reportAttributes,\n );\n }\n report.file = report.generator.getReportPath();\n }\n\n /** Persist current report data without disposing this Agent or its device. */\n async flushReport(): Promise<string | undefined> {\n if (this.reportReset) await this.reportReset;\n const report = this.reportState;\n if (report.finalization) return report.finalization;\n this.writeOutActionDumps();\n await report.generator.flush();\n return report.lastExecution ? (report.file ?? undefined) : undefined;\n }\n\n private getReportMeta(): ReportMeta {\n return {\n groupName: this.dump.groupName,\n groupDescription: this.dump.groupDescription,\n sdkVersion: this.dump.sdkVersion,\n modelBriefs: this.dump.modelBriefs,\n deviceType: this.dump.deviceType,\n };\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 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 notifyDumpUpdateListeners(executionDump?: ExecutionDump) {\n if (this.dumpUpdateListeners.length === 0) return;\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\n bindRunner(runner: TaskRunner): void {\n this.reportByRunner.set(runner, this.reportState);\n }\n\n assertRunnerCurrent(runner: TaskRunner): void {\n const report = this.reportByRunner.get(runner);\n assert(\n report === this.reportState && !report.finalization,\n 'Cannot write a late execution update into a different or finalized report.',\n );\n }\n\n async flushPendingWrites(): Promise<void> {\n await this.reportState.generator.flush();\n }\n\n async waitForOperations(): Promise<void> {\n while (this.reportState.activeOperations.size) {\n await Promise.allSettled([...this.reportState.activeOperations]);\n }\n }\n\n async waitForReset(): Promise<void> {\n if (this.reportReset) await this.reportReset.catch(() => {});\n }\n}\n"],"names":["warn","getDebug","AgentReportManager","value","report","session","currentReportSession","Error","recorder","undefined","scopeId","executionIds","record","existingIndex","entry","recordIndex","path","error","Object","scopedRecords","outcome","attempt","executionRecordsToReportInput","ReportActionDump","getVersion","fileName","reuseExistingReport","assertReportGenerationOptions","state","nullReportGenerator","ReportGenerator","WeakMap","uuid","Set","assert","operation","pending","Promise","String","getReportFileName","execution","runner","currentDump","opt","ifInBrowser","getDumpString","IS_REPORT_BUILD","dumpString","reportHTMLContent","reportId","runnerDump","buildAgentTestRunReportDump","generateTestRunReportScriptTag","executionDump","WorkflowError","exec","callback","listener","index","console","context","opts"],"mappings":"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"}
|
|
@@ -120,7 +120,7 @@ const aiKeyboardPressInputSchema = z.strictObject({
|
|
|
120
120
|
options: locateOptionsInputSchema.optional()
|
|
121
121
|
});
|
|
122
122
|
const aiScrollInputSchema = z.strictObject({
|
|
123
|
-
prompt: userPromptInputSchema.optional(),
|
|
123
|
+
prompt: userPromptInputSchema.optional().describe('Optional locator for a currently visible scrollable container, such as "the product list". Omit for page scrolling. Describe the container, not a scrolling instruction or an off-screen target to find. Set direction, distance, and scrollType in options.'),
|
|
124
124
|
options: locateOptionsInputSchema.extend({
|
|
125
125
|
direction: z["enum"]([
|
|
126
126
|
'down',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent/test-runner-nodes.mjs","sources":["../../../src/agent/test-runner-nodes.ts"],"sourcesContent":["import { AGENT_AI_CONTEXT_KEYS } from '@midscene/shared/agent-tools/agent-context';\nimport { z } from 'zod/v4';\nimport type { TUserPrompt } from '../common';\nimport { inputStrategies } from '../device/input-strategy';\nimport { NodeExecutionError } from '../test-runner/errors';\nimport type { LocateOption, ScrollParam } from '../yaml';\nimport { buildDetailedLocateParam } from '../yaml/utils';\nimport type { Agent } from './agent';\n\nexport interface AgentTestRunnerNodeResult<TData = unknown> {\n summary?: string;\n data?: TData;\n}\n\nexport interface AgentTestRunnerNodeExecutionContext {\n signal: AbortSignal;\n}\n\n/** Agent-owned Node description consumed by Test Runner adapters. */\nexport interface AgentTestRunnerNodeDefinition<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TData = unknown,\n> {\n name: string;\n title?: string;\n description?: string;\n /** Node input field populated by YAML string shorthand; omit to disable. */\n stringInputKey?: string | false;\n inputSchema: z.ZodObject;\n execute(\n agent: unknown,\n input: TInput,\n context: AgentTestRunnerNodeExecutionContext,\n ):\n | Promise<AgentTestRunnerNodeResult<TData> | undefined>\n | AgentTestRunnerNodeResult<TData>\n | undefined;\n}\n\n/** Static capability implemented by Agent classes that expose Runner Nodes. */\nexport interface AgentTestRunnerNodeProvider {\n getTestRunnerNodeDefinitions(): readonly AgentTestRunnerNodeDefinition[];\n}\n\ntype AnyAgentMethod = (...args: any[]) => unknown;\ntype AgentMethodName<TAgent extends object> = {\n [TKey in keyof TAgent]-?: TAgent[TKey] extends AnyAgentMethod ? TKey : never;\n}[keyof TAgent] &\n string;\ntype AgentMethod<\n TAgent extends object,\n TMethod extends AgentMethodName<TAgent>,\n> = Extract<TAgent[TMethod], AnyAgentMethod>;\n\nexport interface DefineAgentTestRunnerNodeOptions<\n TAgent extends object,\n TMethod extends AgentMethodName<TAgent>,\n TSchema extends z.ZodObject,\n TData = unknown,\n> {\n method: TMethod;\n name?: string;\n title?: string;\n description?: string;\n stringInputKey?: (keyof z.output<TSchema> & string) | false;\n inputSchema: TSchema;\n toArgs(\n input: z.output<TSchema>,\n context: AgentTestRunnerNodeExecutionContext,\n ): Parameters<AgentMethod<TAgent, TMethod>>;\n toResult?(\n result: Awaited<ReturnType<AgentMethod<TAgent, TMethod>>>,\n input: z.output<TSchema>,\n ): AgentTestRunnerNodeResult<TData> | undefined;\n}\n\n/** Create a Node factory whose adapters are checked against an Agent API. */\nexport const createAgentTestRunnerNodeDefinition =\n <TAgent extends object>(agentLabel = 'an Agent') =>\n <\n TMethod extends AgentMethodName<TAgent>,\n TSchema extends z.ZodObject,\n TData = unknown,\n >(\n definition: DefineAgentTestRunnerNodeOptions<\n TAgent,\n TMethod,\n TSchema,\n TData\n >,\n ): AgentTestRunnerNodeDefinition => ({\n name: definition.name ?? definition.method,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(agent, input, context) {\n if (\n typeof agent !== 'object' ||\n agent === null ||\n typeof (agent as Record<string, unknown>)[definition.method] !==\n 'function'\n ) {\n throw new TypeError(\n `getAgent() must return ${agentLabel} with ${definition.method}().`,\n );\n }\n\n const typedAgent = agent as TAgent;\n const method = typedAgent[definition.method] as AgentMethod<\n TAgent,\n TMethod\n >;\n const typedInput = input as z.output<TSchema>;\n const result = (await method.apply(\n typedAgent,\n definition.toArgs(typedInput, context),\n )) as Awaited<ReturnType<AgentMethod<TAgent, TMethod>>>;\n return definition.toResult?.(result, typedInput);\n },\n });\n\nconst nonBlankText = (description: string) =>\n z\n .string()\n .regex(/\\S/, 'value must contain a non-whitespace character')\n .describe(description);\n\nexport const promptImageInputSchema = z.strictObject({\n name: nonBlankText('The name used to identify this reference image.'),\n url: nonBlankText('The URL, data URL, or file path of this reference image.'),\n});\n\nexport const structuredUserPromptInputSchema = z.strictObject({\n prompt: nonBlankText('The natural-language prompt.'),\n images: z.array(promptImageInputSchema).min(1).optional(),\n convertHttpImage2Base64: z\n .boolean()\n .optional()\n .describe('Whether HTTP reference images are converted to base64 first.'),\n});\n\nexport const userPromptInputSchema = z.union([\n nonBlankText('The natural-language prompt.'),\n structuredUserPromptInputSchema,\n]);\n\nexport const aiActOptionsInputSchema = z.strictObject({\n effort: z\n .enum(['fast', 'balance', 'deepThink'])\n .optional()\n .describe('Action planning effort: fast, balanced, or deeper reasoning.'),\n cacheable: z\n .boolean()\n .optional()\n .describe('Whether this action may use the Midscene cache.'),\n fileChooserAccept: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe('Accepted file types for a file chooser.'),\n fileChooserAllowedDir: z\n .string()\n .optional()\n .describe('Directory allowed for prompt-driven file uploads.'),\n deepThink: z\n .union([z.literal('unset'), z.boolean()])\n .optional()\n .describe('Whether to enable deep thinking for this action.'),\n deepLocate: z\n .boolean()\n .optional()\n .describe('Whether to use deep element location.'),\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n});\n\nexport const setAIContextInputSchema = z.strictObject({\n target: z\n .enum(AGENT_AI_CONTEXT_KEYS)\n .describe(\n 'The AI API to configure, e.g. aiAct or aiAssert. Use default as the shared fallback for all AI APIs when neither the call nor the API has its own context.',\n ),\n context: z\n .string()\n .optional()\n .describe(\n 'Agent-level guidance. Omit to remove the selected context; an empty string disables inherited context.',\n ),\n});\n\nexport const aiActInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: aiActOptionsInputSchema.optional(),\n});\n\nexport const insightOptionsInputSchema = z.strictObject({\n domIncluded: z\n .union([z.boolean(), z.literal('visible-only')])\n .optional()\n .describe('How DOM information is included.'),\n screenshotIncluded: z\n .boolean()\n .optional()\n .describe('Whether the request includes a screenshot.'),\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n});\n\nexport const aiAssertOptionsInputSchema = insightOptionsInputSchema.extend({\n keepRawResponse: z\n .boolean()\n .optional()\n .describe('Whether the Agent returns the structured assertion result.'),\n});\n\nexport const aiAssertInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n message: z.string().optional().describe('The assertion failure message.'),\n options: aiAssertOptionsInputSchema.optional(),\n});\n\nexport const locateOptionsInputSchema = z.strictObject({\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n deepLocate: z\n .boolean()\n .optional()\n .describe('Whether to use deep element location.'),\n deepThink: z\n .boolean()\n .optional()\n .describe('Deprecated alias for deepLocate.'),\n cacheable: z\n .boolean()\n .optional()\n .describe('Whether this location may use the Midscene cache.'),\n xpath: z.string().optional().describe('A web XPath location hint.'),\n fileChooserAccept: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe('Files accepted when tapping opens a file chooser.'),\n});\n\nexport const aiTapInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiInputInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n value: z.union([z.string(), z.number()]),\n options: locateOptionsInputSchema\n .extend({\n mode: z.enum(['replace', 'clear', 'typeOnly', 'append']).optional(),\n autoDismissKeyboard: z.boolean().optional(),\n keyboardTypeDelay: z.number().nonnegative().optional(),\n inputStrategy: z.enum(inputStrategies).optional(),\n })\n .optional(),\n});\n\nexport const aiKeyboardPressInputSchema = z.strictObject({\n prompt: userPromptInputSchema.optional(),\n keyName: nonBlankText('The key or key combination to press.'),\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiScrollInputSchema = z.strictObject({\n prompt: userPromptInputSchema.optional(),\n options: locateOptionsInputSchema\n .extend({\n direction: z.enum(['down', 'up', 'right', 'left']).optional(),\n scrollType: z\n .enum([\n 'singleAction',\n 'scrollToBottom',\n 'scrollToTop',\n 'scrollToRight',\n 'scrollToLeft',\n 'once',\n 'untilBottom',\n 'untilTop',\n 'untilRight',\n 'untilLeft',\n ])\n .optional(),\n distance: z.number().nullable().optional(),\n })\n .optional(),\n});\n\nexport const aiPinchInputSchema = z.strictObject({\n prompt: userPromptInputSchema.optional(),\n direction: z.enum(['in', 'out']),\n options: locateOptionsInputSchema\n .extend({\n distance: z.number().optional(),\n duration: z.number().nonnegative().optional(),\n })\n .optional(),\n});\n\nexport const aiLongPressInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: locateOptionsInputSchema\n .extend({ duration: z.number().nonnegative().optional() })\n .optional(),\n});\n\nexport const aiDragAndDropInputSchema = z.strictObject({\n from: userPromptInputSchema,\n to: userPromptInputSchema,\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiQueryInputSchema = z.strictObject({\n prompt: z.union([\n nonBlankText('The extraction request.'),\n z.record(z.string(), z.string()),\n ]),\n options: insightOptionsInputSchema.optional(),\n});\n\nexport const aiWaitForOptionsInputSchema = insightOptionsInputSchema\n .extend({\n timeoutMs: z.number().positive().optional(),\n checkIntervalMs: z.number().positive().optional(),\n })\n .superRefine((input, context) => {\n if ((input.checkIntervalMs ?? 3000) > (input.timeoutMs ?? 15000)) {\n context.addIssue({\n code: 'custom',\n path: ['checkIntervalMs'],\n message: 'checkIntervalMs must not exceed timeoutMs',\n });\n }\n });\n\nexport const aiWaitForInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: aiWaitForOptionsInputSchema.optional(),\n});\n\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\n\nexport const javascriptInputSchema = z.strictObject({\n script: nonBlankText('JavaScript to evaluate through the current interface.'),\n});\n\nexport const runGherkinScenarioInputSchema = z.strictObject({\n scenario: nonBlankText('The Gherkin scenario to execute.'),\n options: aiActOptionsInputSchema.optional(),\n});\n\nexport const actionInputSchema = z.strictObject({\n name: nonBlankText('The ActionSpace action name, for example DragAndDrop.'),\n params: z.unknown().optional(),\n});\n\nexport const insightInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: insightOptionsInputSchema.optional(),\n});\n\nexport const reportScreenshotInputSchema = z.strictObject({\n base64: z.string().min(1).describe('A base64-encoded screenshot.'),\n description: z.string().optional().describe('What the screenshot shows.'),\n});\n\nexport const recordToReportOptionsInputSchema = z\n .strictObject({\n content: z.string().optional().describe('The report text content.'),\n screenshotBase64: z\n .string()\n .optional()\n .describe('One base64-encoded screenshot.'),\n screenshots: z\n .array(reportScreenshotInputSchema)\n .min(1)\n .optional()\n .describe('Screenshots attached to the report section.'),\n })\n .superRefine((input, context) => {\n if (\n input.screenshotBase64 !== undefined &&\n input.screenshots !== undefined\n ) {\n context.addIssue({\n code: 'custom',\n message: 'screenshotBase64 and screenshots are mutually exclusive',\n });\n }\n });\n\nexport const recordToReportInputSchema = z.strictObject({\n title: z.string().optional().describe('The report section title.'),\n options: recordToReportOptionsInputSchema.optional(),\n});\n\nexport type UserPromptNodeInput = z.infer<typeof userPromptInputSchema>;\nexport type SetAIContextNodeInput = z.infer<typeof setAIContextInputSchema>;\nexport type AiActNodeOptions = z.infer<typeof aiActOptionsInputSchema>;\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeOptions = z.infer<typeof aiAssertOptionsInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiTapNodeOptions = z.infer<typeof locateOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type AiInputNodeInput = z.infer<typeof aiInputInputSchema>;\nexport type AiKeyboardPressNodeInput = z.infer<\n typeof aiKeyboardPressInputSchema\n>;\nexport type AiScrollNodeInput = z.infer<typeof aiScrollInputSchema>;\nexport type AiPinchNodeInput = z.infer<typeof aiPinchInputSchema>;\nexport type AiLongPressNodeInput = z.infer<typeof aiLongPressInputSchema>;\nexport type AiDragAndDropNodeInput = z.infer<typeof aiDragAndDropInputSchema>;\nexport type AiQueryNodeInput = z.infer<typeof aiQueryInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type JavascriptNodeInput = z.infer<typeof javascriptInputSchema>;\nexport type RunGherkinScenarioNodeInput = z.infer<\n typeof runGherkinScenarioInputSchema\n>;\nexport type ActionNodeInput = z.infer<typeof actionInputSchema>;\nexport type InsightNodeOptions = z.infer<typeof insightOptionsInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeOptions = z.infer<\n typeof recordToReportOptionsInputSchema\n>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\n\nexport type CommonAgentTestRunnerApi = Pick<\n Agent,\n | 'aiAct'\n | 'aiTap'\n | 'aiAssert'\n | 'aiBoolean'\n | 'aiNumber'\n | 'aiString'\n | 'aiAsk'\n | 'recordToReport'\n>;\n\nconst defineCommonAgentNode =\n createAgentTestRunnerNodeDefinition<CommonAgentTestRunnerApi>('an Agent');\n\n// Select the current overloads without expanding the minimum API required by\n// existing getAgent() providers. Each Node checks its own method at execution.\ntype AgentActionNodeApi = Pick<\n Agent,\n | 'sleep'\n | 'setAIContext'\n | 'aiHover'\n | 'aiDoubleClick'\n | 'aiRightClick'\n | 'aiMiddleClick'\n | 'aiLongPress'\n | 'aiClearInput'\n | 'aiPinch'\n | 'aiLocate'\n | 'aiQuery'\n | 'aiWaitFor'\n | 'evaluateJavaScript'\n | 'runGherkinScenario'\n | 'callActionInActionSpace'\n> & {\n aiInput(\n prompt: TUserPrompt,\n options: NonNullable<Parameters<Agent['aiInput']>[2]> & {\n value: string | number;\n },\n ): Promise<void>;\n aiKeyboardPress(\n prompt: TUserPrompt | undefined,\n options: LocateOption & { keyName: string },\n ): Promise<void>;\n aiScroll(\n prompt: TUserPrompt | undefined,\n options: LocateOption & ScrollParam,\n ): Promise<void>;\n};\n\nconst defineAgentActionNode =\n createAgentTestRunnerNodeDefinition<AgentActionNodeApi>('an Agent');\n\nconst promptText = (prompt: UserPromptNodeInput): string =>\n typeof prompt === 'string' ? prompt : prompt.prompt;\n\nconst valueResult = (\n value: unknown,\n): AgentTestRunnerNodeResult | undefined => ({ data: value });\n\nconst aiActNode = defineCommonAgentNode({\n method: 'aiAct',\n description: 'Perform a natural-language task with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiActInputSchema,\n toArgs(input, context) {\n return [\n input.prompt,\n {\n ...input.options,\n abortSignal: context.signal,\n },\n ];\n },\n toResult(output) {\n return output === undefined ? undefined : { summary: output };\n },\n});\n\nconst aiTapNode = defineCommonAgentNode({\n method: 'aiTap',\n description: 'Locate and tap an element with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs(input) {\n return [input.prompt, input.options];\n },\n toResult(_output, input) {\n return { summary: `Tapped: ${promptText(input.prompt)}` };\n },\n});\n\nconst aiAssertNode = defineCommonAgentNode({\n method: 'aiAssert',\n description: 'Assert a natural-language condition with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiAssertInputSchema,\n toArgs(input, context) {\n return [\n input.prompt,\n input.message,\n {\n keepRawResponse: true,\n ...input.options,\n abortSignal: context.signal,\n },\n ];\n },\n toResult(output, input) {\n const result = {\n summary: `Assertion ${output?.pass === false ? 'failed' : 'passed'}: ${promptText(input.prompt)}`,\n ...(output === undefined\n ? {}\n : {\n data: {\n pass: output.pass,\n ...(output.thought === undefined\n ? {}\n : { thought: output.thought }),\n ...(output.message === undefined\n ? {}\n : { message: output.message }),\n },\n }),\n };\n if (output?.pass === false)\n throw new NodeExecutionError(\n 'aiAssert',\n new Error(\n output.message ||\n input.message ||\n output.thought ||\n `Assertion failed: ${promptText(input.prompt)}`,\n ),\n result,\n );\n return result;\n },\n});\n\nconst locateActionNode = (\n method:\n | 'aiHover'\n | 'aiDoubleClick'\n | 'aiRightClick'\n | 'aiMiddleClick'\n | 'aiClearInput',\n) =>\n defineAgentActionNode({\n method,\n description: {\n aiHover: 'Locate and hover over an element with a Midscene UI Agent.',\n aiDoubleClick:\n 'Locate and double-click an element with a Midscene UI Agent.',\n aiMiddleClick:\n 'Locate and middle-click an element with a Midscene UI Agent.',\n aiRightClick:\n 'Locate and right-click an element with a Midscene UI Agent.',\n aiClearInput:\n 'Locate an input element and clear its value with a Midscene UI Agent.',\n }[method],\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs: (input) => [input.prompt, input.options],\n toResult: (_output, input) => ({\n summary: `${method}: ${promptText(input.prompt)}`,\n }),\n });\n\nconst additionalAgentNodes: readonly AgentTestRunnerNodeDefinition[] = [\n defineAgentActionNode({\n method: 'setAIContext',\n description:\n 'Set or remove Agent-level AI guidance for subsequent calls on the same Agent.',\n inputSchema: setAIContextInputSchema,\n toArgs: (input) => [input.target, input.context],\n }),\n locateActionNode('aiHover'),\n locateActionNode('aiDoubleClick'),\n locateActionNode('aiRightClick'),\n locateActionNode('aiMiddleClick'),\n locateActionNode('aiClearInput'),\n defineAgentActionNode({\n method: 'aiInput',\n description:\n 'Locate an input element and enter a value with a Midscene UI Agent.',\n inputSchema: aiInputInputSchema,\n toArgs: (input) => [input.prompt, { ...input.options, value: input.value }],\n }),\n defineAgentActionNode({\n method: 'aiKeyboardPress',\n description:\n 'Press a key or key combination, optionally targeting a located element.',\n stringInputKey: 'keyName',\n inputSchema: aiKeyboardPressInputSchema,\n toArgs: (input) => [\n input.prompt,\n { ...input.options, keyName: input.keyName },\n ],\n }),\n defineAgentActionNode({\n method: 'aiScroll',\n description:\n 'Scroll the page or a located region with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiScrollInputSchema,\n toArgs: (input) => [input.prompt, { ...input.options }],\n }),\n defineAgentActionNode({\n method: 'aiPinch',\n description:\n 'Perform a pinch-in or pinch-out gesture with a Midscene UI Agent.',\n inputSchema: aiPinchInputSchema,\n toArgs: (input) => [\n input.prompt,\n { ...input.options, direction: input.direction },\n ],\n }),\n defineAgentActionNode({\n method: 'aiLongPress',\n description: 'Locate and long-press an element with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiLongPressInputSchema,\n toArgs: (input) => [input.prompt, input.options],\n }),\n defineAgentActionNode({\n method: 'callActionInActionSpace',\n name: 'aiDragAndDrop',\n description:\n 'Locate source and destination elements, then drag the source to the destination.',\n inputSchema: aiDragAndDropInputSchema,\n toArgs: (input) => [\n 'DragAndDrop',\n {\n from: buildDetailedLocateParam(input.from, input.options),\n to: buildDetailedLocateParam(input.to, input.options),\n },\n ],\n }),\n defineAgentActionNode({\n method: 'aiLocate',\n description:\n 'Locate an element from a natural-language description and store the result.',\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'aiQuery',\n description:\n 'Extract structured data from the current interface and store the result.',\n stringInputKey: 'prompt',\n inputSchema: aiQueryInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'aiWaitFor',\n description: 'Wait until a natural-language condition is satisfied.',\n stringInputKey: 'prompt',\n inputSchema: aiWaitForInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n }),\n defineAgentActionNode({\n method: 'evaluateJavaScript',\n name: 'javascript',\n description:\n 'Evaluate JavaScript in the current interface and store the result.',\n stringInputKey: 'script',\n inputSchema: javascriptInputSchema,\n toArgs: (input) => [input.script],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'runGherkinScenario',\n description:\n 'Execute a Gherkin scenario with the current Midscene UI Agent.',\n stringInputKey: 'scenario',\n inputSchema: runGherkinScenarioInputSchema,\n toArgs: (input, context) => [\n input.scenario,\n { ...input.options, abortSignal: context.signal },\n ],\n }),\n defineAgentActionNode({\n method: 'callActionInActionSpace',\n name: 'action',\n description:\n 'Call a platform or custom action. Its ActionSpace schema validates params when the action executes.',\n inputSchema: actionInputSchema,\n toArgs: (input) => [input.name, input.params],\n toResult: (value) => (value === undefined ? undefined : valueResult(value)),\n }),\n];\n\nconst insightNode = (method: 'aiBoolean' | 'aiNumber' | 'aiString' | 'aiAsk') =>\n defineCommonAgentNode({\n method,\n description: `Run ${method} with a Midscene UI Agent and store its value.`,\n stringInputKey: 'prompt',\n inputSchema: insightInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: (value) => ({\n summary: `${method} returned ${\n method === 'aiString' || method === 'aiAsk'\n ? JSON.stringify(value)\n : value\n }`,\n data: { value },\n }),\n });\n\nconst recordToReportNode = defineCommonAgentNode({\n method: 'recordToReport',\n description: 'Add text or screenshots to the current Midscene report.',\n stringInputKey: 'title',\n inputSchema: recordToReportInputSchema,\n toArgs(input) {\n return [input.title, input.options];\n },\n toResult(_output, input) {\n return { summary: `Recorded to report: ${input.title ?? 'untitled'}` };\n },\n});\n\nexport const commonAgentTestRunnerNodeDefinitions: readonly AgentTestRunnerNodeDefinition[] =\n [\n defineAgentActionNode({\n method: 'sleep',\n description:\n 'Wait for a fixed number of milliseconds, recording standard UI snapshots and honoring cancellation.',\n inputSchema: z.strictObject({ ms: z.number().positive() }),\n toArgs(input, { signal }) {\n signal.throwIfAborted();\n return [input.ms, { abortSignal: signal }];\n },\n }),\n aiActNode,\n aiTapNode,\n aiAssertNode,\n insightNode('aiBoolean'),\n insightNode('aiNumber'),\n insightNode('aiString'),\n insightNode('aiAsk'),\n recordToReportNode,\n ...additionalAgentNodes,\n ];\n"],"names":["createAgentTestRunnerNodeDefinition","agentLabel","definition","undefined","agent","input","context","TypeError","typedAgent","method","typedInput","result","nonBlankText","description","z","promptImageInputSchema","structuredUserPromptInputSchema","userPromptInputSchema","aiActOptionsInputSchema","setAIContextInputSchema","AGENT_AI_CONTEXT_KEYS","aiActInputSchema","insightOptionsInputSchema","aiAssertOptionsInputSchema","aiAssertInputSchema","locateOptionsInputSchema","aiTapInputSchema","aiInputInputSchema","inputStrategies","aiKeyboardPressInputSchema","aiScrollInputSchema","aiPinchInputSchema","aiLongPressInputSchema","aiDragAndDropInputSchema","aiQueryInputSchema","aiWaitForOptionsInputSchema","aiWaitForInputSchema","javascriptInputSchema","runGherkinScenarioInputSchema","actionInputSchema","insightInputSchema","reportScreenshotInputSchema","recordToReportOptionsInputSchema","recordToReportInputSchema","defineCommonAgentNode","defineAgentActionNode","promptText","prompt","valueResult","value","aiActNode","output","aiTapNode","_output","aiAssertNode","NodeExecutionError","Error","locateActionNode","additionalAgentNodes","buildDetailedLocateParam","insightNode","JSON","recordToReportNode","commonAgentTestRunnerNodeDefinitions","signal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent/test-runner-nodes.mjs","sources":["../../../src/agent/test-runner-nodes.ts"],"sourcesContent":["import { AGENT_AI_CONTEXT_KEYS } from '@midscene/shared/agent-tools/agent-context';\nimport { z } from 'zod/v4';\nimport type { TUserPrompt } from '../common';\nimport { inputStrategies } from '../device/input-strategy';\nimport { NodeExecutionError } from '../test-runner/errors';\nimport type { LocateOption, ScrollParam } from '../yaml';\nimport { buildDetailedLocateParam } from '../yaml/utils';\nimport type { Agent } from './agent';\n\nexport interface AgentTestRunnerNodeResult<TData = unknown> {\n summary?: string;\n data?: TData;\n}\n\nexport interface AgentTestRunnerNodeExecutionContext {\n signal: AbortSignal;\n}\n\n/** Agent-owned Node description consumed by Test Runner adapters. */\nexport interface AgentTestRunnerNodeDefinition<\n TInput extends Record<string, unknown> = Record<string, unknown>,\n TData = unknown,\n> {\n name: string;\n title?: string;\n description?: string;\n /** Node input field populated by YAML string shorthand; omit to disable. */\n stringInputKey?: string | false;\n inputSchema: z.ZodObject;\n execute(\n agent: unknown,\n input: TInput,\n context: AgentTestRunnerNodeExecutionContext,\n ):\n | Promise<AgentTestRunnerNodeResult<TData> | undefined>\n | AgentTestRunnerNodeResult<TData>\n | undefined;\n}\n\n/** Static capability implemented by Agent classes that expose Runner Nodes. */\nexport interface AgentTestRunnerNodeProvider {\n getTestRunnerNodeDefinitions(): readonly AgentTestRunnerNodeDefinition[];\n}\n\ntype AnyAgentMethod = (...args: any[]) => unknown;\ntype AgentMethodName<TAgent extends object> = {\n [TKey in keyof TAgent]-?: TAgent[TKey] extends AnyAgentMethod ? TKey : never;\n}[keyof TAgent] &\n string;\ntype AgentMethod<\n TAgent extends object,\n TMethod extends AgentMethodName<TAgent>,\n> = Extract<TAgent[TMethod], AnyAgentMethod>;\n\nexport interface DefineAgentTestRunnerNodeOptions<\n TAgent extends object,\n TMethod extends AgentMethodName<TAgent>,\n TSchema extends z.ZodObject,\n TData = unknown,\n> {\n method: TMethod;\n name?: string;\n title?: string;\n description?: string;\n stringInputKey?: (keyof z.output<TSchema> & string) | false;\n inputSchema: TSchema;\n toArgs(\n input: z.output<TSchema>,\n context: AgentTestRunnerNodeExecutionContext,\n ): Parameters<AgentMethod<TAgent, TMethod>>;\n toResult?(\n result: Awaited<ReturnType<AgentMethod<TAgent, TMethod>>>,\n input: z.output<TSchema>,\n ): AgentTestRunnerNodeResult<TData> | undefined;\n}\n\n/** Create a Node factory whose adapters are checked against an Agent API. */\nexport const createAgentTestRunnerNodeDefinition =\n <TAgent extends object>(agentLabel = 'an Agent') =>\n <\n TMethod extends AgentMethodName<TAgent>,\n TSchema extends z.ZodObject,\n TData = unknown,\n >(\n definition: DefineAgentTestRunnerNodeOptions<\n TAgent,\n TMethod,\n TSchema,\n TData\n >,\n ): AgentTestRunnerNodeDefinition => ({\n name: definition.name ?? definition.method,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(agent, input, context) {\n if (\n typeof agent !== 'object' ||\n agent === null ||\n typeof (agent as Record<string, unknown>)[definition.method] !==\n 'function'\n ) {\n throw new TypeError(\n `getAgent() must return ${agentLabel} with ${definition.method}().`,\n );\n }\n\n const typedAgent = agent as TAgent;\n const method = typedAgent[definition.method] as AgentMethod<\n TAgent,\n TMethod\n >;\n const typedInput = input as z.output<TSchema>;\n const result = (await method.apply(\n typedAgent,\n definition.toArgs(typedInput, context),\n )) as Awaited<ReturnType<AgentMethod<TAgent, TMethod>>>;\n return definition.toResult?.(result, typedInput);\n },\n });\n\nconst nonBlankText = (description: string) =>\n z\n .string()\n .regex(/\\S/, 'value must contain a non-whitespace character')\n .describe(description);\n\nexport const promptImageInputSchema = z.strictObject({\n name: nonBlankText('The name used to identify this reference image.'),\n url: nonBlankText('The URL, data URL, or file path of this reference image.'),\n});\n\nexport const structuredUserPromptInputSchema = z.strictObject({\n prompt: nonBlankText('The natural-language prompt.'),\n images: z.array(promptImageInputSchema).min(1).optional(),\n convertHttpImage2Base64: z\n .boolean()\n .optional()\n .describe('Whether HTTP reference images are converted to base64 first.'),\n});\n\nexport const userPromptInputSchema = z.union([\n nonBlankText('The natural-language prompt.'),\n structuredUserPromptInputSchema,\n]);\n\nexport const aiActOptionsInputSchema = z.strictObject({\n effort: z\n .enum(['fast', 'balance', 'deepThink'])\n .optional()\n .describe('Action planning effort: fast, balanced, or deeper reasoning.'),\n cacheable: z\n .boolean()\n .optional()\n .describe('Whether this action may use the Midscene cache.'),\n fileChooserAccept: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe('Accepted file types for a file chooser.'),\n fileChooserAllowedDir: z\n .string()\n .optional()\n .describe('Directory allowed for prompt-driven file uploads.'),\n deepThink: z\n .union([z.literal('unset'), z.boolean()])\n .optional()\n .describe('Whether to enable deep thinking for this action.'),\n deepLocate: z\n .boolean()\n .optional()\n .describe('Whether to use deep element location.'),\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n});\n\nexport const setAIContextInputSchema = z.strictObject({\n target: z\n .enum(AGENT_AI_CONTEXT_KEYS)\n .describe(\n 'The AI API to configure, e.g. aiAct or aiAssert. Use default as the shared fallback for all AI APIs when neither the call nor the API has its own context.',\n ),\n context: z\n .string()\n .optional()\n .describe(\n 'Agent-level guidance. Omit to remove the selected context; an empty string disables inherited context.',\n ),\n});\n\nexport const aiActInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: aiActOptionsInputSchema.optional(),\n});\n\nexport const insightOptionsInputSchema = z.strictObject({\n domIncluded: z\n .union([z.boolean(), z.literal('visible-only')])\n .optional()\n .describe('How DOM information is included.'),\n screenshotIncluded: z\n .boolean()\n .optional()\n .describe('Whether the request includes a screenshot.'),\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n});\n\nexport const aiAssertOptionsInputSchema = insightOptionsInputSchema.extend({\n keepRawResponse: z\n .boolean()\n .optional()\n .describe('Whether the Agent returns the structured assertion result.'),\n});\n\nexport const aiAssertInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n message: z.string().optional().describe('The assertion failure message.'),\n options: aiAssertOptionsInputSchema.optional(),\n});\n\nexport const locateOptionsInputSchema = z.strictObject({\n context: z\n .string()\n .optional()\n .describe(\n 'Additional facts, rules, constraints, or output requirements for this AI call. Overrides inherited aiContexts; an empty string disables inherited user context.',\n ),\n deepLocate: z\n .boolean()\n .optional()\n .describe('Whether to use deep element location.'),\n deepThink: z\n .boolean()\n .optional()\n .describe('Deprecated alias for deepLocate.'),\n cacheable: z\n .boolean()\n .optional()\n .describe('Whether this location may use the Midscene cache.'),\n xpath: z.string().optional().describe('A web XPath location hint.'),\n fileChooserAccept: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe('Files accepted when tapping opens a file chooser.'),\n});\n\nexport const aiTapInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiInputInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n value: z.union([z.string(), z.number()]),\n options: locateOptionsInputSchema\n .extend({\n mode: z.enum(['replace', 'clear', 'typeOnly', 'append']).optional(),\n autoDismissKeyboard: z.boolean().optional(),\n keyboardTypeDelay: z.number().nonnegative().optional(),\n inputStrategy: z.enum(inputStrategies).optional(),\n })\n .optional(),\n});\n\nexport const aiKeyboardPressInputSchema = z.strictObject({\n prompt: userPromptInputSchema.optional(),\n keyName: nonBlankText('The key or key combination to press.'),\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiScrollInputSchema = z.strictObject({\n prompt: userPromptInputSchema\n .optional()\n .describe(\n 'Optional locator for a currently visible scrollable container, such as \"the product list\". Omit for page scrolling. Describe the container, not a scrolling instruction or an off-screen target to find. Set direction, distance, and scrollType in options.',\n ),\n options: locateOptionsInputSchema\n .extend({\n direction: z.enum(['down', 'up', 'right', 'left']).optional(),\n scrollType: z\n .enum([\n 'singleAction',\n 'scrollToBottom',\n 'scrollToTop',\n 'scrollToRight',\n 'scrollToLeft',\n 'once',\n 'untilBottom',\n 'untilTop',\n 'untilRight',\n 'untilLeft',\n ])\n .optional(),\n distance: z.number().nullable().optional(),\n })\n .optional(),\n});\n\nexport const aiPinchInputSchema = z.strictObject({\n prompt: userPromptInputSchema.optional(),\n direction: z.enum(['in', 'out']),\n options: locateOptionsInputSchema\n .extend({\n distance: z.number().optional(),\n duration: z.number().nonnegative().optional(),\n })\n .optional(),\n});\n\nexport const aiLongPressInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: locateOptionsInputSchema\n .extend({ duration: z.number().nonnegative().optional() })\n .optional(),\n});\n\nexport const aiDragAndDropInputSchema = z.strictObject({\n from: userPromptInputSchema,\n to: userPromptInputSchema,\n options: locateOptionsInputSchema.optional(),\n});\n\nexport const aiQueryInputSchema = z.strictObject({\n prompt: z.union([\n nonBlankText('The extraction request.'),\n z.record(z.string(), z.string()),\n ]),\n options: insightOptionsInputSchema.optional(),\n});\n\nexport const aiWaitForOptionsInputSchema = insightOptionsInputSchema\n .extend({\n timeoutMs: z.number().positive().optional(),\n checkIntervalMs: z.number().positive().optional(),\n })\n .superRefine((input, context) => {\n if ((input.checkIntervalMs ?? 3000) > (input.timeoutMs ?? 15000)) {\n context.addIssue({\n code: 'custom',\n path: ['checkIntervalMs'],\n message: 'checkIntervalMs must not exceed timeoutMs',\n });\n }\n });\n\nexport const aiWaitForInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: aiWaitForOptionsInputSchema.optional(),\n});\n\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\n\nexport const javascriptInputSchema = z.strictObject({\n script: nonBlankText('JavaScript to evaluate through the current interface.'),\n});\n\nexport const runGherkinScenarioInputSchema = z.strictObject({\n scenario: nonBlankText('The Gherkin scenario to execute.'),\n options: aiActOptionsInputSchema.optional(),\n});\n\nexport const actionInputSchema = z.strictObject({\n name: nonBlankText('The ActionSpace action name, for example DragAndDrop.'),\n params: z.unknown().optional(),\n});\n\nexport const insightInputSchema = z.strictObject({\n prompt: userPromptInputSchema,\n options: insightOptionsInputSchema.optional(),\n});\n\nexport const reportScreenshotInputSchema = z.strictObject({\n base64: z.string().min(1).describe('A base64-encoded screenshot.'),\n description: z.string().optional().describe('What the screenshot shows.'),\n});\n\nexport const recordToReportOptionsInputSchema = z\n .strictObject({\n content: z.string().optional().describe('The report text content.'),\n screenshotBase64: z\n .string()\n .optional()\n .describe('One base64-encoded screenshot.'),\n screenshots: z\n .array(reportScreenshotInputSchema)\n .min(1)\n .optional()\n .describe('Screenshots attached to the report section.'),\n })\n .superRefine((input, context) => {\n if (\n input.screenshotBase64 !== undefined &&\n input.screenshots !== undefined\n ) {\n context.addIssue({\n code: 'custom',\n message: 'screenshotBase64 and screenshots are mutually exclusive',\n });\n }\n });\n\nexport const recordToReportInputSchema = z.strictObject({\n title: z.string().optional().describe('The report section title.'),\n options: recordToReportOptionsInputSchema.optional(),\n});\n\nexport type UserPromptNodeInput = z.infer<typeof userPromptInputSchema>;\nexport type SetAIContextNodeInput = z.infer<typeof setAIContextInputSchema>;\nexport type AiActNodeOptions = z.infer<typeof aiActOptionsInputSchema>;\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeOptions = z.infer<typeof aiAssertOptionsInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiTapNodeOptions = z.infer<typeof locateOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type AiInputNodeInput = z.infer<typeof aiInputInputSchema>;\nexport type AiKeyboardPressNodeInput = z.infer<\n typeof aiKeyboardPressInputSchema\n>;\nexport type AiScrollNodeInput = z.infer<typeof aiScrollInputSchema>;\nexport type AiPinchNodeInput = z.infer<typeof aiPinchInputSchema>;\nexport type AiLongPressNodeInput = z.infer<typeof aiLongPressInputSchema>;\nexport type AiDragAndDropNodeInput = z.infer<typeof aiDragAndDropInputSchema>;\nexport type AiQueryNodeInput = z.infer<typeof aiQueryInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type JavascriptNodeInput = z.infer<typeof javascriptInputSchema>;\nexport type RunGherkinScenarioNodeInput = z.infer<\n typeof runGherkinScenarioInputSchema\n>;\nexport type ActionNodeInput = z.infer<typeof actionInputSchema>;\nexport type InsightNodeOptions = z.infer<typeof insightOptionsInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeOptions = z.infer<\n typeof recordToReportOptionsInputSchema\n>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\n\nexport type CommonAgentTestRunnerApi = Pick<\n Agent,\n | 'aiAct'\n | 'aiTap'\n | 'aiAssert'\n | 'aiBoolean'\n | 'aiNumber'\n | 'aiString'\n | 'aiAsk'\n | 'recordToReport'\n>;\n\nconst defineCommonAgentNode =\n createAgentTestRunnerNodeDefinition<CommonAgentTestRunnerApi>('an Agent');\n\n// Select the current overloads without expanding the minimum API required by\n// existing getAgent() providers. Each Node checks its own method at execution.\ntype AgentActionNodeApi = Pick<\n Agent,\n | 'sleep'\n | 'setAIContext'\n | 'aiHover'\n | 'aiDoubleClick'\n | 'aiRightClick'\n | 'aiMiddleClick'\n | 'aiLongPress'\n | 'aiClearInput'\n | 'aiPinch'\n | 'aiLocate'\n | 'aiQuery'\n | 'aiWaitFor'\n | 'evaluateJavaScript'\n | 'runGherkinScenario'\n | 'callActionInActionSpace'\n> & {\n aiInput(\n prompt: TUserPrompt,\n options: NonNullable<Parameters<Agent['aiInput']>[2]> & {\n value: string | number;\n },\n ): Promise<void>;\n aiKeyboardPress(\n prompt: TUserPrompt | undefined,\n options: LocateOption & { keyName: string },\n ): Promise<void>;\n aiScroll(\n prompt: TUserPrompt | undefined,\n options: LocateOption & ScrollParam,\n ): Promise<void>;\n};\n\nconst defineAgentActionNode =\n createAgentTestRunnerNodeDefinition<AgentActionNodeApi>('an Agent');\n\nconst promptText = (prompt: UserPromptNodeInput): string =>\n typeof prompt === 'string' ? prompt : prompt.prompt;\n\nconst valueResult = (\n value: unknown,\n): AgentTestRunnerNodeResult | undefined => ({ data: value });\n\nconst aiActNode = defineCommonAgentNode({\n method: 'aiAct',\n description: 'Perform a natural-language task with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiActInputSchema,\n toArgs(input, context) {\n return [\n input.prompt,\n {\n ...input.options,\n abortSignal: context.signal,\n },\n ];\n },\n toResult(output) {\n return output === undefined ? undefined : { summary: output };\n },\n});\n\nconst aiTapNode = defineCommonAgentNode({\n method: 'aiTap',\n description: 'Locate and tap an element with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs(input) {\n return [input.prompt, input.options];\n },\n toResult(_output, input) {\n return { summary: `Tapped: ${promptText(input.prompt)}` };\n },\n});\n\nconst aiAssertNode = defineCommonAgentNode({\n method: 'aiAssert',\n description: 'Assert a natural-language condition with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiAssertInputSchema,\n toArgs(input, context) {\n return [\n input.prompt,\n input.message,\n {\n keepRawResponse: true,\n ...input.options,\n abortSignal: context.signal,\n },\n ];\n },\n toResult(output, input) {\n const result = {\n summary: `Assertion ${output?.pass === false ? 'failed' : 'passed'}: ${promptText(input.prompt)}`,\n ...(output === undefined\n ? {}\n : {\n data: {\n pass: output.pass,\n ...(output.thought === undefined\n ? {}\n : { thought: output.thought }),\n ...(output.message === undefined\n ? {}\n : { message: output.message }),\n },\n }),\n };\n if (output?.pass === false)\n throw new NodeExecutionError(\n 'aiAssert',\n new Error(\n output.message ||\n input.message ||\n output.thought ||\n `Assertion failed: ${promptText(input.prompt)}`,\n ),\n result,\n );\n return result;\n },\n});\n\nconst locateActionNode = (\n method:\n | 'aiHover'\n | 'aiDoubleClick'\n | 'aiRightClick'\n | 'aiMiddleClick'\n | 'aiClearInput',\n) =>\n defineAgentActionNode({\n method,\n description: {\n aiHover: 'Locate and hover over an element with a Midscene UI Agent.',\n aiDoubleClick:\n 'Locate and double-click an element with a Midscene UI Agent.',\n aiMiddleClick:\n 'Locate and middle-click an element with a Midscene UI Agent.',\n aiRightClick:\n 'Locate and right-click an element with a Midscene UI Agent.',\n aiClearInput:\n 'Locate an input element and clear its value with a Midscene UI Agent.',\n }[method],\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs: (input) => [input.prompt, input.options],\n toResult: (_output, input) => ({\n summary: `${method}: ${promptText(input.prompt)}`,\n }),\n });\n\nconst additionalAgentNodes: readonly AgentTestRunnerNodeDefinition[] = [\n defineAgentActionNode({\n method: 'setAIContext',\n description:\n 'Set or remove Agent-level AI guidance for subsequent calls on the same Agent.',\n inputSchema: setAIContextInputSchema,\n toArgs: (input) => [input.target, input.context],\n }),\n locateActionNode('aiHover'),\n locateActionNode('aiDoubleClick'),\n locateActionNode('aiRightClick'),\n locateActionNode('aiMiddleClick'),\n locateActionNode('aiClearInput'),\n defineAgentActionNode({\n method: 'aiInput',\n description:\n 'Locate an input element and enter a value with a Midscene UI Agent.',\n inputSchema: aiInputInputSchema,\n toArgs: (input) => [input.prompt, { ...input.options, value: input.value }],\n }),\n defineAgentActionNode({\n method: 'aiKeyboardPress',\n description:\n 'Press a key or key combination, optionally targeting a located element.',\n stringInputKey: 'keyName',\n inputSchema: aiKeyboardPressInputSchema,\n toArgs: (input) => [\n input.prompt,\n { ...input.options, keyName: input.keyName },\n ],\n }),\n defineAgentActionNode({\n method: 'aiScroll',\n description:\n 'Scroll the page or a located region with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiScrollInputSchema,\n toArgs: (input) => [input.prompt, { ...input.options }],\n }),\n defineAgentActionNode({\n method: 'aiPinch',\n description:\n 'Perform a pinch-in or pinch-out gesture with a Midscene UI Agent.',\n inputSchema: aiPinchInputSchema,\n toArgs: (input) => [\n input.prompt,\n { ...input.options, direction: input.direction },\n ],\n }),\n defineAgentActionNode({\n method: 'aiLongPress',\n description: 'Locate and long-press an element with a Midscene UI Agent.',\n stringInputKey: 'prompt',\n inputSchema: aiLongPressInputSchema,\n toArgs: (input) => [input.prompt, input.options],\n }),\n defineAgentActionNode({\n method: 'callActionInActionSpace',\n name: 'aiDragAndDrop',\n description:\n 'Locate source and destination elements, then drag the source to the destination.',\n inputSchema: aiDragAndDropInputSchema,\n toArgs: (input) => [\n 'DragAndDrop',\n {\n from: buildDetailedLocateParam(input.from, input.options),\n to: buildDetailedLocateParam(input.to, input.options),\n },\n ],\n }),\n defineAgentActionNode({\n method: 'aiLocate',\n description:\n 'Locate an element from a natural-language description and store the result.',\n stringInputKey: 'prompt',\n inputSchema: aiTapInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'aiQuery',\n description:\n 'Extract structured data from the current interface and store the result.',\n stringInputKey: 'prompt',\n inputSchema: aiQueryInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'aiWaitFor',\n description: 'Wait until a natural-language condition is satisfied.',\n stringInputKey: 'prompt',\n inputSchema: aiWaitForInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n }),\n defineAgentActionNode({\n method: 'evaluateJavaScript',\n name: 'javascript',\n description:\n 'Evaluate JavaScript in the current interface and store the result.',\n stringInputKey: 'script',\n inputSchema: javascriptInputSchema,\n toArgs: (input) => [input.script],\n toResult: valueResult,\n }),\n defineAgentActionNode({\n method: 'runGherkinScenario',\n description:\n 'Execute a Gherkin scenario with the current Midscene UI Agent.',\n stringInputKey: 'scenario',\n inputSchema: runGherkinScenarioInputSchema,\n toArgs: (input, context) => [\n input.scenario,\n { ...input.options, abortSignal: context.signal },\n ],\n }),\n defineAgentActionNode({\n method: 'callActionInActionSpace',\n name: 'action',\n description:\n 'Call a platform or custom action. Its ActionSpace schema validates params when the action executes.',\n inputSchema: actionInputSchema,\n toArgs: (input) => [input.name, input.params],\n toResult: (value) => (value === undefined ? undefined : valueResult(value)),\n }),\n];\n\nconst insightNode = (method: 'aiBoolean' | 'aiNumber' | 'aiString' | 'aiAsk') =>\n defineCommonAgentNode({\n method,\n description: `Run ${method} with a Midscene UI Agent and store its value.`,\n stringInputKey: 'prompt',\n inputSchema: insightInputSchema,\n toArgs: (input, context) => [\n input.prompt,\n { ...input.options, abortSignal: context.signal },\n ],\n toResult: (value) => ({\n summary: `${method} returned ${\n method === 'aiString' || method === 'aiAsk'\n ? JSON.stringify(value)\n : value\n }`,\n data: { value },\n }),\n });\n\nconst recordToReportNode = defineCommonAgentNode({\n method: 'recordToReport',\n description: 'Add text or screenshots to the current Midscene report.',\n stringInputKey: 'title',\n inputSchema: recordToReportInputSchema,\n toArgs(input) {\n return [input.title, input.options];\n },\n toResult(_output, input) {\n return { summary: `Recorded to report: ${input.title ?? 'untitled'}` };\n },\n});\n\nexport const commonAgentTestRunnerNodeDefinitions: readonly AgentTestRunnerNodeDefinition[] =\n [\n defineAgentActionNode({\n method: 'sleep',\n description:\n 'Wait for a fixed number of milliseconds, recording standard UI snapshots and honoring cancellation.',\n inputSchema: z.strictObject({ ms: z.number().positive() }),\n toArgs(input, { signal }) {\n signal.throwIfAborted();\n return [input.ms, { abortSignal: signal }];\n },\n }),\n aiActNode,\n aiTapNode,\n aiAssertNode,\n insightNode('aiBoolean'),\n insightNode('aiNumber'),\n insightNode('aiString'),\n insightNode('aiAsk'),\n recordToReportNode,\n ...additionalAgentNodes,\n ];\n"],"names":["createAgentTestRunnerNodeDefinition","agentLabel","definition","undefined","agent","input","context","TypeError","typedAgent","method","typedInput","result","nonBlankText","description","z","promptImageInputSchema","structuredUserPromptInputSchema","userPromptInputSchema","aiActOptionsInputSchema","setAIContextInputSchema","AGENT_AI_CONTEXT_KEYS","aiActInputSchema","insightOptionsInputSchema","aiAssertOptionsInputSchema","aiAssertInputSchema","locateOptionsInputSchema","aiTapInputSchema","aiInputInputSchema","inputStrategies","aiKeyboardPressInputSchema","aiScrollInputSchema","aiPinchInputSchema","aiLongPressInputSchema","aiDragAndDropInputSchema","aiQueryInputSchema","aiWaitForOptionsInputSchema","aiWaitForInputSchema","javascriptInputSchema","runGherkinScenarioInputSchema","actionInputSchema","insightInputSchema","reportScreenshotInputSchema","recordToReportOptionsInputSchema","recordToReportInputSchema","defineCommonAgentNode","defineAgentActionNode","promptText","prompt","valueResult","value","aiActNode","output","aiTapNode","_output","aiAssertNode","NodeExecutionError","Error","locateActionNode","additionalAgentNodes","buildDetailedLocateParam","insightNode","JSON","recordToReportNode","commonAgentTestRunnerNodeDefinitions","signal"],"mappings":"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"}
|
package/dist/es/agent/utils.mjs
CHANGED
|
@@ -153,7 +153,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
const getMidsceneVersion = ()=>"1.17.
|
|
156
|
+
const getMidsceneVersion = ()=>"1.17.8-beta-20261106101231.0";
|
|
157
157
|
const parsePrompt = (prompt)=>{
|
|
158
158
|
if ('string' == typeof prompt) return {
|
|
159
159
|
textPrompt: prompt,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const parts = [
|
|
2
|
+
'instructions',
|
|
3
|
+
'screenshot',
|
|
4
|
+
'task',
|
|
5
|
+
'references'
|
|
6
|
+
];
|
|
7
|
+
function parseLocatePromptOrder(order = 'system(instructions):userTogether(screenshot:task):userEach(references)') {
|
|
8
|
+
const groups = order.split(/(?<=\)):/);
|
|
9
|
+
const remaining = new Set(parts);
|
|
10
|
+
function invalid(reason) {
|
|
11
|
+
return new Error(`Invalid locate promptOrder "${order}": ${reason}`);
|
|
12
|
+
}
|
|
13
|
+
const promptGroups = groups.map((group)=>{
|
|
14
|
+
const match = /^(system|userTogether|userEach)\(([^()]+)\)$/.exec(group);
|
|
15
|
+
if (!match) throw invalid(`expected system(...), userTogether(...) or userEach(...), got "${group}"`);
|
|
16
|
+
const groupParts = match[2].split(':');
|
|
17
|
+
for (const part of groupParts){
|
|
18
|
+
if (!remaining.delete(part)) throw invalid(`unknown or repeated part "${part}"`);
|
|
19
|
+
if ('system' === match[1] && ('screenshot' === part || 'references' === part)) throw invalid(`system cannot contain image part "${part}"`);
|
|
20
|
+
}
|
|
21
|
+
if ('system' === match[1]) return {
|
|
22
|
+
mode: 'system',
|
|
23
|
+
parts: groupParts
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
mode: match[1],
|
|
27
|
+
parts: groupParts
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
if (remaining.size) throw invalid(`missing ${[
|
|
31
|
+
...remaining
|
|
32
|
+
].join(', ')}`);
|
|
33
|
+
return {
|
|
34
|
+
groups: promptGroups
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function buildLocateMessages(order, input) {
|
|
38
|
+
const messages = [];
|
|
39
|
+
for (const group of order.groups){
|
|
40
|
+
if ('system' === group.mode) {
|
|
41
|
+
messages.push({
|
|
42
|
+
role: 'system',
|
|
43
|
+
content: group.parts.map((part)=>input[part]).join('\n\n')
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const expanded = group.parts.flatMap((part)=>{
|
|
48
|
+
if ('references' === part) return input.references;
|
|
49
|
+
const content = 'screenshot' === part ? [
|
|
50
|
+
{
|
|
51
|
+
type: 'image',
|
|
52
|
+
url: input.screenshot
|
|
53
|
+
}
|
|
54
|
+
] : input[part];
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
role: 'user',
|
|
58
|
+
content
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
});
|
|
62
|
+
if ('userEach' === group.mode) messages.push(...expanded);
|
|
63
|
+
else if (expanded.length) {
|
|
64
|
+
const content = expanded.flatMap((message)=>'string' == typeof message.content ? [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: message.content
|
|
68
|
+
}
|
|
69
|
+
] : message.content);
|
|
70
|
+
messages.push({
|
|
71
|
+
role: 'user',
|
|
72
|
+
content
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return messages;
|
|
77
|
+
}
|
|
78
|
+
export { buildLocateMessages, parseLocatePromptOrder };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=locate-prompt-order.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/model-adapter/locate-prompt-order.mjs","sources":["../../../../src/ai-model/model-adapter/locate-prompt-order.ts"],"sourcesContent":["import type {\n ConversationSystemMessage,\n ConversationUserMessage,\n MessageContent,\n} from '../service-caller/types';\n\nconst parts = ['instructions', 'screenshot', 'task', 'references'] as const;\ntype Part = (typeof parts)[number];\n\nexport type LocateMessages = (\n | ConversationSystemMessage\n | ConversationUserMessage\n)[];\n\ntype TextPart = Extract<Part, 'instructions' | 'task'>;\ntype PromptGroup =\n | { mode: 'system'; parts: TextPart[] }\n | { mode: 'userTogether' | 'userEach'; parts: Part[] };\n\nexport interface LocatePromptOrder {\n groups: readonly PromptGroup[];\n}\n\nexport function parseLocatePromptOrder(\n order = 'system(instructions):userTogether(screenshot:task):userEach(references)',\n): LocatePromptOrder {\n const groups = order.split(/(?<=\\)):/);\n const remaining = new Set<string>(parts);\n\n function invalid(reason: string): Error {\n return new Error(`Invalid locate promptOrder \"${order}\": ${reason}`);\n }\n\n const promptGroups = groups.map((group): PromptGroup => {\n const match = /^(system|userTogether|userEach)\\(([^()]+)\\)$/.exec(group);\n if (!match) {\n throw invalid(\n `expected system(...), userTogether(...) or userEach(...), got \"${group}\"`,\n );\n }\n const groupParts = match[2].split(':');\n for (const part of groupParts) {\n if (!remaining.delete(part)) {\n throw invalid(`unknown or repeated part \"${part}\"`);\n }\n if (\n match[1] === 'system' &&\n (part === 'screenshot' || part === 'references')\n ) {\n throw invalid(`system cannot contain image part \"${part}\"`);\n }\n }\n if (match[1] === 'system') {\n return { mode: 'system', parts: groupParts as TextPart[] };\n }\n return {\n mode: match[1] as 'userTogether' | 'userEach',\n parts: groupParts as Part[],\n };\n });\n if (remaining.size) {\n throw invalid(`missing ${[...remaining].join(', ')}`);\n }\n return { groups: promptGroups };\n}\n\nexport function buildLocateMessages(\n order: LocatePromptOrder,\n input: {\n instructions: string;\n screenshot: string;\n task: string;\n references: ConversationUserMessage[];\n },\n): LocateMessages {\n const messages: LocateMessages = [];\n for (const group of order.groups) {\n if (group.mode === 'system') {\n messages.push({\n role: 'system',\n content: group.parts.map((part) => input[part]).join('\\n\\n'),\n });\n continue;\n }\n const expanded = group.parts.flatMap((part): ConversationUserMessage[] => {\n if (part === 'references') {\n return input.references;\n }\n const content =\n part === 'screenshot'\n ? [{ type: 'image' as const, url: input.screenshot }]\n : input[part];\n return [{ role: 'user', content }];\n });\n if (group.mode === 'userEach') {\n messages.push(...expanded);\n } else if (expanded.length) {\n const content = expanded.flatMap((message): MessageContent[] =>\n typeof message.content === 'string'\n ? [{ type: 'text', text: message.content }]\n : message.content,\n );\n messages.push({ role: 'user', content });\n }\n }\n return messages;\n}\n"],"names":["parts","parseLocatePromptOrder","order","groups","remaining","Set","invalid","reason","Error","promptGroups","group","match","groupParts","part","buildLocateMessages","input","messages","expanded","content","message"],"mappings":"AAMA,MAAMA,QAAQ;IAAC;IAAgB;IAAc;IAAQ;CAAa;AAiB3D,SAASC,uBACdC,QAAQ,yEAAyE;IAEjF,MAAMC,SAASD,MAAM,KAAK,CAAC;IAC3B,MAAME,YAAY,IAAIC,IAAYL;IAElC,SAASM,QAAQC,MAAc;QAC7B,OAAO,IAAIC,MAAM,CAAC,4BAA4B,EAAEN,MAAM,GAAG,EAAEK,QAAQ;IACrE;IAEA,MAAME,eAAeN,OAAO,GAAG,CAAC,CAACO;QAC/B,MAAMC,QAAQ,+CAA+C,IAAI,CAACD;QAClE,IAAI,CAACC,OACH,MAAML,QACJ,CAAC,+DAA+D,EAAEI,MAAM,CAAC,CAAC;QAG9E,MAAME,aAAaD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;QAClC,KAAK,MAAME,QAAQD,WAAY;YAC7B,IAAI,CAACR,UAAU,MAAM,CAACS,OACpB,MAAMP,QAAQ,CAAC,0BAA0B,EAAEO,KAAK,CAAC,CAAC;YAEpD,IACEF,AAAa,aAAbA,KAAK,CAAC,EAAE,IACPE,CAAAA,AAAS,iBAATA,QAAyBA,AAAS,iBAATA,IAAoB,GAE9C,MAAMP,QAAQ,CAAC,kCAAkC,EAAEO,KAAK,CAAC,CAAC;QAE9D;QACA,IAAIF,AAAa,aAAbA,KAAK,CAAC,EAAE,EACV,OAAO;YAAE,MAAM;YAAU,OAAOC;QAAyB;QAE3D,OAAO;YACL,MAAMD,KAAK,CAAC,EAAE;YACd,OAAOC;QACT;IACF;IACA,IAAIR,UAAU,IAAI,EAChB,MAAME,QAAQ,CAAC,QAAQ,EAAE;WAAIF;KAAU,CAAC,IAAI,CAAC,OAAO;IAEtD,OAAO;QAAE,QAAQK;IAAa;AAChC;AAEO,SAASK,oBACdZ,KAAwB,EACxBa,KAKC;IAED,MAAMC,WAA2B,EAAE;IACnC,KAAK,MAAMN,SAASR,MAAM,MAAM,CAAE;QAChC,IAAIQ,AAAe,aAAfA,MAAM,IAAI,EAAe;YAC3BM,SAAS,IAAI,CAAC;gBACZ,MAAM;gBACN,SAASN,MAAM,KAAK,CAAC,GAAG,CAAC,CAACG,OAASE,KAAK,CAACF,KAAK,EAAE,IAAI,CAAC;YACvD;YACA;QACF;QACA,MAAMI,WAAWP,MAAM,KAAK,CAAC,OAAO,CAAC,CAACG;YACpC,IAAIA,AAAS,iBAATA,MACF,OAAOE,MAAM,UAAU;YAEzB,MAAMG,UACJL,AAAS,iBAATA,OACI;gBAAC;oBAAE,MAAM;oBAAkB,KAAKE,MAAM,UAAU;gBAAC;aAAE,GACnDA,KAAK,CAACF,KAAK;YACjB,OAAO;gBAAC;oBAAE,MAAM;oBAAQK;gBAAQ;aAAE;QACpC;QACA,IAAIR,AAAe,eAAfA,MAAM,IAAI,EACZM,SAAS,IAAI,IAAIC;aACZ,IAAIA,SAAS,MAAM,EAAE;YAC1B,MAAMC,UAAUD,SAAS,OAAO,CAAC,CAACE,UAChC,AAA2B,YAA3B,OAAOA,QAAQ,OAAO,GAClB;oBAAC;wBAAE,MAAM;wBAAQ,MAAMA,QAAQ,OAAO;oBAAC;iBAAE,GACzCA,QAAQ,OAAO;YAErBH,SAAS,IAAI,CAAC;gBAAE,MAAM;gBAAQE;YAAQ;QACxC;IACF;IACA,OAAOF;AACT"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLocateResultCodec } from "../shared/model-locate-result/factory.mjs";
|
|
2
2
|
import { resolvePlanningTapLocator } from "../workflows/grounding/planning-action-locate.mjs";
|
|
3
3
|
import { createDefaultElementProtocol, createDefaultSearchAreaProtocol } from "./default-locate-protocol.mjs";
|
|
4
|
+
import { parseLocatePromptOrder } from "./locate-prompt-order.mjs";
|
|
4
5
|
const defaultLocateResultFormatDefinition = {
|
|
5
6
|
coordinates: {
|
|
6
7
|
shape: 'bbox',
|
|
@@ -31,7 +32,7 @@ function resolveLocate(locate, resolvedCustomPlanner, protocolContext) {
|
|
|
31
32
|
const searchAreaResultCodec = searchAreaResultFormat ? createLocateResultCodec(searchAreaResultFormat) : void 0;
|
|
32
33
|
return {
|
|
33
34
|
kind: 'standard',
|
|
34
|
-
|
|
35
|
+
promptOrder: parseLocatePromptOrder(locate?.promptOrder),
|
|
35
36
|
element: {
|
|
36
37
|
protocol: elementProtocol,
|
|
37
38
|
resultCodec: elementResultCodec
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/model-adapter/locate.mjs","sources":["../../../../src/ai-model/model-adapter/locate.ts"],"sourcesContent":["import { createLocateResultCodec } from '../shared/model-locate-result/factory';\nimport type { LocateResultFormatDefinition } from '../shared/model-locate-result/types';\nimport { resolvePlanningTapLocator } from '../workflows/grounding/planning-action-locate';\nimport type { ResolvedCustomPlanningDefinition } from './custom-planning-types';\nimport {\n createDefaultElementProtocol,\n createDefaultSearchAreaProtocol,\n} from './default-locate-protocol';\nimport type {\n StandardLocateProtocolContext,\n StandardLocateProtocolDefinition,\n} from './locate-protocol';\nimport type { LocateAdapter, ModelAdapterDefinition } from './types';\n\nconst defaultLocateResultFormatDefinition: LocateResultFormatDefinition = {\n coordinates: { shape: 'bbox', order: 'xy', normalizedBy: 1000 },\n};\n\nconst resolveLocateProtocolDefinition = (\n protocolDefinition: StandardLocateProtocolDefinition,\n protocolContext: StandardLocateProtocolContext,\n) =>\n typeof protocolDefinition === 'function'\n ? protocolDefinition(protocolContext)\n : protocolDefinition;\n\nexport function resolveLocate(\n locate: ModelAdapterDefinition['locate'],\n resolvedCustomPlanner: ResolvedCustomPlanningDefinition | undefined,\n protocolContext: StandardLocateProtocolContext,\n): LocateAdapter {\n if (locate?.kind === 'custom') {\n let locateFn = locate.locateFn;\n\n if (!locateFn) {\n const planningTapLocator = locate.planningTapLocator;\n\n if (!planningTapLocator) {\n throw new Error(\n 'Custom locate definition requires either locateFn or planningTapLocator',\n );\n }\n\n if (!resolvedCustomPlanner) {\n throw new Error(\n 'Custom planning tap locator requires a custom planning planner definition',\n );\n }\n locateFn = resolvePlanningTapLocator(\n planningTapLocator,\n resolvedCustomPlanner,\n );\n }\n\n return {\n kind: 'custom',\n locateFn,\n };\n }\n\n const elementProtocol = resolveLocateProtocolDefinition(\n locate?.element?.protocol ?? createDefaultElementProtocol,\n protocolContext,\n );\n const elementResultFormat =\n locate?.element?.resultFormat ?? defaultLocateResultFormatDefinition;\n const elementResultCodec = createLocateResultCodec(elementResultFormat);\n const searchAreaProtocol =\n locate?.searchArea === false\n ? undefined\n : resolveLocateProtocolDefinition(\n locate?.searchArea?.protocol ?? createDefaultSearchAreaProtocol,\n protocolContext,\n );\n const searchAreaResultFormat =\n locate?.searchArea === false\n ? undefined\n : (locate?.searchArea?.resultFormat ?? elementResultFormat);\n const searchAreaResultCodec = searchAreaResultFormat\n ? createLocateResultCodec(searchAreaResultFormat)\n : undefined;\n\n return {\n kind: 'standard',\n
|
|
1
|
+
{"version":3,"file":"ai-model/model-adapter/locate.mjs","sources":["../../../../src/ai-model/model-adapter/locate.ts"],"sourcesContent":["import { createLocateResultCodec } from '../shared/model-locate-result/factory';\nimport type { LocateResultFormatDefinition } from '../shared/model-locate-result/types';\nimport { resolvePlanningTapLocator } from '../workflows/grounding/planning-action-locate';\nimport type { ResolvedCustomPlanningDefinition } from './custom-planning-types';\nimport {\n createDefaultElementProtocol,\n createDefaultSearchAreaProtocol,\n} from './default-locate-protocol';\nimport { parseLocatePromptOrder } from './locate-prompt-order';\nimport type {\n StandardLocateProtocolContext,\n StandardLocateProtocolDefinition,\n} from './locate-protocol';\nimport type { LocateAdapter, ModelAdapterDefinition } from './types';\n\nconst defaultLocateResultFormatDefinition: LocateResultFormatDefinition = {\n coordinates: { shape: 'bbox', order: 'xy', normalizedBy: 1000 },\n};\n\nconst resolveLocateProtocolDefinition = (\n protocolDefinition: StandardLocateProtocolDefinition,\n protocolContext: StandardLocateProtocolContext,\n) =>\n typeof protocolDefinition === 'function'\n ? protocolDefinition(protocolContext)\n : protocolDefinition;\n\nexport function resolveLocate(\n locate: ModelAdapterDefinition['locate'],\n resolvedCustomPlanner: ResolvedCustomPlanningDefinition | undefined,\n protocolContext: StandardLocateProtocolContext,\n): LocateAdapter {\n if (locate?.kind === 'custom') {\n let locateFn = locate.locateFn;\n\n if (!locateFn) {\n const planningTapLocator = locate.planningTapLocator;\n\n if (!planningTapLocator) {\n throw new Error(\n 'Custom locate definition requires either locateFn or planningTapLocator',\n );\n }\n\n if (!resolvedCustomPlanner) {\n throw new Error(\n 'Custom planning tap locator requires a custom planning planner definition',\n );\n }\n locateFn = resolvePlanningTapLocator(\n planningTapLocator,\n resolvedCustomPlanner,\n );\n }\n\n return {\n kind: 'custom',\n locateFn,\n };\n }\n\n const elementProtocol = resolveLocateProtocolDefinition(\n locate?.element?.protocol ?? createDefaultElementProtocol,\n protocolContext,\n );\n const elementResultFormat =\n locate?.element?.resultFormat ?? defaultLocateResultFormatDefinition;\n const elementResultCodec = createLocateResultCodec(elementResultFormat);\n const searchAreaProtocol =\n locate?.searchArea === false\n ? undefined\n : resolveLocateProtocolDefinition(\n locate?.searchArea?.protocol ?? createDefaultSearchAreaProtocol,\n protocolContext,\n );\n const searchAreaResultFormat =\n locate?.searchArea === false\n ? undefined\n : (locate?.searchArea?.resultFormat ?? elementResultFormat);\n const searchAreaResultCodec = searchAreaResultFormat\n ? createLocateResultCodec(searchAreaResultFormat)\n : undefined;\n\n return {\n kind: 'standard',\n promptOrder: parseLocatePromptOrder(locate?.promptOrder),\n element: {\n protocol: elementProtocol,\n resultCodec: elementResultCodec,\n },\n ...(searchAreaProtocol && searchAreaResultCodec\n ? {\n searchArea: {\n protocol: searchAreaProtocol,\n resultCodec: searchAreaResultCodec,\n },\n }\n : {}),\n };\n}\n"],"names":["defaultLocateResultFormatDefinition","resolveLocateProtocolDefinition","protocolDefinition","protocolContext","resolveLocate","locate","resolvedCustomPlanner","locateFn","planningTapLocator","Error","resolvePlanningTapLocator","elementProtocol","createDefaultElementProtocol","elementResultFormat","elementResultCodec","createLocateResultCodec","searchAreaProtocol","undefined","createDefaultSearchAreaProtocol","searchAreaResultFormat","searchAreaResultCodec","parseLocatePromptOrder"],"mappings":";;;;AAeA,MAAMA,sCAAoE;IACxE,aAAa;QAAE,OAAO;QAAQ,OAAO;QAAM,cAAc;IAAK;AAChE;AAEA,MAAMC,kCAAkC,CACtCC,oBACAC,kBAEA,AAA8B,cAA9B,OAAOD,qBACHA,mBAAmBC,mBACnBD;AAEC,SAASE,cACdC,MAAwC,EACxCC,qBAAmE,EACnEH,eAA8C;IAE9C,IAAIE,QAAQ,SAAS,UAAU;QAC7B,IAAIE,WAAWF,OAAO,QAAQ;QAE9B,IAAI,CAACE,UAAU;YACb,MAAMC,qBAAqBH,OAAO,kBAAkB;YAEpD,IAAI,CAACG,oBACH,MAAM,IAAIC,MACR;YAIJ,IAAI,CAACH,uBACH,MAAM,IAAIG,MACR;YAGJF,WAAWG,0BACTF,oBACAF;QAEJ;QAEA,OAAO;YACL,MAAM;YACNC;QACF;IACF;IAEA,MAAMI,kBAAkBV,gCACtBI,QAAQ,SAAS,YAAYO,8BAC7BT;IAEF,MAAMU,sBACJR,QAAQ,SAAS,gBAAgBL;IACnC,MAAMc,qBAAqBC,wBAAwBF;IACnD,MAAMG,qBACJX,QAAQ,eAAe,QACnBY,SACAhB,gCACEI,QAAQ,YAAY,YAAYa,iCAChCf;IAER,MAAMgB,yBACJd,QAAQ,eAAe,QACnBY,SACCZ,QAAQ,YAAY,gBAAgBQ;IAC3C,MAAMO,wBAAwBD,yBAC1BJ,wBAAwBI,0BACxBF;IAEJ,OAAO;QACL,MAAM;QACN,aAAaI,uBAAuBhB,QAAQ;QAC5C,SAAS;YACP,UAAUM;YACV,aAAaG;QACf;QACA,GAAIE,sBAAsBI,wBACtB;YACE,YAAY;gBACV,UAAUJ;gBACV,aAAaI;YACf;QACF,IACA,CAAC,CAAC;IACR;AACF"}
|
|
@@ -84,7 +84,7 @@ async function genericLocate(locateRequest) {
|
|
|
84
84
|
locateImage: locateRequest.locateImage,
|
|
85
85
|
imagePreprocess: adapter.imagePreprocess,
|
|
86
86
|
targetDescription: targetElementDescription,
|
|
87
|
-
|
|
87
|
+
promptOrder: adapter.locate.promptOrder
|
|
88
88
|
});
|
|
89
89
|
try {
|
|
90
90
|
return await callAiAndParseWithRetry({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/workflows/grounding/locate.mjs","sources":["../../../../../src/ai-model/workflows/grounding/locate.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { TUserPrompt } from '../../../common';\nimport { userPromptToString } from '../../../common';\nimport { buildElementLocateSystemPrompt } from '../../prompt/locate';\nimport {\n AIResponseParseError,\n callGenerativeAI,\n} from '../../service-caller/index';\nimport {\n callAiAndParseWithRetry,\n withSemanticRetryFeedback,\n} from '../../service-caller/semantic-retry';\nimport { mapSearchAreaResultToOriginalResult } from './search-area-mapping';\nimport type {\n LocateModelResponse,\n LocateOptions,\n LocateRequest,\n LocateResult,\n} from './types';\nimport { formatLocateModelContext, prepareLocateModelInput } from './utils';\n\nconst debugGrounding = getDebug('ai:grounding');\n\nexport { userPromptToString as extraTextFromUserPrompt } from '../../../common';\n\nexport async function AiLocateElement(\n options: LocateOptions & { targetElementDescription: TUserPrompt },\n): Promise<LocateResult> {\n const { targetElementDescription, ...locateOptions } = options;\n assert(\n targetElementDescription,\n 'cannot find the target element description',\n );\n\n const { context } = locateOptions;\n const locateImage = locateOptions.searchConfig?.image ?? {\n imageBase64: context.screenshot.base64,\n width: context.shotSize.width,\n height: context.shotSize.height,\n };\n const locateRequest: LocateRequest = {\n targetElementDescription,\n locateImage,\n options: locateOptions,\n };\n\n const locateAdapter = options.modelRuntime.adapter.locate;\n const locateFn =\n locateAdapter.kind === 'custom' ? locateAdapter.locateFn : genericLocate;\n const locateResponse = await locateFn(locateRequest);\n const {\n locatedPixelResult,\n rawResponse,\n rawAssistantOutput,\n usage,\n reasoningContent,\n errors = [],\n } = locateResponse;\n const baseLocateResult = {\n rawResponse,\n rawAssistantOutput,\n usage,\n reasoning_content: reasoningContent,\n };\n\n if (!locatedPixelResult) {\n return {\n parseResult: {\n element: undefined,\n errors,\n },\n ...baseLocateResult,\n };\n }\n\n try {\n const mappedResult = mapSearchAreaResultToOriginalResult(\n locatedPixelResult,\n locateOptions.searchConfig?.mapping,\n );\n debugGrounding('center', mappedResult.center);\n\n return {\n parseResult: {\n element: {\n ...mappedResult,\n description: userPromptToString(targetElementDescription),\n },\n errors: [],\n },\n ...baseLocateResult,\n };\n } catch (error) {\n const msg =\n error instanceof Error\n ? `Failed to parse locate result: ${error.message}`\n : 'unknown error in locate';\n return {\n parseResult: {\n element: undefined,\n errors: errors.length > 0 ? [...errors, `(${msg})`] : [msg],\n },\n ...baseLocateResult,\n };\n }\n}\n\nexport async function genericLocate(\n locateRequest: LocateRequest,\n): Promise<LocateModelResponse> {\n const { options, targetElementDescription } = locateRequest;\n const modelRuntime = options.modelRuntime;\n const { adapter } = modelRuntime;\n assert(\n adapter.locate.kind === 'standard',\n 'generic locate requires a standard locate adapter',\n );\n const { protocol, resultCodec } = adapter.locate.element;\n\n const systemPrompt = buildElementLocateSystemPrompt({\n systemPromptIntroduction: protocol.systemPromptIntroduction,\n responseInstructions: protocol.buildResponseInstructions(\n resultCodec.promptSpec,\n ),\n });\n\n const userInstructionPrompt = protocol.buildUserPrompt(\n userPromptToString(targetElementDescription),\n );\n\n const { messages, preparedImage } = await prepareLocateModelInput({\n systemPrompt,\n userPrompt: userInstructionPrompt,\n locateImage: locateRequest.locateImage,\n imagePreprocess: adapter.imagePreprocess,\n targetDescription: targetElementDescription,\n userMessageContentOrder: adapter.locate.userMessageContentOrder,\n });\n\n try {\n return await callAiAndParseWithRetry({\n callAi: (retryAttempt, previousParseError) =>\n callGenerativeAI(\n withSemanticRetryFeedback(messages, previousParseError),\n modelRuntime,\n {\n abortSignal: options.abortSignal,\n expectedJsonObjectResponse: protocol.expectedJsonObjectResponse,\n semanticRetryAttempt: retryAttempt,\n },\n ),\n parseResponse: (response): LocateModelResponse => {\n const parsedLocateResult = protocol.parseRawResponse(\n response.content,\n resultCodec.promptSpec,\n );\n const rawResponse = response.content;\n const locateError = parsedLocateResult.error;\n if (parsedLocateResult.kind === 'not-found') {\n return {\n rawResponse,\n rawAssistantOutput: response.rawAssistantOutput,\n usage: response.usage,\n reasoningContent: response.reasoning_content,\n errors: locateError ? [locateError] : [],\n };\n }\n\n try {\n const result = resultCodec.toPixelResult(parsedLocateResult.target, {\n preparedSize: preparedImage.preparedSize,\n contentSize: preparedImage.contentSize,\n });\n return {\n locatedPixelResult: result,\n rawResponse,\n rawAssistantOutput: response.rawAssistantOutput,\n usage: response.usage,\n reasoningContent: response.reasoning_content,\n errors: [],\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const message = [\n locateError && `error in locate result: ${locateError}`,\n `coordinate parsing error: ${errorMessage}`,\n formatLocateModelContext(modelRuntime),\n ]\n .filter(Boolean)\n .join('\\n');\n throw new Error(message, { cause: error });\n }\n },\n toParseError: (error, response) => {\n const parseErrorMessage =\n error instanceof Error\n ? `Failed to parse locate result: ${error.message}`\n : 'unknown error in locate result';\n return new AIResponseParseError(\n parseErrorMessage,\n response.content,\n response.usage,\n response.rawAssistantOutput,\n response.reasoning_content,\n );\n },\n parseRetryTimes: modelRuntime.config.retryCount,\n parseRetryInterval: modelRuntime.config.retryInterval,\n abortSignal: options.abortSignal,\n onParseRetry: (error) => {\n debugGrounding(\n 'retrying locate after coordinate parsing failed: %s',\n error instanceof Error ? error.message : String(error),\n );\n },\n });\n } catch (callError) {\n if (callError instanceof AIResponseParseError) {\n return {\n rawResponse: callError.rawResponse,\n rawAssistantOutput: callError.rawAssistantOutput,\n usage: callError.usage,\n reasoningContent: callError.reasoningContent,\n errors: [callError.message],\n };\n }\n\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n return {\n rawResponse: errorMessage,\n errors: [`AI call error: ${errorMessage}`],\n };\n }\n}\n"],"names":["debugGrounding","getDebug","AiLocateElement","options","targetElementDescription","locateOptions","assert","context","locateImage","locateRequest","locateAdapter","locateFn","genericLocate","locateResponse","locatedPixelResult","rawResponse","rawAssistantOutput","usage","reasoningContent","errors","baseLocateResult","undefined","mappedResult","mapSearchAreaResultToOriginalResult","userPromptToString","error","msg","Error","modelRuntime","adapter","protocol","resultCodec","systemPrompt","buildElementLocateSystemPrompt","userInstructionPrompt","messages","preparedImage","prepareLocateModelInput","callAiAndParseWithRetry","retryAttempt","previousParseError","callGenerativeAI","withSemanticRetryFeedback","response","parsedLocateResult","locateError","result","errorMessage","String","message","formatLocateModelContext","Boolean","parseErrorMessage","AIResponseParseError","callError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/workflows/grounding/locate.mjs","sources":["../../../../../src/ai-model/workflows/grounding/locate.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { TUserPrompt } from '../../../common';\nimport { userPromptToString } from '../../../common';\nimport { buildElementLocateSystemPrompt } from '../../prompt/locate';\nimport {\n AIResponseParseError,\n callGenerativeAI,\n} from '../../service-caller/index';\nimport {\n callAiAndParseWithRetry,\n withSemanticRetryFeedback,\n} from '../../service-caller/semantic-retry';\nimport { mapSearchAreaResultToOriginalResult } from './search-area-mapping';\nimport type {\n LocateModelResponse,\n LocateOptions,\n LocateRequest,\n LocateResult,\n} from './types';\nimport { formatLocateModelContext, prepareLocateModelInput } from './utils';\n\nconst debugGrounding = getDebug('ai:grounding');\n\nexport { userPromptToString as extraTextFromUserPrompt } from '../../../common';\n\nexport async function AiLocateElement(\n options: LocateOptions & { targetElementDescription: TUserPrompt },\n): Promise<LocateResult> {\n const { targetElementDescription, ...locateOptions } = options;\n assert(\n targetElementDescription,\n 'cannot find the target element description',\n );\n\n const { context } = locateOptions;\n const locateImage = locateOptions.searchConfig?.image ?? {\n imageBase64: context.screenshot.base64,\n width: context.shotSize.width,\n height: context.shotSize.height,\n };\n const locateRequest: LocateRequest = {\n targetElementDescription,\n locateImage,\n options: locateOptions,\n };\n\n const locateAdapter = options.modelRuntime.adapter.locate;\n const locateFn =\n locateAdapter.kind === 'custom' ? locateAdapter.locateFn : genericLocate;\n const locateResponse = await locateFn(locateRequest);\n const {\n locatedPixelResult,\n rawResponse,\n rawAssistantOutput,\n usage,\n reasoningContent,\n errors = [],\n } = locateResponse;\n const baseLocateResult = {\n rawResponse,\n rawAssistantOutput,\n usage,\n reasoning_content: reasoningContent,\n };\n\n if (!locatedPixelResult) {\n return {\n parseResult: {\n element: undefined,\n errors,\n },\n ...baseLocateResult,\n };\n }\n\n try {\n const mappedResult = mapSearchAreaResultToOriginalResult(\n locatedPixelResult,\n locateOptions.searchConfig?.mapping,\n );\n debugGrounding('center', mappedResult.center);\n\n return {\n parseResult: {\n element: {\n ...mappedResult,\n description: userPromptToString(targetElementDescription),\n },\n errors: [],\n },\n ...baseLocateResult,\n };\n } catch (error) {\n const msg =\n error instanceof Error\n ? `Failed to parse locate result: ${error.message}`\n : 'unknown error in locate';\n return {\n parseResult: {\n element: undefined,\n errors: errors.length > 0 ? [...errors, `(${msg})`] : [msg],\n },\n ...baseLocateResult,\n };\n }\n}\n\nexport async function genericLocate(\n locateRequest: LocateRequest,\n): Promise<LocateModelResponse> {\n const { options, targetElementDescription } = locateRequest;\n const modelRuntime = options.modelRuntime;\n const { adapter } = modelRuntime;\n assert(\n adapter.locate.kind === 'standard',\n 'generic locate requires a standard locate adapter',\n );\n const { protocol, resultCodec } = adapter.locate.element;\n\n const systemPrompt = buildElementLocateSystemPrompt({\n systemPromptIntroduction: protocol.systemPromptIntroduction,\n responseInstructions: protocol.buildResponseInstructions(\n resultCodec.promptSpec,\n ),\n });\n\n const userInstructionPrompt = protocol.buildUserPrompt(\n userPromptToString(targetElementDescription),\n );\n\n const { messages, preparedImage } = await prepareLocateModelInput({\n systemPrompt,\n userPrompt: userInstructionPrompt,\n locateImage: locateRequest.locateImage,\n imagePreprocess: adapter.imagePreprocess,\n targetDescription: targetElementDescription,\n promptOrder: adapter.locate.promptOrder,\n });\n\n try {\n return await callAiAndParseWithRetry({\n callAi: (retryAttempt, previousParseError) =>\n callGenerativeAI(\n withSemanticRetryFeedback(messages, previousParseError),\n modelRuntime,\n {\n abortSignal: options.abortSignal,\n expectedJsonObjectResponse: protocol.expectedJsonObjectResponse,\n semanticRetryAttempt: retryAttempt,\n },\n ),\n parseResponse: (response): LocateModelResponse => {\n const parsedLocateResult = protocol.parseRawResponse(\n response.content,\n resultCodec.promptSpec,\n );\n const rawResponse = response.content;\n const locateError = parsedLocateResult.error;\n if (parsedLocateResult.kind === 'not-found') {\n return {\n rawResponse,\n rawAssistantOutput: response.rawAssistantOutput,\n usage: response.usage,\n reasoningContent: response.reasoning_content,\n errors: locateError ? [locateError] : [],\n };\n }\n\n try {\n const result = resultCodec.toPixelResult(parsedLocateResult.target, {\n preparedSize: preparedImage.preparedSize,\n contentSize: preparedImage.contentSize,\n });\n return {\n locatedPixelResult: result,\n rawResponse,\n rawAssistantOutput: response.rawAssistantOutput,\n usage: response.usage,\n reasoningContent: response.reasoning_content,\n errors: [],\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const message = [\n locateError && `error in locate result: ${locateError}`,\n `coordinate parsing error: ${errorMessage}`,\n formatLocateModelContext(modelRuntime),\n ]\n .filter(Boolean)\n .join('\\n');\n throw new Error(message, { cause: error });\n }\n },\n toParseError: (error, response) => {\n const parseErrorMessage =\n error instanceof Error\n ? `Failed to parse locate result: ${error.message}`\n : 'unknown error in locate result';\n return new AIResponseParseError(\n parseErrorMessage,\n response.content,\n response.usage,\n response.rawAssistantOutput,\n response.reasoning_content,\n );\n },\n parseRetryTimes: modelRuntime.config.retryCount,\n parseRetryInterval: modelRuntime.config.retryInterval,\n abortSignal: options.abortSignal,\n onParseRetry: (error) => {\n debugGrounding(\n 'retrying locate after coordinate parsing failed: %s',\n error instanceof Error ? error.message : String(error),\n );\n },\n });\n } catch (callError) {\n if (callError instanceof AIResponseParseError) {\n return {\n rawResponse: callError.rawResponse,\n rawAssistantOutput: callError.rawAssistantOutput,\n usage: callError.usage,\n reasoningContent: callError.reasoningContent,\n errors: [callError.message],\n };\n }\n\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n return {\n rawResponse: errorMessage,\n errors: [`AI call error: ${errorMessage}`],\n };\n }\n}\n"],"names":["debugGrounding","getDebug","AiLocateElement","options","targetElementDescription","locateOptions","assert","context","locateImage","locateRequest","locateAdapter","locateFn","genericLocate","locateResponse","locatedPixelResult","rawResponse","rawAssistantOutput","usage","reasoningContent","errors","baseLocateResult","undefined","mappedResult","mapSearchAreaResultToOriginalResult","userPromptToString","error","msg","Error","modelRuntime","adapter","protocol","resultCodec","systemPrompt","buildElementLocateSystemPrompt","userInstructionPrompt","messages","preparedImage","prepareLocateModelInput","callAiAndParseWithRetry","retryAttempt","previousParseError","callGenerativeAI","withSemanticRetryFeedback","response","parsedLocateResult","locateError","result","errorMessage","String","message","formatLocateModelContext","Boolean","parseErrorMessage","AIResponseParseError","callError"],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ async function AiLocateSection(options) {
|
|
|
31
31
|
},
|
|
32
32
|
imagePreprocess: adapter.imagePreprocess,
|
|
33
33
|
targetDescription: sectionDescription,
|
|
34
|
-
|
|
34
|
+
promptOrder: adapter.locate.promptOrder
|
|
35
35
|
});
|
|
36
36
|
let parsedResult;
|
|
37
37
|
try {
|