@midscene/core 1.14.0 → 1.15.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/action-readiness.mjs +72 -0
- package/dist/es/agent/action-readiness.mjs.map +1 -0
- package/dist/es/agent/agent.mjs +14 -6
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/task-builder.mjs +45 -33
- package/dist/es/agent/task-builder.mjs.map +1 -1
- package/dist/es/agent/tasks.mjs +9 -2
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/connectivity/run-connectivity-test.mjs +24 -6
- package/dist/es/ai-model/connectivity/run-connectivity-test.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs +10 -3
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs +3 -2
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs +10 -6
- package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/responses/stream.mjs +8 -1
- package/dist/es/ai-model/service-caller/openai/responses/stream.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/utils.mjs +3 -2
- package/dist/es/ai-model/service-caller/utils.mjs.map +1 -1
- package/dist/es/device/index.mjs +11 -4
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/es/yaml/legacy-yaml-runtime.mjs +6 -0
- package/dist/es/yaml/legacy-yaml-runtime.mjs.map +1 -1
- package/dist/es/yaml/player.mjs +7 -3
- package/dist/es/yaml/player.mjs.map +1 -1
- package/dist/lib/agent/action-readiness.js +112 -0
- package/dist/lib/agent/action-readiness.js.map +1 -0
- package/dist/lib/agent/agent.js +13 -5
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/task-builder.js +45 -33
- package/dist/lib/agent/task-builder.js.map +1 -1
- package/dist/lib/agent/tasks.js +9 -2
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/connectivity/run-connectivity-test.js +24 -6
- package/dist/lib/ai-model/connectivity/run-connectivity-test.js.map +1 -1
- package/dist/lib/ai-model/service-caller/call-ai.js +10 -3
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/call-openai.js +3 -2
- package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js +10 -6
- package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/responses/stream.js +8 -1
- package/dist/lib/ai-model/service-caller/openai/responses/stream.js.map +1 -1
- package/dist/lib/ai-model/service-caller/utils.js +3 -2
- package/dist/lib/ai-model/service-caller/utils.js.map +1 -1
- package/dist/lib/device/index.js +11 -4
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/lib/yaml/legacy-yaml-runtime.js +6 -0
- package/dist/lib/yaml/legacy-yaml-runtime.js.map +1 -1
- package/dist/lib/yaml/player.js +7 -3
- package/dist/lib/yaml/player.js.map +1 -1
- package/dist/types/agent/action-readiness.d.ts +14 -0
- package/dist/types/agent/action-readiness.d.ts.map +1 -0
- package/dist/types/agent/agent.d.ts +6 -2
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/agent/index.d.ts +1 -1
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/task-builder.d.ts +4 -1
- package/dist/types/agent/task-builder.d.ts.map +1 -1
- package/dist/types/agent/tasks.d.ts +3 -0
- package/dist/types/agent/tasks.d.ts.map +1 -1
- package/dist/types/ai-model/connectivity/index.d.ts +1 -1
- package/dist/types/ai-model/connectivity/index.d.ts.map +1 -1
- package/dist/types/ai-model/connectivity/run-connectivity-test.d.ts +14 -1
- package/dist/types/ai-model/connectivity/run-connectivity-test.d.ts.map +1 -1
- package/dist/types/ai-model/index.d.ts +1 -1
- package/dist/types/ai-model/index.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/types.d.ts +2 -0
- package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/call-ai.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/call-openai.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/chat-completion/stream.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/responses/stream.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/utils.d.ts +3 -2
- package/dist/types/ai-model/service-caller/utils.d.ts.map +1 -1
- package/dist/types/device/index.d.ts +7 -1
- package/dist/types/device/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +28 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/yaml/legacy-yaml-runtime.d.ts.map +1 -1
- package/dist/types/yaml/player.d.ts +2 -1
- package/dist/types/yaml/player.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/es/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { AgentAIContexts } from '@midscene/shared/agent-tools/agent-context';\nimport type { SerializedError } from '@midscene/shared/agent-tools/error-formatter';\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type OpenAI from 'openai';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport type { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport type { SerializedError } from '@midscene/shared/agent-tools/error-formatter';\nexport type {\n AgentAIContextKey,\n AgentAIContexts,\n AiApiName,\n} from '@midscene/shared/agent-tools/agent-context';\nexport * from './yaml';\n\nexport { ServiceError } from './errors';\nexport {\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n} from './dump/report-action-dump';\n\nexport type RawAssistantOutput =\n | {\n type: 'chat-completion';\n rawValue: OpenAI.Chat.Completions.ChatCompletionMessage;\n }\n | {\n type: 'responses';\n rawValue: OpenAI.Responses.ResponseOutputItem[];\n };\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n /** Duration of the final successful request attempt, in milliseconds. */\n time_cost: number | undefined;\n /** Request duration including all attempts and retry waits, excluding preparation, in milliseconds. */\n total_time_cost?: number;\n /** Request retries performed within this call; excludes semantic parse retries. */\n retry_count?: number;\n /** Actual protocol used for the model call, resolved by Midscene. */\n api_type?: 'chat-completion' | 'responses' | 'codex';\n model_name: string | undefined;\n model_description: string | undefined;\n /**\n * Raw top-level `.model` value returned by the model service response.\n */\n response_model_name: string | undefined;\n /**\n * Semantic intent of the model call, such as default, planning, or insight.\n */\n intent: string | undefined;\n /**\n * Config slot where the model config was resolved from. For example, a\n * planning call may use the default slot when no planning model is configured.\n */\n slot: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport type LocateResultPoint = [number, number];\nexport type Bbox = [number, number, number, number];\nexport type LocateResultBbox = Bbox;\nexport type PixelBbox = Bbox;\n\nexport interface AIElementLocateResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n error?: string;\n}\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n references_bbox?: LocateResultBbox[];\n references_point?: LocateResultPoint[];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect?: Rect;\n center: [number, number];\n centerDistance?: number;\n /** Whether the expected point is inside the located rect; absent for point-only results. */\n includedInRect?: boolean;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepLocate: boolean;\n deepDescribe: boolean;\n verifyResult?: LocateValidatorResult;\n success: boolean;\n error?: string;\n failureStage?: 'describe' | 'verify';\n}\n\n/**\n * context\n */\n\nexport interface UiNode {\n type: string;\n attrs: Record<string, string | undefined>;\n bounds: Rect;\n children: UiNode[];\n}\n\nexport interface UITreeSnapshot {\n platform: 'android';\n capturedAt: number;\n root: UiNode;\n}\n\nexport abstract class UIContext {\n /**\n * screenshot of the current UI state. which size is shotSize(be shrunk by screenshotShrinkFactor),\n */\n abstract screenshot: ScreenshotItem;\n\n /**\n * Optional sequence of screenshots captured over a short time window, in\n * temporal order (earliest first, latest last). When present with more than\n * one frame, extract/assert flows submit all frames to the model so it can\n * observe transient UI (toasts, carousels, auto-hiding controls). The last\n * frame is the same state as {@link screenshot}.\n */\n abstract screenshotSequence?: ScreenshotItem[];\n\n /**\n * screenshot size after shrinking\n */\n abstract shotSize: Size;\n\n /**\n * The ratio for converting shrunk screenshot coordinates to logical coordinates.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - User-defined screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - shrunkShotToLogicalRatio: dpr / screenshotShrinkFactor = 6 / 2 = 3\n * - To map back to logical coordinates: 1500 / shrunkShotToLogicalRatio = 500px\n */\n abstract shrunkShotToLogicalRatio: number;\n\n abstract _isFrozen?: boolean;\n\n // @deprecated - backward compatibility for aiLocate\n abstract deprecatedDpr?: number;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type AiActEffort = 'fast' | 'balance' | 'deepThink';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: unknown;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: unknown;\n rawAssistantOutput?: RawAssistantOutput;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n /**\n * Adapter-extracted content from the search-area model call. This is not the\n * full provider response or choices[0].message.\n */\n searchAreaRawResponse?: string;\n searchAreaRawChoiceMessage?: unknown;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport type ReportAttributes = Record<\n string,\n string | number | boolean | null | undefined\n>;\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: ReportAttributes;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement?: LocateResultElement[];\n deepLocate?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n /**\n * Additional facts, decision rules, constraints, or output requirements for\n * this assertion. It overrides `aiContexts.aiAssert` and\n * `aiContexts.default`;\n * `''` disables inherited user context for this call.\n */\n context?: string;\n abortSignal?: AbortSignal;\n}\n\nexport interface AgentAssertResult {\n pass: boolean;\n thought?: string;\n message?: string;\n}\n\nexport type QueryOptions = ServiceExtractOption;\nexport type AssertOptions = AgentAssertOpt & ServiceExtractOption;\n\n/** Options for fixed observations, which never read the live DOM. */\nexport type ObservationQueryOptions = Omit<QueryOptions, 'domIncluded'> & {\n domIncluded?: never;\n};\n\n/** Assertion options for fixed observations, which never read the live DOM. */\nexport type ObservationAssertOptions = Omit<AssertOptions, 'domIncluded'> & {\n domIncluded?: never;\n};\n\n/** Read-only AI operations supported by both live Agents and UI observations. */\nexport interface InsightAPI<\n QueryOpt extends QueryOptions = QueryOptions,\n AssertOpt extends AssertOptions = AssertOptions,\n> {\n aiQuery<ReturnType = any>(\n demand: ServiceExtractParam,\n options?: QueryOpt,\n ): Promise<ReturnType>;\n aiBoolean(prompt: TUserPrompt, options?: QueryOpt): Promise<boolean>;\n aiNumber(prompt: TUserPrompt, options?: QueryOpt): Promise<number>;\n aiString(prompt: TUserPrompt, options?: QueryOpt): Promise<string>;\n aiAsk(prompt: TUserPrompt, options?: QueryOpt): Promise<string>;\n aiAssert(\n assertion: TUserPrompt,\n message?: string,\n options?: AssertOpt,\n ): Promise<AgentAssertResult | undefined>;\n}\n\n/**\n * planning\n *\n */\n\ntype PlanningActionBase = {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n};\n\nexport type PlanningAction<ParamType = undefined> = PlanningActionBase &\n ([ParamType] extends [undefined] ? { param?: any } : { param: ParamType });\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction | null;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawAssistantOutput?: RawAssistantOutput;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\n/**\n * Generic agent progress bus.\n *\n * Progress notifications are a single, generic stream the agent broadcasts as\n * it works. Each event is a thin envelope: a `scope` naming the producer, a\n * `phase` within that producer's lifecycle, a monotonic `sequence`, and a\n * structured, presentation-free `data` payload. The bus knows nothing about any\n * particular producer's payload - consumers narrow by `scope`. `aiAct` is the\n * first producer (\"pilot\") on this bus; others (queries, waits, ...) can be\n * added without touching the bus, the listener API, or the renderer core.\n */\nexport interface AgentProgressEvent<\n TScope extends string = string,\n TData = unknown,\n TPhase extends string = string,\n> {\n scope: TScope;\n phase: TPhase;\n sequence: number;\n data: TData;\n}\n\nexport type AgentProgressListener<\n TScope extends string = string,\n TData = unknown,\n TPhase extends string = string,\n> = (event: AgentProgressEvent<TScope, TData, TPhase>) => Promise<void> | void;\n\n// --- aiAct: the first producer on the generic progress bus ---\n\nexport type AiActProgressPhase =\n | 'start'\n | 'plan_thinking'\n | 'plan_planned'\n | 'plan_action'\n | 'plan_failed'\n | 'action_running'\n | 'action_done'\n | 'action_failed'\n | 'complete'\n | 'failed';\n\nexport interface AiActProgressAction {\n name: string;\n target?: string;\n point?: [number, number];\n bbox?: [number, number, number, number];\n /**\n * Structured, compacted summary of the action params for actions that are\n * not described by a point/bbox (e.g. `Sleep` -> `{ timeMs }`). This is data,\n * never a pre-formatted display string; consumers decide how to render it.\n */\n param?: unknown;\n}\n\n/**\n * Structured payload carried by aiAct progress events. The producer only\n * reports *what happened* as data: the action involved, the raw text the model\n * produced, timings and errors. It never assembles human-readable log lines or\n * truncates strings for display - that belongs to whichever layer consumes the\n * stream (e.g. the CLI verbose renderer).\n */\nexport interface AiActProgressData {\n /** Original user instruction, present on the `start` phase. */\n prompt?: string;\n planIndex?: number;\n planLimit?: number;\n /** Latest screenshot, present on `plan_thinking`. */\n screenshot?: ScreenshotItem;\n /** Structured action descriptor, present on the `*action*` phases. */\n action?: AiActProgressAction;\n /**\n * Raw, untruncated semantic text produced by the model. Consumers choose\n * which field to surface and how to format/truncate it.\n */\n thought?: string;\n log?: string;\n output?: string;\n /** Wall-clock cost of an action, present on `action_done`/`action_failed`. */\n durationMs?: number;\n error?: string;\n}\n\nexport const aiActProgressScope = 'aiAct';\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n description?: string;\n timing?: string;\n}\n\nexport interface RecordToReportScreenshot {\n /**\n * PNG/JPEG data URI, or raw PNG base64 body.\n */\n base64: string;\n description?: string;\n}\n\nexport interface RecordToReportOptions {\n content?: string;\n /**\n * @deprecated Use `screenshots: [{ base64 }]` instead.\n */\n screenshotBase64?: string;\n /**\n * Custom screenshots to display under a single report entry.\n */\n screenshots?: RecordToReportScreenshot[];\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n /**\n * Maximum planning feedback length inherited from the action definition.\n * Set to 'unlimited' to disable truncation for this task.\n */\n planningFeedbackMaxLength?: number | 'unlimited';\n executor: (\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n /**\n * Optional feedback produced by a task for the next planning round.\n * This is execution metadata, not part of the action return value.\n */\n planningFeedback?: string;\n /**\n * A bounded diagnostic DTO created when the task executor throws. Arbitrary\n * upstream payloads are intentionally omitted; use this field for structured\n * diagnostics or errorMessage/errorStack for the common display path.\n */\n error?: SerializedError;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n getUiContextStart?: number;\n getUiContextEnd?: number;\n callAiStart?: number;\n callAiEnd?: number;\n beforeInvokeActionHookStart?: number;\n beforeInvokeActionHookEnd?: number;\n callActionStart?: number;\n callActionEnd?: number;\n afterInvokeActionHookStart?: number;\n afterInvokeActionHookEnd?: number;\n captureAfterCallingSnapshotStart?: number;\n captureAfterCallingSnapshotEnd?: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n /**\n * Pixel rect of the deepLocate first-stage search area in screenshot\n * coordinates. Used by reports to explain the crop/zoom area that the\n * final locate ran against.\n */\n searchArea?: Rect;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n /** Stable unique identifier for this execution run */\n id?: string;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/*\ntask - service-locate\n*/\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n DetailedLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n domIncluded?: boolean | 'visible-only';\n context?: string;\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport interface ExecutionTaskPlanningParam {\n userInstruction: TUserPrompt;\n userInstructionDisplay?: string;\n replanningCycleLimit?: number;\n aiActContext?: string;\n imagesIncludeCount?: number;\n effort?: AiActEffort;\n subGoalStatus?: string;\n memoriesStatus?: string;\n}\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningParam,\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n DetailedLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nHow a report file stores screenshots:\n- `inline`: base64 image script tags embedded in the single HTML file\n- `directory`: external PNG files under a sibling `screenshots/` dir\n*/\nexport type ScreenshotMode = 'inline' | 'directory';\n\n/*\nReport metadata - extracted from ReportActionDump for per-execution writes\n*/\nexport interface ReportMeta {\n groupName: string;\n groupDescription?: string;\n sdkVersion: string;\n modelBriefs: ModelBrief[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupMeta = ReportMeta;\n\n/*\nReport dump\n*/\nexport interface IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: ModelBrief[];\n executions: IExecutionDump[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type IGroupedActionDump = IReportActionDump;\n\nexport interface ModelBrief {\n /**\n * The intent/category of the model call, for example \"planning\" or \"insight\".\n */\n intent?: string;\n\n /**\n * The model name returned by usage metadata, for example \"gpt-4o\".\n */\n name?: string;\n\n /**\n * Optional human-readable model description, for example \"qwen2.5-vl mode\".\n */\n modelDescription?: string;\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Provider token usage, without guaranteed Midscene metadata. See the final response for AIUsageInfo. */\n usage?: OpenAI.CompletionUsage;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n /**\n * Maximum number of characters from planningFeedback sent to the next\n * planning round. Omit to use the core default, or set to 'unlimited' to\n * disable truncation for this action.\n */\n planningFeedbackMaxLength?: number | 'unlimited';\n call: (\n param: TParam,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n /**\n * An example param object for this action.\n * Locate fields with { prompt } may be resolved to internal pixel bboxes when needed.\n */\n sample?: { [K in keyof TParam]?: any };\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n /**\n * Optional cache directory path.\n * When set, cache files are written to this directory instead of\n * `<MIDSCENE_RUN_DIR>/cache`.\n */\n cacheDir?: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface AgentOpt {\n // @deprecated Use `reportFileName` and `cache.id` instead.\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if persist per-execution dump files next to the report, default false */\n persistExecutionDump?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n /**\n * Agent-level AI guidance containing business facts, rules, constraints, or\n * output requirements. `aiContexts.default` is a shared fallback used only\n * when neither the call nor the matching API key provides a context.\n *\n * A per-call `options.context` overrides the matching API context, which\n * overrides `aiContexts.default`. An empty string explicitly clears\n * inherited user context for that scope. These layers are not automatically\n * merged.\n */\n aiContexts?: AgentAIContexts;\n /**\n * Compatibility alias for `aiContexts.aiAct`. `aiContexts.aiAct` takes\n * precedence when both are provided.\n * @deprecated Use `aiContexts.aiAct` instead.\n */\n aiActContext?: string;\n /**\n * Older compatibility alias for `aiContexts.aiAct`.\n * @deprecated Use `aiContexts.aiAct` instead.\n */\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n reportAttributes?: ReportAttributes;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults are resolved by the active model adapter: 20 for standard planning,\n * 40 for UI-TARS, and 100 for Auto-GLM.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * When set to true, Midscene will use the target device's formatted local\n * time instead of the runtime system time. The target interface must implement\n * getDeviceLocalTimeString to provide device-local wall-clock time.\n * Default: false\n */\n useDeviceTime?: boolean;\n\n /**\n * Custom screenshot shrink factor to reduce AI token usage.\n * When set, the screenshot will be scaled down by this factor from the physical resolution.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - AI analyzes the 1500px screenshot\n * - Coordinates are transformed back to logical (500px) before actions execute\n *\n * Benefits:\n * - Reduces token usage for high-resolution screenshots\n * - Maintains accuracy by scaling coordinates appropriately\n *\n * Must be >= 1 (shrinking only, enlarging is not supported).\n *\n * @default 1 (no shrinking, uses original physical screenshot)\n */\n screenshotShrinkFactor?: number;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n\n /**\n * Called once per LLM call as soon as its usage is available, with the raw\n * {@link AIUsageInfo} (token counts, model name, intent, request id, etc.).\n *\n * Use this for real-time, per-spec cost observability — e.g. push each call\n * to Langfuse without waiting for the run to finish. For aggregated totals,\n * read `agent.metrics` instead.\n */\n onLLMUsage?: (usage: AIUsageInfo) => void;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileAttributes {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n}\n\ntype SkippedReportFileAttributes = Omit<ReportFileAttributes, 'testStatus'> & {\n testStatus: 'skipped';\n};\n\nexport type ReportFileWithAttributes =\n | {\n reportFilePath: string;\n reportAttributes: ReportFileAttributes;\n }\n | {\n reportFilePath?: undefined;\n reportAttributes: SkippedReportFileAttributes;\n };\n"],"names":["UIContext","aiActProgressScope"],"mappings":";;;AA4KO,MAAeA;AAqCtB;AA2VO,MAAMC,qBAAqB"}
|
|
1
|
+
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { AgentAIContexts } from '@midscene/shared/agent-tools/agent-context';\nimport type { SerializedError } from '@midscene/shared/agent-tools/error-formatter';\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type OpenAI from 'openai';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport type { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport type { SerializedError } from '@midscene/shared/agent-tools/error-formatter';\nexport type {\n AgentAIContextKey,\n AgentAIContexts,\n AiApiName,\n} from '@midscene/shared/agent-tools/agent-context';\nexport * from './yaml';\n\nexport { ServiceError } from './errors';\nexport {\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n} from './dump/report-action-dump';\n\nexport type RawAssistantOutput =\n | {\n type: 'chat-completion';\n rawValue: OpenAI.Chat.Completions.ChatCompletionMessage;\n }\n | {\n type: 'responses';\n rawValue: OpenAI.Responses.ResponseOutputItem[];\n };\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n /** Duration of the final successful request attempt, in milliseconds. */\n time_cost: number | undefined;\n /** Request duration including all attempts and retry waits, excluding preparation, in milliseconds. */\n total_time_cost?: number;\n /** Request retries performed within this call; excludes semantic parse retries. */\n retry_count?: number;\n /** Actual protocol used for the model call, resolved by Midscene. */\n api_protocol?: 'chat-completion' | 'responses' | 'codex';\n /** Whether the model call used streaming, after resolving call options. */\n stream?: boolean;\n model_name: string | undefined;\n model_description: string | undefined;\n /**\n * Raw top-level `.model` value returned by the model service response.\n */\n response_model_name: string | undefined;\n /**\n * Semantic intent of the model call, such as default, planning, or insight.\n */\n intent: string | undefined;\n /**\n * Config slot where the model config was resolved from. For example, a\n * planning call may use the default slot when no planning model is configured.\n */\n slot: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport type LocateResultPoint = [number, number];\nexport type Bbox = [number, number, number, number];\nexport type LocateResultBbox = Bbox;\nexport type PixelBbox = Bbox;\n\nexport interface AIElementLocateResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n error?: string;\n}\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n references_bbox?: LocateResultBbox[];\n references_point?: LocateResultPoint[];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect?: Rect;\n center: [number, number];\n centerDistance?: number;\n /** Whether the expected point is inside the located rect; absent for point-only results. */\n includedInRect?: boolean;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepLocate: boolean;\n deepDescribe: boolean;\n verifyResult?: LocateValidatorResult;\n success: boolean;\n error?: string;\n failureStage?: 'describe' | 'verify';\n}\n\n/**\n * context\n */\n\nexport interface UiNode {\n type: string;\n attrs: Record<string, string | undefined>;\n bounds: Rect;\n children: UiNode[];\n}\n\nexport interface UITreeSnapshot {\n platform: 'android';\n capturedAt: number;\n root: UiNode;\n}\n\nexport abstract class UIContext {\n /**\n * screenshot of the current UI state. which size is shotSize(be shrunk by screenshotShrinkFactor),\n */\n abstract screenshot: ScreenshotItem;\n\n /**\n * Optional sequence of screenshots captured over a short time window, in\n * temporal order (earliest first, latest last). When present with more than\n * one frame, extract/assert flows submit all frames to the model so it can\n * observe transient UI (toasts, carousels, auto-hiding controls). The last\n * frame is the same state as {@link screenshot}.\n */\n abstract screenshotSequence?: ScreenshotItem[];\n\n /**\n * screenshot size after shrinking\n */\n abstract shotSize: Size;\n\n /**\n * The ratio for converting shrunk screenshot coordinates to logical coordinates.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - User-defined screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - shrunkShotToLogicalRatio: dpr / screenshotShrinkFactor = 6 / 2 = 3\n * - To map back to logical coordinates: 1500 / shrunkShotToLogicalRatio = 500px\n */\n abstract shrunkShotToLogicalRatio: number;\n\n abstract _isFrozen?: boolean;\n\n // @deprecated - backward compatibility for aiLocate\n abstract deprecatedDpr?: number;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type AiActEffort = 'fast' | 'balance' | 'deepThink';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: unknown;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: unknown;\n rawAssistantOutput?: RawAssistantOutput;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n /**\n * Adapter-extracted content from the search-area model call. This is not the\n * full provider response or choices[0].message.\n */\n searchAreaRawResponse?: string;\n searchAreaRawChoiceMessage?: unknown;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport type ReportAttributes = Record<\n string,\n string | number | boolean | null | undefined\n>;\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: ReportAttributes;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement?: LocateResultElement[];\n deepLocate?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n /**\n * Additional facts, decision rules, constraints, or output requirements for\n * this assertion. It overrides `aiContexts.aiAssert` and\n * `aiContexts.default`;\n * `''` disables inherited user context for this call.\n */\n context?: string;\n abortSignal?: AbortSignal;\n}\n\nexport interface AgentAssertResult {\n pass: boolean;\n thought?: string;\n message?: string;\n}\n\nexport type QueryOptions = ServiceExtractOption;\nexport type AssertOptions = AgentAssertOpt & ServiceExtractOption;\n\n/** Options for fixed observations, which never read the live DOM. */\nexport type ObservationQueryOptions = Omit<QueryOptions, 'domIncluded'> & {\n domIncluded?: never;\n};\n\n/** Assertion options for fixed observations, which never read the live DOM. */\nexport type ObservationAssertOptions = Omit<AssertOptions, 'domIncluded'> & {\n domIncluded?: never;\n};\n\n/** Read-only AI operations supported by both live Agents and UI observations. */\nexport interface InsightAPI<\n QueryOpt extends QueryOptions = QueryOptions,\n AssertOpt extends AssertOptions = AssertOptions,\n> {\n aiQuery<ReturnType = any>(\n demand: ServiceExtractParam,\n options?: QueryOpt,\n ): Promise<ReturnType>;\n aiBoolean(prompt: TUserPrompt, options?: QueryOpt): Promise<boolean>;\n aiNumber(prompt: TUserPrompt, options?: QueryOpt): Promise<number>;\n aiString(prompt: TUserPrompt, options?: QueryOpt): Promise<string>;\n aiAsk(prompt: TUserPrompt, options?: QueryOpt): Promise<string>;\n aiAssert(\n assertion: TUserPrompt,\n message?: string,\n options?: AssertOpt,\n ): Promise<AgentAssertResult | undefined>;\n}\n\n/**\n * planning\n *\n */\n\ntype PlanningActionBase = {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n};\n\nexport type PlanningAction<ParamType = undefined> = PlanningActionBase &\n ([ParamType] extends [undefined] ? { param?: any } : { param: ParamType });\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction | null;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawAssistantOutput?: RawAssistantOutput;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\n/**\n * Generic agent progress bus.\n *\n * Progress notifications are a single, generic stream the agent broadcasts as\n * it works. Each event is a thin envelope: a `scope` naming the producer, a\n * `phase` within that producer's lifecycle, a monotonic `sequence`, and a\n * structured, presentation-free `data` payload. The bus knows nothing about any\n * particular producer's payload - consumers narrow by `scope`. `aiAct` is the\n * first producer (\"pilot\") on this bus; others (queries, waits, ...) can be\n * added without touching the bus, the listener API, or the renderer core.\n */\nexport interface AgentProgressEvent<\n TScope extends string = string,\n TData = unknown,\n TPhase extends string = string,\n> {\n scope: TScope;\n phase: TPhase;\n sequence: number;\n data: TData;\n}\n\nexport type AgentProgressListener<\n TScope extends string = string,\n TData = unknown,\n TPhase extends string = string,\n> = (event: AgentProgressEvent<TScope, TData, TPhase>) => Promise<void> | void;\n\n// --- aiAct: the first producer on the generic progress bus ---\n\nexport type AiActProgressPhase =\n | 'start'\n | 'plan_thinking'\n | 'plan_planned'\n | 'plan_action'\n | 'plan_failed'\n | 'action_running'\n | 'action_done'\n | 'action_failed'\n | 'complete'\n | 'failed';\n\nexport interface AiActProgressAction {\n name: string;\n target?: string;\n point?: [number, number];\n bbox?: [number, number, number, number];\n /**\n * Structured, compacted summary of the action params for actions that are\n * not described by a point/bbox (e.g. `Sleep` -> `{ timeMs }`). This is data,\n * never a pre-formatted display string; consumers decide how to render it.\n */\n param?: unknown;\n}\n\n/**\n * Structured payload carried by aiAct progress events. The producer only\n * reports *what happened* as data: the action involved, the raw text the model\n * produced, timings and errors. It never assembles human-readable log lines or\n * truncates strings for display - that belongs to whichever layer consumes the\n * stream (e.g. the CLI verbose renderer).\n */\nexport interface AiActProgressData {\n /** Original user instruction, present on the `start` phase. */\n prompt?: string;\n planIndex?: number;\n planLimit?: number;\n /** Latest screenshot, present on `plan_thinking`. */\n screenshot?: ScreenshotItem;\n /** Structured action descriptor, present on the `*action*` phases. */\n action?: AiActProgressAction;\n /**\n * Raw, untruncated semantic text produced by the model. Consumers choose\n * which field to surface and how to format/truncate it.\n */\n thought?: string;\n log?: string;\n output?: string;\n /** Wall-clock cost of an action, present on `action_done`/`action_failed`. */\n durationMs?: number;\n error?: string;\n}\n\nexport const aiActProgressScope = 'aiAct';\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n description?: string;\n timing?: string;\n}\n\nexport interface RecordToReportScreenshot {\n /**\n * PNG/JPEG data URI, or raw PNG base64 body.\n */\n base64: string;\n description?: string;\n}\n\nexport interface RecordToReportOptions {\n content?: string;\n /**\n * @deprecated Use `screenshots: [{ base64 }]` instead.\n */\n screenshotBase64?: string;\n /**\n * Custom screenshots to display under a single report entry.\n */\n screenshots?: RecordToReportScreenshot[];\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n /** Custom readiness owns optional post-action settling, not input/gesture timing. */\n skipDefaultWait?: boolean;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n /**\n * Maximum planning feedback length inherited from the action definition.\n * Set to 'unlimited' to disable truncation for this task.\n */\n planningFeedbackMaxLength?: number | 'unlimited';\n executor: (\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n /** Readiness strategy selected for this atomic action execution. */\n actionReadiness?: 'default' | 'custom';\n /**\n * Optional feedback produced by a task for the next planning round.\n * This is execution metadata, not part of the action return value.\n */\n planningFeedback?: string;\n /**\n * A bounded diagnostic DTO created when the task executor throws. Arbitrary\n * upstream payloads are intentionally omitted; use this field for structured\n * diagnostics or errorMessage/errorStack for the common display path.\n */\n error?: SerializedError;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n getUiContextStart?: number;\n getUiContextEnd?: number;\n callAiStart?: number;\n callAiEnd?: number;\n beforeInvokeActionHookStart?: number;\n beforeInvokeActionHookEnd?: number;\n callActionStart?: number;\n callActionEnd?: number;\n afterInvokeActionHookStart?: number;\n afterInvokeActionHookEnd?: number;\n waitForActionReadyStart?: number;\n waitForActionReadyEnd?: number;\n captureAfterCallingSnapshotStart?: number;\n captureAfterCallingSnapshotEnd?: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n /**\n * Pixel rect of the deepLocate first-stage search area in screenshot\n * coordinates. Used by reports to explain the crop/zoom area that the\n * final locate ran against.\n */\n searchArea?: Rect;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n /** Stable unique identifier for this execution run */\n id?: string;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/*\ntask - service-locate\n*/\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n DetailedLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n domIncluded?: boolean | 'visible-only';\n context?: string;\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport interface ExecutionTaskPlanningParam {\n userInstruction: TUserPrompt;\n userInstructionDisplay?: string;\n replanningCycleLimit?: number;\n aiActContext?: string;\n imagesIncludeCount?: number;\n effort?: AiActEffort;\n subGoalStatus?: string;\n memoriesStatus?: string;\n}\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningParam,\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n DetailedLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nHow a report file stores screenshots:\n- `inline`: base64 image script tags embedded in the single HTML file\n- `directory`: external PNG files under a sibling `screenshots/` dir\n*/\nexport type ScreenshotMode = 'inline' | 'directory';\n\n/*\nReport metadata - extracted from ReportActionDump for per-execution writes\n*/\nexport interface ReportMeta {\n groupName: string;\n groupDescription?: string;\n sdkVersion: string;\n modelBriefs: ModelBrief[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupMeta = ReportMeta;\n\n/*\nReport dump\n*/\nexport interface IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: ModelBrief[];\n executions: IExecutionDump[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type IGroupedActionDump = IReportActionDump;\n\nexport interface ModelBrief {\n /**\n * The intent/category of the model call, for example \"planning\" or \"insight\".\n */\n intent?: string;\n\n /**\n * The model name returned by usage metadata, for example \"gpt-4o\".\n */\n name?: string;\n\n /**\n * Optional human-readable model description, for example \"qwen2.5-vl mode\".\n */\n modelDescription?: string;\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Provider token usage, without guaranteed Midscene metadata. See the final response for AIUsageInfo. */\n usage?: OpenAI.CompletionUsage;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n /**\n * Maximum number of characters from planningFeedback sent to the next\n * planning round. Omit to use the core default, or set to 'unlimited' to\n * disable truncation for this action.\n */\n planningFeedbackMaxLength?: number | 'unlimited';\n call: (\n param: TParam,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n /**\n * An example param object for this action.\n * Locate fields with { prompt } may be resolved to internal pixel bboxes when needed.\n */\n sample?: { [K in keyof TParam]?: any };\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n /**\n * Optional cache directory path.\n * When set, cache files are written to this directory instead of\n * `<MIDSCENE_RUN_DIR>/cache`.\n */\n cacheDir?: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface ActionReadyContext {\n /** Cancels this execution. Use it to stop polling, requests, or listeners. */\n readonly signal?: AbortSignal;\n /** A new identity for each executed atomic action, including cache replay. */\n action: {\n readonly id: string;\n readonly name: string;\n /** Resolved action parameters. Treat them as read-only. */\n readonly param: unknown;\n };\n}\n\n/** Runs after a successful atomic action. Slow callbacks warn after 5 seconds. */\nexport type WaitForActionReady = (context: ActionReadyContext) => Promise<void>;\n\nexport interface AgentOpt {\n // @deprecated Use `reportFileName` and `cache.id` instead.\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if persist per-execution dump files next to the report, default false */\n persistExecutionDump?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n /**\n * Agent-level AI guidance containing business facts, rules, constraints, or\n * output requirements. `aiContexts.default` is a shared fallback used only\n * when neither the call nor the matching API key provides a context.\n *\n * A per-call `options.context` overrides the matching API context, which\n * overrides `aiContexts.default`. An empty string explicitly clears\n * inherited user context for that scope. These layers are not automatically\n * merged.\n */\n aiContexts?: AgentAIContexts;\n /**\n * Compatibility alias for `aiContexts.aiAct`. `aiContexts.aiAct` takes\n * precedence when both are provided.\n * @deprecated Use `aiContexts.aiAct` instead.\n */\n aiActContext?: string;\n /**\n * Older compatibility alias for `aiContexts.aiAct`.\n * @deprecated Use `aiContexts.aiAct` instead.\n */\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n reportAttributes?: ReportAttributes;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults are resolved by the active model adapter: 20 for standard planning,\n * 40 for UI-TARS, and 100 for Auto-GLM.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * Await application readiness after each successful atomic action, including\n * aiAct substeps and cache replay. Replaces default action waits.\n * After 5 seconds, logs one warning and continues waiting without a timeout.\n */\n waitForActionReady?: WaitForActionReady;\n\n /**\n * When set to true, Midscene will use the target device's formatted local\n * time instead of the runtime system time. The target interface must implement\n * getDeviceLocalTimeString to provide device-local wall-clock time.\n * Default: false\n */\n useDeviceTime?: boolean;\n\n /**\n * Custom screenshot shrink factor to reduce AI token usage.\n * When set, the screenshot will be scaled down by this factor from the physical resolution.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - AI analyzes the 1500px screenshot\n * - Coordinates are transformed back to logical (500px) before actions execute\n *\n * Benefits:\n * - Reduces token usage for high-resolution screenshots\n * - Maintains accuracy by scaling coordinates appropriately\n *\n * Must be >= 1 (shrinking only, enlarging is not supported).\n *\n * @default 1 (no shrinking, uses original physical screenshot)\n */\n screenshotShrinkFactor?: number;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n\n /**\n * Called once per LLM call as soon as its usage is available, with the raw\n * {@link AIUsageInfo} (token counts, model name, intent, request id, etc.).\n *\n * Use this for real-time, per-spec cost observability — e.g. push each call\n * to Langfuse without waiting for the run to finish. For aggregated totals,\n * read `agent.metrics` instead.\n */\n onLLMUsage?: (usage: AIUsageInfo) => void;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileAttributes {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n}\n\ntype SkippedReportFileAttributes = Omit<ReportFileAttributes, 'testStatus'> & {\n testStatus: 'skipped';\n};\n\nexport type ReportFileWithAttributes =\n | {\n reportFilePath: string;\n reportAttributes: ReportFileAttributes;\n }\n | {\n reportFilePath?: undefined;\n reportAttributes: SkippedReportFileAttributes;\n };\n"],"names":["UIContext","aiActProgressScope"],"mappings":";;;AA8KO,MAAeA;AAqCtB;AA2VO,MAAMC,qBAAqB"}
|
package/dist/es/utils.mjs
CHANGED
|
@@ -184,7 +184,7 @@ function stringifyDumpData(data, indents) {
|
|
|
184
184
|
return JSON.stringify(data, replacerForPageObject, indents);
|
|
185
185
|
}
|
|
186
186
|
function getVersion() {
|
|
187
|
-
return "1.
|
|
187
|
+
return "1.15.0";
|
|
188
188
|
}
|
|
189
189
|
function debugLog(...message) {
|
|
190
190
|
const debugMode = process.env[MIDSCENE_DEBUG_MODE];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isActionReadinessError } from "../agent/action-readiness.mjs";
|
|
1
2
|
import { deriveCaseStatus } from "../dump/task-status.mjs";
|
|
2
3
|
import { getDebug } from "@midscene/shared/logger";
|
|
3
4
|
import { assert } from "@midscene/shared/utils";
|
|
@@ -88,6 +89,11 @@ const createLegacyYamlNodeResolver = (options, resolveTask, recordTaskErrors, ca
|
|
|
88
89
|
};
|
|
89
90
|
const runLegacyYamlDocument = (document, options, resolveTask, recordTaskErrors, captureReportTraces, runOptions)=>runWorkflowDocument(document, {
|
|
90
91
|
...runOptions ?? {},
|
|
92
|
+
isFatalError: (run)=>[
|
|
93
|
+
...run.beforeEach,
|
|
94
|
+
...run.steps,
|
|
95
|
+
...run.afterEach
|
|
96
|
+
].some((step)=>isActionReadinessError(step.error?.cause)) || (runOptions?.isFatalError?.(run) ?? false),
|
|
91
97
|
onStepResult: async (info, result)=>{
|
|
92
98
|
if (result.meta.captureResult && result.output && Object.hasOwn(result.output, 'data')) await options.setResult(result.meta.resultName, getLegacyYamlResultData(result.node, result.output));
|
|
93
99
|
await runOptions?.onStepResult?.(info, result);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml/legacy-yaml-runtime.mjs","sources":["../../../src/yaml/legacy-yaml-runtime.ts"],"sourcesContent":["import type { Agent } from '@/agent/agent';\nimport { deriveCaseStatus } from '@/dump/task-status';\nimport type {\n DeviceAction,\n MidsceneYamlScript,\n MidsceneYamlTask,\n} from '@/types';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport {\n type CaseRunOutcome,\n type CollectedWorkflowDocument,\n type NodeDefinition,\n type RunWorkflowDocumentOptions,\n type WorkflowDocumentExecutionResult,\n assertResourceAvailable,\n defineNode,\n runWorkflowDocument,\n trackResourceOperation,\n waitForResourceIdle,\n} from '../test-runner';\nimport {\n enterYamlAction,\n runInYamlExecutionContext,\n} from './execution-session';\nimport {\n collectLegacyYamlDocument,\n collectLegacyYamlTaskDocument,\n} from './test-runner-compat';\nimport {\n getLegacyYamlResultData,\n legacyAgentTestRunnerNodeDefinitions,\n} from './test-runner-nodes';\nconst debug = getDebug('yaml-player');\n\ntype SetLegacyYamlResult = (\n key: string | undefined,\n value: any,\n) => void | Promise<void>;\n\nexport type LegacyYamlRuntimeRunOptions = Omit<\n RunWorkflowDocumentOptions<Agent>,\n 'projectContext' | 'resolveCaseReportScopeId' | 'resolveNode' | 'retry'\n> & { document?: CollectedWorkflowDocument };\n\nexport interface LegacyYamlRuntimeOptions {\n agent: Agent;\n actionSpace: readonly DeviceAction[];\n sourcePath?: string;\n setResult: SetLegacyYamlResult;\n}\n\nexport interface LegacyYamlRuntime {\n /** Wait before releasing an Agent whose non-cooperative action was aborted. */\n waitForIdle(): Promise<void>;\n runScript(\n script: MidsceneYamlScript,\n options?: LegacyYamlRuntimeRunOptions,\n ): Promise<WorkflowDocumentExecutionResult>;\n runTask(\n task: MidsceneYamlTask,\n taskIndex: number,\n options?: LegacyYamlRuntimeRunOptions,\n ): Promise<WorkflowDocumentExecutionResult>;\n}\n\nconst hasFailedReportExecutionSince = (\n agent: Agent,\n executionCountBefore: number,\n): boolean =>\n deriveCaseStatus(\n (agent.dump?.executions ?? []).slice(executionCountBefore),\n ) === 'failed';\n\nconst recordYamlTaskError = async (\n agent: Agent,\n task: MidsceneYamlTask,\n flowItemIndex: number,\n error: Error,\n): Promise<void> => {\n const recordErrorToReport = (agent as any).recordErrorToReport;\n if (typeof recordErrorToReport !== 'function') return;\n\n try {\n await recordErrorToReport.call(agent, `YAML task failed - ${task.name}`, {\n error,\n content: `Step ${flowItemIndex} failed while running YAML task \"${task.name}\".`,\n });\n } catch (reportError) {\n debug('failed to record yaml error to report', reportError);\n }\n};\n\nconst createLegacyYamlNodeResolver = (\n options: LegacyYamlRuntimeOptions,\n resolveTask: (taskIndex: number) => MidsceneYamlTask | undefined,\n recordTaskErrors: boolean,\n captureReportTraces: boolean,\n): ((\n name: string,\n) => NodeDefinition<Record<string, unknown>, unknown, Agent>) => {\n const definitions = new Map(\n legacyAgentTestRunnerNodeDefinitions.map((definition) => [\n definition.name,\n definition,\n ]),\n );\n const nodes = new Map<\n string,\n NodeDefinition<Record<string, unknown>, unknown, Agent>\n >();\n\n return (name) => {\n const existing = nodes.get(name);\n if (existing) return existing;\n\n const definition = definitions.get(name);\n assert(definition, `Unknown Agent Node: ${name}`);\n const node = defineNode<Record<string, unknown>, unknown, Agent>({\n name,\n inputSchema: definition.inputSchema,\n execute: (ctx) =>\n runInYamlExecutionContext(async () => {\n ctx.signal.throwIfAborted();\n assertResourceAvailable(options.agent, false, ctx.signal);\n assert(ctx.scope === 'case', 'YAML task requires a Case scope');\n const taskIndex = ctx.case.caseIndex;\n const flowItemIndex = ctx.case.stepIndex;\n const task = resolveTask(taskIndex);\n assert(task, `missing YAML task at index ${taskIndex}`);\n assert(task.flow, 'missing flow in task');\n\n const executionCountBeforeStep =\n options.agent.dump?.executions?.length ?? 0;\n const addDumpUpdateListener = captureReportTraces\n ? (options.agent as any).addDumpUpdateListener\n : undefined;\n const removeDumpListener =\n typeof addDumpUpdateListener === 'function'\n ? addDumpUpdateListener.call(\n options.agent,\n (_dump: string, execution?: { id?: string }) => {\n if (!ctx.signal.aborted && execution?.id) {\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n });\n }\n },\n )\n : undefined;\n\n let listening = true;\n const removeListener = () => {\n if (!listening) return;\n listening = false;\n removeDumpListener?.();\n };\n const operation = trackResourceOperation(\n options.agent,\n Promise.resolve().then(() => {\n ctx.signal.throwIfAborted();\n return definition.execute(options.agent, ctx.input, {\n signal: ctx.signal,\n });\n }),\n ctx.signal,\n );\n const leaveAction = enterYamlAction(options.agent, ctx.signal);\n const onAbort = () => {\n removeListener();\n };\n ctx.signal.addEventListener('abort', onAbort, { once: true });\n\n try {\n return await operation;\n } catch (error) {\n const reportAlreadyContainsFailure = hasFailedReportExecutionSince(\n options.agent,\n executionCountBeforeStep,\n );\n if (\n !ctx.signal.aborted &&\n recordTaskErrors &&\n !reportAlreadyContainsFailure\n ) {\n await recordYamlTaskError(\n options.agent,\n task,\n flowItemIndex,\n error as Error,\n );\n }\n throw error;\n } finally {\n ctx.signal.removeEventListener('abort', onAbort);\n removeListener();\n leaveAction();\n }\n }),\n });\n nodes.set(name, node);\n return node;\n };\n};\n\nconst runLegacyYamlDocument = (\n document: CollectedWorkflowDocument,\n options: LegacyYamlRuntimeOptions,\n resolveTask: (taskIndex: number) => MidsceneYamlTask | undefined,\n recordTaskErrors: boolean,\n captureReportTraces: boolean,\n runOptions?: LegacyYamlRuntimeRunOptions,\n): Promise<WorkflowDocumentExecutionResult> => {\n return runWorkflowDocument(document, {\n ...(runOptions ?? {}),\n onStepResult: async (info, result) => {\n if (\n result.meta.captureResult &&\n result.output &&\n Object.hasOwn(result.output, 'data')\n ) {\n // Publication follows the recorded action. The Node result is never mutated.\n await options.setResult(\n result.meta.resultName,\n getLegacyYamlResultData(result.node, result.output),\n );\n }\n await runOptions?.onStepResult?.(info, result);\n },\n resolveNode: createLegacyYamlNodeResolver(\n options,\n resolveTask,\n recordTaskErrors,\n captureReportTraces,\n ),\n projectContext: options.agent,\n // Legacy CLI retry recreates the whole player and Agent outside this\n // runtime, so the shared kernel must not retry a Case here.\n retry: 0,\n // One legacy YAML file owns one Agent report shared by all of its tasks.\n resolveCaseReportScopeId: (_collectedCase, _attemptIndex, documentRunId) =>\n documentRunId,\n });\n};\n\nexport const createLegacyYamlRuntime = (\n options: LegacyYamlRuntimeOptions,\n): LegacyYamlRuntime => {\n const sourcePath = options.sourcePath ?? '<inline-yaml>';\n return {\n waitForIdle: () => waitForResourceIdle(options.agent),\n runScript: (script, runOptions) =>\n runLegacyYamlDocument(\n runOptions?.document ??\n collectLegacyYamlDocument(script, sourcePath, options.actionSpace),\n options,\n (taskIndex) => script.tasks?.[taskIndex],\n true,\n true,\n runOptions,\n ),\n runTask: (task, taskIndex, runOptions) =>\n runLegacyYamlDocument(\n collectLegacyYamlTaskDocument(\n task,\n taskIndex,\n sourcePath,\n options.actionSpace,\n ),\n options,\n () => task,\n false,\n false,\n runOptions,\n ),\n };\n};\n\nexport const legacyYamlOutcomeError = (outcome: CaseRunOutcome): Error => {\n const failedStep = [\n ...(outcome.run?.beforeEach ?? []),\n ...(outcome.run?.steps ?? []),\n ...(outcome.run?.afterEach ?? []),\n ].find((step) => step.status === 'failed');\n const error = failedStep?.error;\n return error?.code === 'NODE_EXECUTION_ERROR' && error.cause instanceof Error\n ? error.cause\n : (error ?? new Error(`Task \"${outcome.name}\" failed`));\n};\n"],"names":["debug","getDebug","hasFailedReportExecutionSince","agent","executionCountBefore","deriveCaseStatus","recordYamlTaskError","task","flowItemIndex","error","recordErrorToReport","reportError","createLegacyYamlNodeResolver","options","resolveTask","recordTaskErrors","captureReportTraces","definitions","Map","legacyAgentTestRunnerNodeDefinitions","definition","nodes","name","existing","assert","node","defineNode","ctx","runInYamlExecutionContext","assertResourceAvailable","taskIndex","executionCountBeforeStep","addDumpUpdateListener","undefined","removeDumpListener","_dump","execution","listening","removeListener","operation","trackResourceOperation","Promise","leaveAction","enterYamlAction","onAbort","reportAlreadyContainsFailure","runLegacyYamlDocument","document","runOptions","runWorkflowDocument","info","result","Object","getLegacyYamlResultData","_collectedCase","_attemptIndex","documentRunId","createLegacyYamlRuntime","sourcePath","waitForResourceIdle","script","collectLegacyYamlDocument","collectLegacyYamlTaskDocument","legacyYamlOutcomeError","outcome","failedStep","step","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"yaml/legacy-yaml-runtime.mjs","sources":["../../../src/yaml/legacy-yaml-runtime.ts"],"sourcesContent":["import { isActionReadinessError } from '@/agent/action-readiness';\nimport type { Agent } from '@/agent/agent';\nimport { deriveCaseStatus } from '@/dump/task-status';\nimport type {\n DeviceAction,\n MidsceneYamlScript,\n MidsceneYamlTask,\n} from '@/types';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport {\n type CaseRunOutcome,\n type CollectedWorkflowDocument,\n type NodeDefinition,\n type RunWorkflowDocumentOptions,\n type WorkflowDocumentExecutionResult,\n assertResourceAvailable,\n defineNode,\n runWorkflowDocument,\n trackResourceOperation,\n waitForResourceIdle,\n} from '../test-runner';\nimport {\n enterYamlAction,\n runInYamlExecutionContext,\n} from './execution-session';\nimport {\n collectLegacyYamlDocument,\n collectLegacyYamlTaskDocument,\n} from './test-runner-compat';\nimport {\n getLegacyYamlResultData,\n legacyAgentTestRunnerNodeDefinitions,\n} from './test-runner-nodes';\nconst debug = getDebug('yaml-player');\n\ntype SetLegacyYamlResult = (\n key: string | undefined,\n value: any,\n) => void | Promise<void>;\n\nexport type LegacyYamlRuntimeRunOptions = Omit<\n RunWorkflowDocumentOptions<Agent>,\n 'projectContext' | 'resolveCaseReportScopeId' | 'resolveNode' | 'retry'\n> & { document?: CollectedWorkflowDocument };\n\nexport interface LegacyYamlRuntimeOptions {\n agent: Agent;\n actionSpace: readonly DeviceAction[];\n sourcePath?: string;\n setResult: SetLegacyYamlResult;\n}\n\nexport interface LegacyYamlRuntime {\n /** Wait before releasing an Agent whose non-cooperative action was aborted. */\n waitForIdle(): Promise<void>;\n runScript(\n script: MidsceneYamlScript,\n options?: LegacyYamlRuntimeRunOptions,\n ): Promise<WorkflowDocumentExecutionResult>;\n runTask(\n task: MidsceneYamlTask,\n taskIndex: number,\n options?: LegacyYamlRuntimeRunOptions,\n ): Promise<WorkflowDocumentExecutionResult>;\n}\n\nconst hasFailedReportExecutionSince = (\n agent: Agent,\n executionCountBefore: number,\n): boolean =>\n deriveCaseStatus(\n (agent.dump?.executions ?? []).slice(executionCountBefore),\n ) === 'failed';\n\nconst recordYamlTaskError = async (\n agent: Agent,\n task: MidsceneYamlTask,\n flowItemIndex: number,\n error: Error,\n): Promise<void> => {\n const recordErrorToReport = (agent as any).recordErrorToReport;\n if (typeof recordErrorToReport !== 'function') return;\n\n try {\n await recordErrorToReport.call(agent, `YAML task failed - ${task.name}`, {\n error,\n content: `Step ${flowItemIndex} failed while running YAML task \"${task.name}\".`,\n });\n } catch (reportError) {\n debug('failed to record yaml error to report', reportError);\n }\n};\n\nconst createLegacyYamlNodeResolver = (\n options: LegacyYamlRuntimeOptions,\n resolveTask: (taskIndex: number) => MidsceneYamlTask | undefined,\n recordTaskErrors: boolean,\n captureReportTraces: boolean,\n): ((\n name: string,\n) => NodeDefinition<Record<string, unknown>, unknown, Agent>) => {\n const definitions = new Map(\n legacyAgentTestRunnerNodeDefinitions.map((definition) => [\n definition.name,\n definition,\n ]),\n );\n const nodes = new Map<\n string,\n NodeDefinition<Record<string, unknown>, unknown, Agent>\n >();\n\n return (name) => {\n const existing = nodes.get(name);\n if (existing) return existing;\n\n const definition = definitions.get(name);\n assert(definition, `Unknown Agent Node: ${name}`);\n const node = defineNode<Record<string, unknown>, unknown, Agent>({\n name,\n inputSchema: definition.inputSchema,\n execute: (ctx) =>\n runInYamlExecutionContext(async () => {\n ctx.signal.throwIfAborted();\n assertResourceAvailable(options.agent, false, ctx.signal);\n assert(ctx.scope === 'case', 'YAML task requires a Case scope');\n const taskIndex = ctx.case.caseIndex;\n const flowItemIndex = ctx.case.stepIndex;\n const task = resolveTask(taskIndex);\n assert(task, `missing YAML task at index ${taskIndex}`);\n assert(task.flow, 'missing flow in task');\n\n const executionCountBeforeStep =\n options.agent.dump?.executions?.length ?? 0;\n const addDumpUpdateListener = captureReportTraces\n ? (options.agent as any).addDumpUpdateListener\n : undefined;\n const removeDumpListener =\n typeof addDumpUpdateListener === 'function'\n ? addDumpUpdateListener.call(\n options.agent,\n (_dump: string, execution?: { id?: string }) => {\n if (!ctx.signal.aborted && execution?.id) {\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n });\n }\n },\n )\n : undefined;\n\n let listening = true;\n const removeListener = () => {\n if (!listening) return;\n listening = false;\n removeDumpListener?.();\n };\n const operation = trackResourceOperation(\n options.agent,\n Promise.resolve().then(() => {\n ctx.signal.throwIfAborted();\n return definition.execute(options.agent, ctx.input, {\n signal: ctx.signal,\n });\n }),\n ctx.signal,\n );\n const leaveAction = enterYamlAction(options.agent, ctx.signal);\n const onAbort = () => {\n removeListener();\n };\n ctx.signal.addEventListener('abort', onAbort, { once: true });\n\n try {\n return await operation;\n } catch (error) {\n const reportAlreadyContainsFailure = hasFailedReportExecutionSince(\n options.agent,\n executionCountBeforeStep,\n );\n if (\n !ctx.signal.aborted &&\n recordTaskErrors &&\n !reportAlreadyContainsFailure\n ) {\n await recordYamlTaskError(\n options.agent,\n task,\n flowItemIndex,\n error as Error,\n );\n }\n throw error;\n } finally {\n ctx.signal.removeEventListener('abort', onAbort);\n removeListener();\n leaveAction();\n }\n }),\n });\n nodes.set(name, node);\n return node;\n };\n};\n\nconst runLegacyYamlDocument = (\n document: CollectedWorkflowDocument,\n options: LegacyYamlRuntimeOptions,\n resolveTask: (taskIndex: number) => MidsceneYamlTask | undefined,\n recordTaskErrors: boolean,\n captureReportTraces: boolean,\n runOptions?: LegacyYamlRuntimeRunOptions,\n): Promise<WorkflowDocumentExecutionResult> => {\n return runWorkflowDocument(document, {\n ...(runOptions ?? {}),\n isFatalError: (run) =>\n [...run.beforeEach, ...run.steps, ...run.afterEach].some((step) =>\n isActionReadinessError(step.error?.cause),\n ) ||\n (runOptions?.isFatalError?.(run) ?? false),\n onStepResult: async (info, result) => {\n if (\n result.meta.captureResult &&\n result.output &&\n Object.hasOwn(result.output, 'data')\n ) {\n // Publication follows the recorded action. The Node result is never mutated.\n await options.setResult(\n result.meta.resultName,\n getLegacyYamlResultData(result.node, result.output),\n );\n }\n await runOptions?.onStepResult?.(info, result);\n },\n resolveNode: createLegacyYamlNodeResolver(\n options,\n resolveTask,\n recordTaskErrors,\n captureReportTraces,\n ),\n projectContext: options.agent,\n // Legacy CLI retry recreates the whole player and Agent outside this\n // runtime, so the shared kernel must not retry a Case here.\n retry: 0,\n // One legacy YAML file owns one Agent report shared by all of its tasks.\n resolveCaseReportScopeId: (_collectedCase, _attemptIndex, documentRunId) =>\n documentRunId,\n });\n};\n\nexport const createLegacyYamlRuntime = (\n options: LegacyYamlRuntimeOptions,\n): LegacyYamlRuntime => {\n const sourcePath = options.sourcePath ?? '<inline-yaml>';\n return {\n waitForIdle: () => waitForResourceIdle(options.agent),\n runScript: (script, runOptions) =>\n runLegacyYamlDocument(\n runOptions?.document ??\n collectLegacyYamlDocument(script, sourcePath, options.actionSpace),\n options,\n (taskIndex) => script.tasks?.[taskIndex],\n true,\n true,\n runOptions,\n ),\n runTask: (task, taskIndex, runOptions) =>\n runLegacyYamlDocument(\n collectLegacyYamlTaskDocument(\n task,\n taskIndex,\n sourcePath,\n options.actionSpace,\n ),\n options,\n () => task,\n false,\n false,\n runOptions,\n ),\n };\n};\n\nexport const legacyYamlOutcomeError = (outcome: CaseRunOutcome): Error => {\n const failedStep = [\n ...(outcome.run?.beforeEach ?? []),\n ...(outcome.run?.steps ?? []),\n ...(outcome.run?.afterEach ?? []),\n ].find((step) => step.status === 'failed');\n const error = failedStep?.error;\n return error?.code === 'NODE_EXECUTION_ERROR' && error.cause instanceof Error\n ? error.cause\n : (error ?? new Error(`Task \"${outcome.name}\" failed`));\n};\n"],"names":["debug","getDebug","hasFailedReportExecutionSince","agent","executionCountBefore","deriveCaseStatus","recordYamlTaskError","task","flowItemIndex","error","recordErrorToReport","reportError","createLegacyYamlNodeResolver","options","resolveTask","recordTaskErrors","captureReportTraces","definitions","Map","legacyAgentTestRunnerNodeDefinitions","definition","nodes","name","existing","assert","node","defineNode","ctx","runInYamlExecutionContext","assertResourceAvailable","taskIndex","executionCountBeforeStep","addDumpUpdateListener","undefined","removeDumpListener","_dump","execution","listening","removeListener","operation","trackResourceOperation","Promise","leaveAction","enterYamlAction","onAbort","reportAlreadyContainsFailure","runLegacyYamlDocument","document","runOptions","runWorkflowDocument","run","step","isActionReadinessError","info","result","Object","getLegacyYamlResultData","_collectedCase","_attemptIndex","documentRunId","createLegacyYamlRuntime","sourcePath","waitForResourceIdle","script","collectLegacyYamlDocument","collectLegacyYamlTaskDocument","legacyYamlOutcomeError","outcome","failedStep","Error"],"mappings":"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"}
|
package/dist/es/yaml/player.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { isActionReadinessError } from "../agent/action-readiness.mjs";
|
|
3
4
|
import { getMidsceneRunSubDir } from "@midscene/shared/common";
|
|
4
5
|
import { getDebug } from "@midscene/shared/logger";
|
|
5
6
|
import { assert, ifInBrowser, ifInWorker, uuid } from "@midscene/shared/utils";
|
|
@@ -101,6 +102,7 @@ class ScriptPlayer {
|
|
|
101
102
|
const taskIndex = statusIndex >= 0 ? statusIndex : 'number' == typeof declaredIndex && Number.isInteger(declaredIndex) ? declaredIndex : 0;
|
|
102
103
|
const state = getLegacyYamlPlayerState(this);
|
|
103
104
|
state.executionResult = await this.createRuntime(agent).runTask(taskStatus, taskIndex, {
|
|
105
|
+
signal: this.abortSignal ?? currentYamlSignal(agent),
|
|
104
106
|
onStepStart: (info)=>{
|
|
105
107
|
if ('case' === info.scope && 'steps' === info.case.phase) taskStatus.currentStep = info.case.stepIndex;
|
|
106
108
|
}
|
|
@@ -144,7 +146,7 @@ class ScriptPlayer {
|
|
|
144
146
|
...newFreeFn || []
|
|
145
147
|
];
|
|
146
148
|
session = enterYamlExecution(agent);
|
|
147
|
-
signal = currentYamlSignal(agent);
|
|
149
|
+
signal = this.abortSignal ?? currentYamlSignal(agent);
|
|
148
150
|
signal?.throwIfAborted();
|
|
149
151
|
this.actionSpace = await agent.getActionSpace();
|
|
150
152
|
const originalOnTaskStartTip = agent.onTaskStartTip;
|
|
@@ -197,7 +199,7 @@ class ScriptPlayer {
|
|
|
197
199
|
await this.setTaskStatus(taskIndex, 'error', error);
|
|
198
200
|
}
|
|
199
201
|
});
|
|
200
|
-
const stoppedOnFailure = state.executionResult.cases.some((outcome)=>'failed' === outcome.status && !this.taskStatusList[outcome.caseIndex]?.continueOnError);
|
|
202
|
+
const stoppedOnFailure = state.executionResult.cases.some((outcome)=>'failed' === outcome.status && (!this.taskStatusList[outcome.caseIndex]?.continueOnError || isActionReadinessError(legacyYamlOutcomeError(outcome))));
|
|
201
203
|
this.setPlayerStatus(stoppedOnFailure || signal?.aborted ? 'error' : 'done');
|
|
202
204
|
}
|
|
203
205
|
} catch (error) {
|
|
@@ -281,10 +283,11 @@ class ScriptPlayer {
|
|
|
281
283
|
if (executionError) throw executionError;
|
|
282
284
|
if (cleanupError) throw cleanupError;
|
|
283
285
|
}
|
|
284
|
-
constructor(script, setupAgent, onTaskStatusChange, scriptPath){
|
|
286
|
+
constructor(script, setupAgent, onTaskStatusChange, scriptPath, abortSignal){
|
|
285
287
|
_define_property(this, "script", void 0);
|
|
286
288
|
_define_property(this, "setupAgent", void 0);
|
|
287
289
|
_define_property(this, "onTaskStatusChange", void 0);
|
|
290
|
+
_define_property(this, "abortSignal", void 0);
|
|
288
291
|
_define_property(this, "currentTaskIndex", void 0);
|
|
289
292
|
_define_property(this, "taskStatusList", void 0);
|
|
290
293
|
_define_property(this, "status", void 0);
|
|
@@ -303,6 +306,7 @@ class ScriptPlayer {
|
|
|
303
306
|
this.script = script;
|
|
304
307
|
this.setupAgent = setupAgent;
|
|
305
308
|
this.onTaskStatusChange = onTaskStatusChange;
|
|
309
|
+
this.abortSignal = abortSignal;
|
|
306
310
|
this.taskStatusList = [];
|
|
307
311
|
this.status = 'init';
|
|
308
312
|
this.unnamedResultIndex = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml/player.mjs","sources":["../../../src/yaml/player.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport type { Agent } from '@/agent/agent';\nimport type {\n DeviceAction,\n FreeFn,\n MidsceneYamlScript,\n MidsceneYamlScriptEnv,\n ScriptPlayerStatusValue,\n ScriptPlayerTaskStatus,\n} from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, ifInWorker, uuid } from '@midscene/shared/utils';\nimport type { WorkflowDocumentExecutionResult } from '../test-runner';\nimport {\n WorkflowExecutionFailure,\n WorkflowPublicationError,\n} from '../test-runner/engine/execution-failure';\nimport { cleanupResources } from '../test-runner/engine/resource-operations';\nimport { runFreeFnCleanup } from './cleanup';\nimport {\n YamlExecutionOwnershipError,\n currentYamlSignal,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from './execution-session';\nimport { createLegacyYamlExecutionRecord } from './legacy-yaml-execution-record';\nimport { publishLegacyYamlReport } from './legacy-yaml-report';\nimport {\n createLegacyYamlRuntime,\n legacyYamlOutcomeError,\n} from './legacy-yaml-runtime';\nimport {\n getLegacyYamlPlayerState,\n initializeLegacyYamlPlayerState,\n} from './player-state';\nimport { collectLegacyYamlDocument } from './test-runner-compat';\nimport { resolveWebTarget, resolveYamlOutputConfig } from './utils';\n\nconst debug = getDebug('yaml-player');\n\n/**\n * Compatibility facade for the legacy YAML API.\n *\n * Execution is delegated to the shared Runner kernel through\n * createLegacyYamlRuntime(). This class keeps the mutable status, result,\n * progress, output, and setup/cleanup behavior exposed by the old API.\n */\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\n public currentTaskIndex?: number;\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\n public status: ScriptPlayerStatusValue = 'init';\n public reportFile?: string | null;\n public result: Record<string, any>;\n private unnamedResultIndex = 0;\n private publicationErrors: WorkflowPublicationError[] = [];\n public output?: string | null;\n public unstableLogContent?: string | null;\n public errorInSetup?: Error;\n private interfaceAgent: Agent | null = null;\n public agentStatusTip?: string;\n public target?: MidsceneYamlScriptEnv;\n private actionSpace: DeviceAction[] = [];\n private scriptPath?: string;\n\n constructor(\n private script: MidsceneYamlScript,\n private setupAgent: (platform: T) => Promise<{\n agent: Agent;\n freeFn: FreeFn[];\n }>,\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\n scriptPath?: string,\n ) {\n this.scriptPath = scriptPath;\n // Collection validates syntax but defers old task/Step errors until execution.\n collectLegacyYamlDocument(script, scriptPath);\n initializeLegacyYamlPlayerState(this);\n this.result = {};\n\n this.target =\n resolveWebTarget(script)?.target ||\n script.android ||\n script.ios ||\n script.harmony ||\n script.computer ||\n script.config;\n const outputConfig = resolveYamlOutputConfig(script);\n\n if (ifInBrowser || ifInWorker) {\n this.output = undefined;\n debug('output is undefined in browser or worker');\n } else if (outputConfig.output) {\n this.output = resolve(process.cwd(), outputConfig.output);\n debug('setting output by config.output', this.output);\n } else {\n const scriptName = this.scriptPath\n ? basename(this.scriptPath, '.yaml').replace(/\\.(ya?ml)$/i, '')\n : 'script';\n this.output = join(\n getMidsceneRunSubDir('output'),\n `${scriptName}-${uuid()}.json`,\n );\n debug('setting output by script path', this.output);\n }\n\n if (ifInBrowser || ifInWorker) {\n this.unstableLogContent = undefined;\n } else if (typeof outputConfig.unstableLogContent === 'string') {\n this.unstableLogContent = resolve(\n process.cwd(),\n outputConfig.unstableLogContent,\n );\n } else if (outputConfig.unstableLogContent === true) {\n this.unstableLogContent = join(\n getMidsceneRunSubDir('output'),\n 'unstableLogContent.json',\n );\n }\n\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\n ...task,\n index: taskIndex,\n status: 'init',\n totalSteps: task.flow?.length || 0,\n }));\n }\n\n private setResult(key: string | undefined, value: any) {\n const keyToUse = key || this.unnamedResultIndex++;\n if (this.result[keyToUse]) {\n getDebug('yaml-player', { console: true })(\n `result key ${keyToUse} already exists, will overwrite`,\n );\n }\n this.result[keyToUse] = value;\n\n return this.flushResult();\n }\n\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\n this.status = status;\n this.errorInSetup = error;\n }\n\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\n const taskIndexToNotify =\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\n\n if (typeof taskIndexToNotify !== 'number') return;\n\n const taskStatus = this.taskStatusList[taskIndexToNotify];\n this.onTaskStatusChange?.(taskStatus);\n }\n\n private async setTaskStatus(\n index: number,\n statusValue: ScriptPlayerStatusValue,\n error?: Error,\n ) {\n this.taskStatusList[index].status = statusValue;\n if (error) this.taskStatusList[index].error = error;\n this.notifyCurrentTaskStatusChange(index);\n }\n\n private setTaskIndex(taskIndex: number) {\n this.currentTaskIndex = taskIndex;\n }\n\n private async flushResult() {\n if (this.output) {\n const output = resolve(process.cwd(), this.output);\n await this.publish(output, async () => {\n const outputDir = dirname(output);\n await mkdir(outputDir, { recursive: true });\n await writeFile(\n output,\n JSON.stringify(this.result || {}, undefined, 2),\n );\n });\n }\n }\n\n private async flushUnstableLogContent() {\n if (this.unstableLogContent) {\n const content = this.interfaceAgent?._unstableLogContent();\n const filePath = resolve(process.cwd(), this.unstableLogContent);\n await this.publish(filePath, async () => {\n const outputDir = dirname(filePath);\n await mkdir(outputDir, { recursive: true });\n await writeFile(filePath, JSON.stringify(content, null, 2));\n });\n }\n }\n\n private async publish(path: string, write: () => Promise<void>) {\n try {\n await write();\n } catch (cause) {\n const error = new WorkflowPublicationError('write-result', path, cause);\n this.publicationErrors.push(error);\n throw error;\n }\n }\n\n private createRuntime(agent: Agent) {\n return createLegacyYamlRuntime({\n agent,\n actionSpace: this.actionSpace,\n sourcePath: this.scriptPath,\n setResult: (key, value) => this.setResult(key, value),\n });\n }\n\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: Agent) {\n const { flow } = taskStatus;\n assert(flow, 'missing flow in task');\n const statusIndex = this.taskStatusList.indexOf(taskStatus);\n const declaredIndex = (\n taskStatus as ScriptPlayerTaskStatus & {\n index?: unknown;\n }\n ).index;\n const taskIndex =\n statusIndex >= 0\n ? statusIndex\n : typeof declaredIndex === 'number' && Number.isInteger(declaredIndex)\n ? declaredIndex\n : 0;\n\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = await this.createRuntime(agent).runTask(\n taskStatus,\n taskIndex,\n {\n onStepStart: (info) => {\n if (info.scope === 'case' && info.case.phase === 'steps') {\n taskStatus.currentStep = info.case.stepIndex;\n }\n },\n },\n );\n\n const outcome = state.executionResult.cases[0];\n if (!outcome || outcome.status === 'not-run') {\n throw new Error(`Task \"${taskStatus.name}\" did not run.`);\n }\n if (outcome.status === 'failed') throw legacyYamlOutcomeError(outcome);\n\n this.reportFile = agent.reportFile;\n await this.flushUnstableLogContent();\n }\n\n async run() {\n return runInYamlExecutionContext(() => this.runInContext());\n }\n\n private async runInContext() {\n const startedAt = new Date();\n const runId = uuid();\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = undefined;\n state.executionRecord = undefined;\n this.publicationErrors = [];\n const { android, ios, harmony, computer } = this.script;\n const webEnv = resolveWebTarget(this.script)?.target;\n const androidEnv = android;\n const iosEnv = ios;\n const harmonyEnv = harmony;\n const computerEnv = computer;\n const platform =\n webEnv || androidEnv || iosEnv || harmonyEnv || computerEnv;\n\n this.setPlayerStatus('running');\n\n let agent: Agent | null = null;\n let freeFn: FreeFn[] = [];\n let setupError: unknown;\n let executionError: unknown;\n let cleanupError: unknown;\n let signal: AbortSignal | undefined;\n let session: ReturnType<typeof enterYamlExecution> | undefined;\n try {\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\n platform as T,\n );\n // Register ownership immediately: discovering actions can fail too.\n agent = newAgent;\n agent._prepareForTestRunner?.();\n freeFn = [...(newFreeFn || [])];\n session = enterYamlExecution(agent);\n signal = currentYamlSignal(agent);\n signal?.throwIfAborted();\n this.actionSpace = await agent.getActionSpace();\n const originalOnTaskStartTip = agent.onTaskStartTip;\n agent.onTaskStartTip = (tip) => {\n if (this.status === 'running') this.agentStatusTip = tip;\n originalOnTaskStartTip?.(tip);\n };\n freeFn.push({\n name: 'restore-agent-onTaskStartTip',\n fn: () => {\n if (agent) agent.onTaskStartTip = originalOnTaskStartTip;\n },\n });\n } catch (error) {\n // A rejected borrower must not close or publish into another run's Agent.\n if (error instanceof YamlExecutionOwnershipError) throw error;\n setupError = error;\n this.setPlayerStatus('error', error as Error);\n }\n this.interfaceAgent = agent;\n\n const document = collectLegacyYamlDocument(\n this.script,\n this.scriptPath ?? '<inline-yaml>',\n this.actionSpace,\n );\n const runtime =\n agent && !setupError ? this.createRuntime(agent) : undefined;\n try {\n if (runtime && agent) {\n state.executionResult = await runtime.runScript(this.script, {\n document,\n signal,\n createDocumentRunId: () => runId,\n onCaseStart: (collectedCase) => {\n const taskIndex = collectedCase.caseIndex;\n this.setTaskIndex(taskIndex);\n return this.setTaskStatus(taskIndex, 'running');\n },\n onStepStart: (info) => {\n if (info.scope !== 'case' || info.case.phase !== 'steps') return;\n const taskStatus = this.taskStatusList[info.case.caseIndex];\n if (taskStatus) taskStatus.currentStep = info.case.stepIndex;\n },\n onCaseOutcome: async (outcome) => {\n if (outcome.status === 'not-run') return;\n const taskIndex = outcome.caseIndex;\n const taskStatus = this.taskStatusList[taskIndex];\n assert(taskStatus, `missing YAML task at index ${taskIndex}`);\n\n this.reportFile = agent?.reportFile;\n if (outcome.status === 'success') {\n await this.setTaskStatus(taskIndex, 'done');\n await this.flushUnstableLogContent();\n return;\n }\n\n const error = legacyYamlOutcomeError(outcome);\n await this.setTaskStatus(taskIndex, 'error', error);\n },\n });\n const stoppedOnFailure = state.executionResult.cases.some(\n (outcome) =>\n outcome.status === 'failed' &&\n !this.taskStatusList[outcome.caseIndex]?.continueOnError,\n );\n this.setPlayerStatus(\n stoppedOnFailure || signal?.aborted ? 'error' : 'done',\n );\n }\n } catch (error) {\n executionError = error;\n if (error instanceof WorkflowExecutionFailure)\n state.executionResult = error.result as WorkflowDocumentExecutionResult;\n this.setPlayerStatus('error', error as Error);\n }\n\n this.reportFile = agent?.reportFile;\n this.agentStatusTip = '';\n\n try {\n // A timeout settles the Runner before an uncooperative device action.\n // Never dispose its Agent while that action is still using it.\n if (session?.isRoot && agent) {\n await cleanupResources([agent], () => runFreeFnCleanup(freeFn), {\n owner: this,\n signal,\n onDeferredError: (error) =>\n getDebug('yaml-player', { console: true })(\n `Deferred YAML cleanup failed: ${String(error)}`,\n ),\n });\n } else await runFreeFnCleanup(freeFn);\n } catch (error) {\n cleanupError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? error) as Error,\n );\n }\n\n const endedAt = new Date();\n const cleanupErrors =\n cleanupError instanceof AggregateError\n ? cleanupError.errors\n : cleanupError === undefined\n ? []\n : [cleanupError];\n state.executionRecord = createLegacyYamlExecutionRecord({\n runId,\n attemptIndex: 0,\n script: this.script,\n document,\n startedAt,\n endedAt,\n execution: state.executionResult,\n setupError,\n executionError,\n cleanupErrors,\n aborted: signal?.aborted ?? false,\n publicationErrors: this.publicationErrors,\n outputs: this.result,\n reportFile: agent?.reportFile,\n children: session?.children,\n });\n let reportError: unknown;\n if (!session || session.isRoot) {\n try {\n const published = await publishLegacyYamlReport({\n agent,\n record: state.executionRecord,\n runId,\n script: this.script,\n scriptPath: this.scriptPath,\n fallbackReportFileName: state.fallbackReportFileName,\n });\n this.reportFile = published.reportFile;\n state.executionRecord = published.record;\n } catch (error) {\n reportError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? cleanupError ?? error) as Error,\n );\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError: error,\n });\n }\n }\n session?.finish(state.executionRecord);\n if (reportError) {\n const errors = [\n setupError,\n executionError,\n ...cleanupErrors,\n reportError,\n ].filter((error) => error !== undefined);\n throw errors.length === 1\n ? reportError\n : new AggregateError(\n errors,\n 'YAML execution/report finalization failed',\n );\n }\n if (executionError && cleanupError) {\n throw new AggregateError(\n [executionError, ...cleanupErrors],\n 'YAML execution and cleanup failed',\n );\n }\n if (executionError) throw executionError;\n if (cleanupError) throw cleanupError;\n }\n}\n"],"names":["debug","getDebug","ScriptPlayer","key","value","keyToUse","status","error","taskIndex","taskIndexToNotify","taskStatus","index","statusValue","output","resolve","process","outputDir","dirname","mkdir","writeFile","JSON","undefined","content","filePath","path","write","cause","WorkflowPublicationError","agent","createLegacyYamlRuntime","flow","assert","statusIndex","declaredIndex","Number","state","getLegacyYamlPlayerState","info","outcome","Error","legacyYamlOutcomeError","runInYamlExecutionContext","startedAt","Date","runId","uuid","android","ios","harmony","computer","webEnv","resolveWebTarget","androidEnv","iosEnv","harmonyEnv","computerEnv","platform","freeFn","setupError","executionError","cleanupError","signal","session","newAgent","newFreeFn","enterYamlExecution","currentYamlSignal","originalOnTaskStartTip","tip","YamlExecutionOwnershipError","document","collectLegacyYamlDocument","runtime","collectedCase","stoppedOnFailure","WorkflowExecutionFailure","cleanupResources","runFreeFnCleanup","String","endedAt","cleanupErrors","AggregateError","createLegacyYamlExecutionRecord","reportError","published","publishLegacyYamlReport","Object","errors","script","setupAgent","onTaskStatusChange","scriptPath","initializeLegacyYamlPlayerState","outputConfig","resolveYamlOutputConfig","ifInBrowser","ifInWorker","scriptName","basename","join","getMidsceneRunSubDir","task"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"yaml/player.mjs","sources":["../../../src/yaml/player.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { isActionReadinessError } from '@/agent/action-readiness';\nimport type { Agent } from '@/agent/agent';\nimport type {\n DeviceAction,\n FreeFn,\n MidsceneYamlScript,\n MidsceneYamlScriptEnv,\n ScriptPlayerStatusValue,\n ScriptPlayerTaskStatus,\n} from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, ifInWorker, uuid } from '@midscene/shared/utils';\nimport type { WorkflowDocumentExecutionResult } from '../test-runner';\nimport {\n WorkflowExecutionFailure,\n WorkflowPublicationError,\n} from '../test-runner/engine/execution-failure';\nimport { cleanupResources } from '../test-runner/engine/resource-operations';\nimport { runFreeFnCleanup } from './cleanup';\nimport {\n YamlExecutionOwnershipError,\n currentYamlSignal,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from './execution-session';\nimport { createLegacyYamlExecutionRecord } from './legacy-yaml-execution-record';\nimport { publishLegacyYamlReport } from './legacy-yaml-report';\nimport {\n createLegacyYamlRuntime,\n legacyYamlOutcomeError,\n} from './legacy-yaml-runtime';\nimport {\n getLegacyYamlPlayerState,\n initializeLegacyYamlPlayerState,\n} from './player-state';\nimport { collectLegacyYamlDocument } from './test-runner-compat';\nimport { resolveWebTarget, resolveYamlOutputConfig } from './utils';\n\nconst debug = getDebug('yaml-player');\n\n/**\n * Compatibility facade for the legacy YAML API.\n *\n * Execution is delegated to the shared Runner kernel through\n * createLegacyYamlRuntime(). This class keeps the mutable status, result,\n * progress, output, and setup/cleanup behavior exposed by the old API.\n */\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\n public currentTaskIndex?: number;\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\n public status: ScriptPlayerStatusValue = 'init';\n public reportFile?: string | null;\n public result: Record<string, any>;\n private unnamedResultIndex = 0;\n private publicationErrors: WorkflowPublicationError[] = [];\n public output?: string | null;\n public unstableLogContent?: string | null;\n public errorInSetup?: Error;\n private interfaceAgent: Agent | null = null;\n public agentStatusTip?: string;\n public target?: MidsceneYamlScriptEnv;\n private actionSpace: DeviceAction[] = [];\n private scriptPath?: string;\n\n constructor(\n private script: MidsceneYamlScript,\n private setupAgent: (platform: T) => Promise<{\n agent: Agent;\n freeFn: FreeFn[];\n }>,\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\n scriptPath?: string,\n private readonly abortSignal?: AbortSignal,\n ) {\n this.scriptPath = scriptPath;\n // Collection validates syntax but defers old task/Step errors until execution.\n collectLegacyYamlDocument(script, scriptPath);\n initializeLegacyYamlPlayerState(this);\n this.result = {};\n\n this.target =\n resolveWebTarget(script)?.target ||\n script.android ||\n script.ios ||\n script.harmony ||\n script.computer ||\n script.config;\n const outputConfig = resolveYamlOutputConfig(script);\n\n if (ifInBrowser || ifInWorker) {\n this.output = undefined;\n debug('output is undefined in browser or worker');\n } else if (outputConfig.output) {\n this.output = resolve(process.cwd(), outputConfig.output);\n debug('setting output by config.output', this.output);\n } else {\n const scriptName = this.scriptPath\n ? basename(this.scriptPath, '.yaml').replace(/\\.(ya?ml)$/i, '')\n : 'script';\n this.output = join(\n getMidsceneRunSubDir('output'),\n `${scriptName}-${uuid()}.json`,\n );\n debug('setting output by script path', this.output);\n }\n\n if (ifInBrowser || ifInWorker) {\n this.unstableLogContent = undefined;\n } else if (typeof outputConfig.unstableLogContent === 'string') {\n this.unstableLogContent = resolve(\n process.cwd(),\n outputConfig.unstableLogContent,\n );\n } else if (outputConfig.unstableLogContent === true) {\n this.unstableLogContent = join(\n getMidsceneRunSubDir('output'),\n 'unstableLogContent.json',\n );\n }\n\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\n ...task,\n index: taskIndex,\n status: 'init',\n totalSteps: task.flow?.length || 0,\n }));\n }\n\n private setResult(key: string | undefined, value: any) {\n const keyToUse = key || this.unnamedResultIndex++;\n if (this.result[keyToUse]) {\n getDebug('yaml-player', { console: true })(\n `result key ${keyToUse} already exists, will overwrite`,\n );\n }\n this.result[keyToUse] = value;\n\n return this.flushResult();\n }\n\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\n this.status = status;\n this.errorInSetup = error;\n }\n\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\n const taskIndexToNotify =\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\n\n if (typeof taskIndexToNotify !== 'number') return;\n\n const taskStatus = this.taskStatusList[taskIndexToNotify];\n this.onTaskStatusChange?.(taskStatus);\n }\n\n private async setTaskStatus(\n index: number,\n statusValue: ScriptPlayerStatusValue,\n error?: Error,\n ) {\n this.taskStatusList[index].status = statusValue;\n if (error) this.taskStatusList[index].error = error;\n this.notifyCurrentTaskStatusChange(index);\n }\n\n private setTaskIndex(taskIndex: number) {\n this.currentTaskIndex = taskIndex;\n }\n\n private async flushResult() {\n if (this.output) {\n const output = resolve(process.cwd(), this.output);\n await this.publish(output, async () => {\n const outputDir = dirname(output);\n await mkdir(outputDir, { recursive: true });\n await writeFile(\n output,\n JSON.stringify(this.result || {}, undefined, 2),\n );\n });\n }\n }\n\n private async flushUnstableLogContent() {\n if (this.unstableLogContent) {\n const content = this.interfaceAgent?._unstableLogContent();\n const filePath = resolve(process.cwd(), this.unstableLogContent);\n await this.publish(filePath, async () => {\n const outputDir = dirname(filePath);\n await mkdir(outputDir, { recursive: true });\n await writeFile(filePath, JSON.stringify(content, null, 2));\n });\n }\n }\n\n private async publish(path: string, write: () => Promise<void>) {\n try {\n await write();\n } catch (cause) {\n const error = new WorkflowPublicationError('write-result', path, cause);\n this.publicationErrors.push(error);\n throw error;\n }\n }\n\n private createRuntime(agent: Agent) {\n return createLegacyYamlRuntime({\n agent,\n actionSpace: this.actionSpace,\n sourcePath: this.scriptPath,\n setResult: (key, value) => this.setResult(key, value),\n });\n }\n\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: Agent) {\n const { flow } = taskStatus;\n assert(flow, 'missing flow in task');\n const statusIndex = this.taskStatusList.indexOf(taskStatus);\n const declaredIndex = (\n taskStatus as ScriptPlayerTaskStatus & {\n index?: unknown;\n }\n ).index;\n const taskIndex =\n statusIndex >= 0\n ? statusIndex\n : typeof declaredIndex === 'number' && Number.isInteger(declaredIndex)\n ? declaredIndex\n : 0;\n\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = await this.createRuntime(agent).runTask(\n taskStatus,\n taskIndex,\n {\n signal: this.abortSignal ?? currentYamlSignal(agent),\n onStepStart: (info) => {\n if (info.scope === 'case' && info.case.phase === 'steps') {\n taskStatus.currentStep = info.case.stepIndex;\n }\n },\n },\n );\n\n const outcome = state.executionResult.cases[0];\n if (!outcome || outcome.status === 'not-run') {\n throw new Error(`Task \"${taskStatus.name}\" did not run.`);\n }\n if (outcome.status === 'failed') throw legacyYamlOutcomeError(outcome);\n\n this.reportFile = agent.reportFile;\n await this.flushUnstableLogContent();\n }\n\n async run() {\n return runInYamlExecutionContext(() => this.runInContext());\n }\n\n private async runInContext() {\n const startedAt = new Date();\n const runId = uuid();\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = undefined;\n state.executionRecord = undefined;\n this.publicationErrors = [];\n const { android, ios, harmony, computer } = this.script;\n const webEnv = resolveWebTarget(this.script)?.target;\n const androidEnv = android;\n const iosEnv = ios;\n const harmonyEnv = harmony;\n const computerEnv = computer;\n const platform =\n webEnv || androidEnv || iosEnv || harmonyEnv || computerEnv;\n\n this.setPlayerStatus('running');\n\n let agent: Agent | null = null;\n let freeFn: FreeFn[] = [];\n let setupError: unknown;\n let executionError: unknown;\n let cleanupError: unknown;\n let signal: AbortSignal | undefined;\n let session: ReturnType<typeof enterYamlExecution> | undefined;\n try {\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\n platform as T,\n );\n // Register ownership immediately: discovering actions can fail too.\n agent = newAgent;\n agent._prepareForTestRunner?.();\n freeFn = [...(newFreeFn || [])];\n session = enterYamlExecution(agent);\n signal = this.abortSignal ?? currentYamlSignal(agent);\n signal?.throwIfAborted();\n this.actionSpace = await agent.getActionSpace();\n const originalOnTaskStartTip = agent.onTaskStartTip;\n agent.onTaskStartTip = (tip) => {\n if (this.status === 'running') this.agentStatusTip = tip;\n originalOnTaskStartTip?.(tip);\n };\n freeFn.push({\n name: 'restore-agent-onTaskStartTip',\n fn: () => {\n if (agent) agent.onTaskStartTip = originalOnTaskStartTip;\n },\n });\n } catch (error) {\n // A rejected borrower must not close or publish into another run's Agent.\n if (error instanceof YamlExecutionOwnershipError) throw error;\n setupError = error;\n this.setPlayerStatus('error', error as Error);\n }\n this.interfaceAgent = agent;\n\n const document = collectLegacyYamlDocument(\n this.script,\n this.scriptPath ?? '<inline-yaml>',\n this.actionSpace,\n );\n const runtime =\n agent && !setupError ? this.createRuntime(agent) : undefined;\n try {\n if (runtime && agent) {\n state.executionResult = await runtime.runScript(this.script, {\n document,\n signal,\n createDocumentRunId: () => runId,\n onCaseStart: (collectedCase) => {\n const taskIndex = collectedCase.caseIndex;\n this.setTaskIndex(taskIndex);\n return this.setTaskStatus(taskIndex, 'running');\n },\n onStepStart: (info) => {\n if (info.scope !== 'case' || info.case.phase !== 'steps') return;\n const taskStatus = this.taskStatusList[info.case.caseIndex];\n if (taskStatus) taskStatus.currentStep = info.case.stepIndex;\n },\n onCaseOutcome: async (outcome) => {\n if (outcome.status === 'not-run') return;\n const taskIndex = outcome.caseIndex;\n const taskStatus = this.taskStatusList[taskIndex];\n assert(taskStatus, `missing YAML task at index ${taskIndex}`);\n\n this.reportFile = agent?.reportFile;\n if (outcome.status === 'success') {\n await this.setTaskStatus(taskIndex, 'done');\n await this.flushUnstableLogContent();\n return;\n }\n\n const error = legacyYamlOutcomeError(outcome);\n await this.setTaskStatus(taskIndex, 'error', error);\n },\n });\n const stoppedOnFailure = state.executionResult.cases.some(\n (outcome) =>\n outcome.status === 'failed' &&\n (!this.taskStatusList[outcome.caseIndex]?.continueOnError ||\n isActionReadinessError(legacyYamlOutcomeError(outcome))),\n );\n this.setPlayerStatus(\n stoppedOnFailure || signal?.aborted ? 'error' : 'done',\n );\n }\n } catch (error) {\n executionError = error;\n if (error instanceof WorkflowExecutionFailure)\n state.executionResult = error.result as WorkflowDocumentExecutionResult;\n this.setPlayerStatus('error', error as Error);\n }\n\n this.reportFile = agent?.reportFile;\n this.agentStatusTip = '';\n\n try {\n // A timeout settles the Runner before an uncooperative device action.\n // Never dispose its Agent while that action is still using it.\n if (session?.isRoot && agent) {\n await cleanupResources([agent], () => runFreeFnCleanup(freeFn), {\n owner: this,\n signal,\n onDeferredError: (error) =>\n getDebug('yaml-player', { console: true })(\n `Deferred YAML cleanup failed: ${String(error)}`,\n ),\n });\n } else await runFreeFnCleanup(freeFn);\n } catch (error) {\n cleanupError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? error) as Error,\n );\n }\n\n const endedAt = new Date();\n const cleanupErrors =\n cleanupError instanceof AggregateError\n ? cleanupError.errors\n : cleanupError === undefined\n ? []\n : [cleanupError];\n state.executionRecord = createLegacyYamlExecutionRecord({\n runId,\n attemptIndex: 0,\n script: this.script,\n document,\n startedAt,\n endedAt,\n execution: state.executionResult,\n setupError,\n executionError,\n cleanupErrors,\n aborted: signal?.aborted ?? false,\n publicationErrors: this.publicationErrors,\n outputs: this.result,\n reportFile: agent?.reportFile,\n children: session?.children,\n });\n let reportError: unknown;\n if (!session || session.isRoot) {\n try {\n const published = await publishLegacyYamlReport({\n agent,\n record: state.executionRecord,\n runId,\n script: this.script,\n scriptPath: this.scriptPath,\n fallbackReportFileName: state.fallbackReportFileName,\n });\n this.reportFile = published.reportFile;\n state.executionRecord = published.record;\n } catch (error) {\n reportError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? cleanupError ?? error) as Error,\n );\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError: error,\n });\n }\n }\n session?.finish(state.executionRecord);\n if (reportError) {\n const errors = [\n setupError,\n executionError,\n ...cleanupErrors,\n reportError,\n ].filter((error) => error !== undefined);\n throw errors.length === 1\n ? reportError\n : new AggregateError(\n errors,\n 'YAML execution/report finalization failed',\n );\n }\n if (executionError && cleanupError) {\n throw new AggregateError(\n [executionError, ...cleanupErrors],\n 'YAML execution and cleanup failed',\n );\n }\n if (executionError) throw executionError;\n if (cleanupError) throw cleanupError;\n }\n}\n"],"names":["debug","getDebug","ScriptPlayer","key","value","keyToUse","status","error","taskIndex","taskIndexToNotify","taskStatus","index","statusValue","output","resolve","process","outputDir","dirname","mkdir","writeFile","JSON","undefined","content","filePath","path","write","cause","WorkflowPublicationError","agent","createLegacyYamlRuntime","flow","assert","statusIndex","declaredIndex","Number","state","getLegacyYamlPlayerState","currentYamlSignal","info","outcome","Error","legacyYamlOutcomeError","runInYamlExecutionContext","startedAt","Date","runId","uuid","android","ios","harmony","computer","webEnv","resolveWebTarget","androidEnv","iosEnv","harmonyEnv","computerEnv","platform","freeFn","setupError","executionError","cleanupError","signal","session","newAgent","newFreeFn","enterYamlExecution","originalOnTaskStartTip","tip","YamlExecutionOwnershipError","document","collectLegacyYamlDocument","runtime","collectedCase","stoppedOnFailure","isActionReadinessError","WorkflowExecutionFailure","cleanupResources","runFreeFnCleanup","String","endedAt","cleanupErrors","AggregateError","createLegacyYamlExecutionRecord","reportError","published","publishLegacyYamlReport","Object","errors","script","setupAgent","onTaskStatusChange","scriptPath","abortSignal","initializeLegacyYamlPlayerState","outputConfig","resolveYamlOutputConfig","ifInBrowser","ifInWorker","scriptName","basename","join","getMidsceneRunSubDir","task"],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
isActionReadinessError: ()=>isActionReadinessError,
|
|
28
|
+
ActionReadinessError: ()=>ActionReadinessError,
|
|
29
|
+
ActionReadiness: ()=>ActionReadiness
|
|
30
|
+
});
|
|
31
|
+
const error_formatter_namespaceObject = require("@midscene/shared/agent-tools/error-formatter");
|
|
32
|
+
const logger_namespaceObject = require("@midscene/shared/logger");
|
|
33
|
+
function _define_property(obj, key, value) {
|
|
34
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
35
|
+
value: value,
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true
|
|
39
|
+
});
|
|
40
|
+
else obj[key] = value;
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
const warn = (0, logger_namespaceObject.getDebug)('agent:action-readiness', {
|
|
44
|
+
console: true
|
|
45
|
+
});
|
|
46
|
+
const errorCode = 'ACTION_READINESS_FAILED';
|
|
47
|
+
class ActionReadinessError extends Error {
|
|
48
|
+
constructor(phase, actionName, cause){
|
|
49
|
+
super(`waitForActionReady ${phase} failed for ${actionName}: ${(0, error_formatter_namespaceObject.getErrorMessage)(cause)}`, {
|
|
50
|
+
cause
|
|
51
|
+
}), _define_property(this, "code", errorCode);
|
|
52
|
+
this.name = 'ActionReadinessError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isActionReadinessError(error) {
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
while(error && 'object' == typeof error && !seen.has(error)){
|
|
58
|
+
seen.add(error);
|
|
59
|
+
const value = error;
|
|
60
|
+
if (value.code === errorCode) return true;
|
|
61
|
+
error = value.cause;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
class ActionReadiness {
|
|
66
|
+
async wait(action, signal) {
|
|
67
|
+
signal?.throwIfAborted();
|
|
68
|
+
let onAbort;
|
|
69
|
+
const timer = setTimeout(()=>{
|
|
70
|
+
warn(`waitForActionReady for ${action.name} (${action.id}) is still pending after 5000ms; continuing to wait.`);
|
|
71
|
+
}, 5000);
|
|
72
|
+
try {
|
|
73
|
+
await new Promise((resolve, reject)=>{
|
|
74
|
+
if (signal) {
|
|
75
|
+
onAbort = ()=>reject(signal.reason);
|
|
76
|
+
signal.addEventListener('abort', onAbort, {
|
|
77
|
+
once: true
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
Promise.resolve(this.callback({
|
|
81
|
+
action,
|
|
82
|
+
signal
|
|
83
|
+
})).then(resolve, reject);
|
|
84
|
+
});
|
|
85
|
+
signal?.throwIfAborted();
|
|
86
|
+
} catch (cause) {
|
|
87
|
+
signal?.throwIfAborted();
|
|
88
|
+
throw new ActionReadinessError('wait', action.name, cause);
|
|
89
|
+
} finally{
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
if (signal && onAbort) signal.removeEventListener('abort', onAbort);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
constructor(callback){
|
|
95
|
+
_define_property(this, "callback", void 0);
|
|
96
|
+
this.callback = callback;
|
|
97
|
+
if ('function' != typeof callback) throw new Error('waitForActionReady must be a function');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.ActionReadiness = __webpack_exports__.ActionReadiness;
|
|
101
|
+
exports.ActionReadinessError = __webpack_exports__.ActionReadinessError;
|
|
102
|
+
exports.isActionReadinessError = __webpack_exports__.isActionReadinessError;
|
|
103
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
104
|
+
"ActionReadiness",
|
|
105
|
+
"ActionReadinessError",
|
|
106
|
+
"isActionReadinessError"
|
|
107
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
108
|
+
Object.defineProperty(exports, '__esModule', {
|
|
109
|
+
value: true
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
//# sourceMappingURL=action-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent/action-readiness.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/agent/action-readiness.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { ActionReadyContext, WaitForActionReady } from '@/types';\nimport { getErrorMessage } from '@midscene/shared/agent-tools/error-formatter';\nimport { getDebug } from '@midscene/shared/logger';\n\nconst warn = getDebug('agent:action-readiness', { console: true });\nconst errorCode = 'ACTION_READINESS_FAILED';\n\nexport class ActionReadinessError extends Error {\n readonly code = errorCode;\n\n constructor(phase: string, actionName: string, cause: unknown) {\n super(\n `waitForActionReady ${phase} failed for ${actionName}: ${getErrorMessage(cause)}`,\n { cause },\n );\n this.name = 'ActionReadinessError';\n }\n}\n\n/** Also recognizes the bounded cause retained by TaskExecutionError. */\nexport function isActionReadinessError(error: unknown): boolean {\n const seen = new Set<unknown>();\n while (error && typeof error === 'object' && !seen.has(error)) {\n seen.add(error);\n const value = error as { code?: unknown; cause?: unknown };\n if (value.code === errorCode) return true;\n error = value.cause;\n }\n return false;\n}\n\n/** Await application readiness after a successful action; slow waits only warn. */\nexport class ActionReadiness {\n constructor(private readonly callback: WaitForActionReady) {\n if (typeof callback !== 'function') {\n throw new Error('waitForActionReady must be a function');\n }\n }\n\n async wait(\n action: ActionReadyContext['action'],\n signal?: AbortSignal,\n ): Promise<void> {\n signal?.throwIfAborted();\n let onAbort: (() => void) | undefined;\n const timer = setTimeout(() => {\n warn(\n `waitForActionReady for ${action.name} (${action.id}) is still pending after 5000ms; continuing to wait.`,\n );\n }, 5000);\n try {\n await new Promise<void>((resolve, reject) => {\n // Listen before invoking user code, including callbacks that abort\n // synchronously or never settle. Both late outcomes stay handled.\n if (signal) {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n }\n Promise.resolve(this.callback({ action, signal })).then(\n resolve,\n reject,\n );\n });\n signal?.throwIfAborted();\n } catch (cause) {\n // Preserve the cancellation reason instead of reporting a readiness failure.\n signal?.throwIfAborted();\n throw new ActionReadinessError('wait', action.name, cause);\n } finally {\n clearTimeout(timer);\n if (signal && onAbort) signal.removeEventListener('abort', onAbort);\n }\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","warn","getDebug","errorCode","ActionReadinessError","Error","phase","actionName","cause","getErrorMessage","isActionReadinessError","error","seen","Set","value","ActionReadiness","action","signal","onAbort","timer","setTimeout","Promise","resolve","reject","clearTimeout","callback"],"mappings":";;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;ACFA,MAAMI,OAAOC,AAAAA,IAAAA,uBAAAA,QAAAA,AAAAA,EAAS,0BAA0B;IAAE,SAAS;AAAK;AAChE,MAAMC,YAAY;AAEX,MAAMC,6BAA6BC;IAGxC,YAAYC,KAAa,EAAEC,UAAkB,EAAEC,KAAc,CAAE;QAC7D,KAAK,CACH,CAAC,mBAAmB,EAAEF,MAAM,YAAY,EAAEC,WAAW,EAAE,EAAEE,AAAAA,IAAAA,gCAAAA,eAAAA,AAAAA,EAAgBD,QAAQ,EACjF;YAAEA;QAAM,IALZ,uBAAS,QAAOL;QAOd,IAAI,CAAC,IAAI,GAAG;IACd;AACF;AAGO,SAASO,uBAAuBC,KAAc;IACnD,MAAMC,OAAO,IAAIC;IACjB,MAAOF,SAAS,AAAiB,YAAjB,OAAOA,SAAsB,CAACC,KAAK,GAAG,CAACD,OAAQ;QAC7DC,KAAK,GAAG,CAACD;QACT,MAAMG,QAAQH;QACd,IAAIG,MAAM,IAAI,KAAKX,WAAW,OAAO;QACrCQ,QAAQG,MAAM,KAAK;IACrB;IACA,OAAO;AACT;AAGO,MAAMC;IAOX,MAAM,KACJC,MAAoC,EACpCC,MAAoB,EACL;QACfA,QAAQ;QACR,IAAIC;QACJ,MAAMC,QAAQC,WAAW;YACvBnB,KACE,CAAC,uBAAuB,EAAEe,OAAO,IAAI,CAAC,EAAE,EAAEA,OAAO,EAAE,CAAC,oDAAoD,CAAC;QAE7G,GAAG;QACH,IAAI;YACF,MAAM,IAAIK,QAAc,CAACC,SAASC;gBAGhC,IAAIN,QAAQ;oBACVC,UAAU,IAAMK,OAAON,OAAO,MAAM;oBACpCA,OAAO,gBAAgB,CAAC,SAASC,SAAS;wBAAE,MAAM;oBAAK;gBACzD;gBACAG,QAAQ,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAEL;oBAAQC;gBAAO,IAAI,IAAI,CACrDK,SACAC;YAEJ;YACAN,QAAQ;QACV,EAAE,OAAOT,OAAO;YAEdS,QAAQ;YACR,MAAM,IAAIb,qBAAqB,QAAQY,OAAO,IAAI,EAAER;QACtD,SAAU;YACRgB,aAAaL;YACb,IAAIF,UAAUC,SAASD,OAAO,mBAAmB,CAAC,SAASC;QAC7D;IACF;IAvCA,YAA6BO,QAA4B,CAAE;;aAA9BA,QAAQ,GAARA;QAC3B,IAAI,AAAoB,cAApB,OAAOA,UACT,MAAM,IAAIpB,MAAM;IAEpB;AAoCF"}
|