@vmosedge/workflow-agent-sdk 0.1.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/README.md ADDED
@@ -0,0 +1,288 @@
1
+ # @vmosedge/workflow-agent-sdk
2
+
3
+ VMOS Edge 自动化 SDK,用于把“目标描述 -> 设备执行 -> 工作流脚本”串成一条完整链路。
4
+
5
+ SDK 内置任务规划、运行时可靠性策略、MCP 工具接入和脚本生成能力,适合 Android/云手机自动化、录制回放和 Agent 执行场景。
6
+
7
+ ## 能力概览
8
+
9
+ - 目标规划:先拆解任务,再进入执行阶段
10
+ - 设备执行:内置本地设备工具,并支持挂接 MCP server 扩展能力
11
+ - 可靠性控制:内置重试、超时、收敛限制和动作后验证 gate
12
+ - 脚本生成:基于 execution log 生成结构化 workflow script
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pnpm add @vmosedge/workflow-agent-sdk
18
+ ```
19
+
20
+ 要求:
21
+
22
+ - Node.js >= 20
23
+ - 需要可访问的模型接口,支持 OpenAI-compatible、Anthropic Claude、Google Gemini
24
+ - 需要设备连接信息:`hostIp` + `deviceId`
25
+
26
+ ## 导出说明
27
+
28
+ - `@vmosedge/workflow-agent-sdk`
29
+ - 轻量导出,包含 `generateScript` 和基础类型
30
+ - `@vmosedge/workflow-agent-sdk/runtime`
31
+ - 完整 runtime 能力,包含 `createAutomationAgentRuntime`、`AutomationAgentRuntime`、默认可靠性配置和错误类型
32
+ - `@vmosedge/workflow-agent-sdk/types`
33
+ - 全量类型定义,适合深度集成
34
+
35
+ ## 快速开始:直接生成脚本
36
+
37
+ 适用于你已经有 execution log,只需要生成 workflow script 的场景。
38
+
39
+ ```ts
40
+ import {
41
+ generateScript,
42
+ type AIProviderConfig,
43
+ type ExecutionLogEntry
44
+ } from '@vmosedge/workflow-agent-sdk'
45
+
46
+ const provider: AIProviderConfig = {
47
+ vendor: 'openai',
48
+ baseUrl: 'https://api.openai.com/v1',
49
+ apiKey: process.env.OPENAI_API_KEY!,
50
+ model: 'gpt-4o-mini'
51
+ }
52
+
53
+ const executionLog: ExecutionLogEntry[] = [
54
+ {
55
+ index: 1,
56
+ toolName: 'start_app',
57
+ arguments: {
58
+ package_name: 'com.example.app'
59
+ },
60
+ result: {
61
+ toolCallId: 'call_1',
62
+ name: 'start_app',
63
+ success: true
64
+ },
65
+ category: 'action',
66
+ timestamp: Date.now()
67
+ }
68
+ ]
69
+
70
+ const workflow = await generateScript(
71
+ '打开目标应用并进入首页',
72
+ executionLog,
73
+ provider
74
+ )
75
+
76
+ console.log(JSON.stringify(workflow, null, 2))
77
+ ```
78
+
79
+ ## 快速开始:使用 runtime 跑完整流程
80
+
81
+ 适用于你希望从自然语言目标出发,先规划、再执行、最后生成脚本的场景。
82
+
83
+ ```ts
84
+ import { createAutomationAgentRuntime } from '@vmosedge/workflow-agent-sdk/runtime'
85
+ import type { AIProviderConfig } from '@vmosedge/workflow-agent-sdk'
86
+
87
+ const provider: AIProviderConfig = {
88
+ vendor: 'openai',
89
+ baseUrl: 'https://api.openai.com/v1',
90
+ apiKey: process.env.OPENAI_API_KEY!,
91
+ model: 'gpt-4o-mini'
92
+ }
93
+
94
+ const runtime = createAutomationAgentRuntime({
95
+ mcpServers: {
96
+ knowledge: {
97
+ transport: 'streamable_http',
98
+ url: 'https://mcp.example.com'
99
+ }
100
+ }
101
+ })
102
+
103
+ runtime.on('planning', (data) => {
104
+ console.log('[planning]', data.status, data.plan)
105
+ })
106
+
107
+ runtime.on('thinking', (data) => {
108
+ console.log('[thinking]', data.text)
109
+ })
110
+
111
+ runtime.on('toolcall', (data) => {
112
+ console.log('[toolcall]', data.toolCall.name, data.toolCall.arguments)
113
+ })
114
+
115
+ runtime.on('toolresult', (data) => {
116
+ console.log('[toolresult]', data.result.name, data.result.success)
117
+ })
118
+
119
+ runtime.on('paused', async (data) => {
120
+ console.log('[paused]', data.text)
121
+
122
+ // 需要人工补充信息时,可恢复继续执行
123
+ // await runtime.resume({
124
+ // sessionId: data.sessionId,
125
+ // message: '继续执行,目标是进入搜索页'
126
+ // })
127
+ })
128
+
129
+ runtime.on('error', (data) => {
130
+ console.error('[error]', data.code, data.error, data.details)
131
+ })
132
+
133
+ const { sessionId } = await runtime.start({
134
+ goal: '打开目标应用并进入搜索页',
135
+ provider,
136
+ device: {
137
+ hostIp: '192.168.50.10',
138
+ deviceId: 'emulator-5554'
139
+ }
140
+ })
141
+
142
+ // 当 session 已暂停,且已经产生可用 execution log 后:
143
+ const result = await runtime.generateScript(sessionId)
144
+
145
+ console.log(JSON.stringify(result.workflow, null, 2))
146
+ console.log(result.diagnostics)
147
+
148
+ await runtime.destroy()
149
+ ```
150
+
151
+ 典型调用顺序:
152
+
153
+ 1. `start()` 启动一次执行
154
+ 2. 如果 Agent 主动暂停并要求补充信息,调用 `resume()`
155
+ 3. 当 session 已暂停且执行日志可用时,调用 `generateScript()`
156
+ 4. 结束时调用 `destroy()`
157
+
158
+ ## Runtime 事件
159
+
160
+ | 事件名 | 说明 |
161
+ | --- | --- |
162
+ | `planning` | 任务规划开始或完成 |
163
+ | `thinking` | 模型当前可见思考文本 |
164
+ | `toolcall` | 发起工具调用 |
165
+ | `toolresult` | 收到工具执行结果 |
166
+ | `diagnostic` | 可靠性策略、重试、收敛等诊断信息 |
167
+ | `paused` | 当前轮次暂停,等待人工补充或外部决策 |
168
+ | `scriptgenerating` | 开始生成 workflow script |
169
+ | `complete` | `generateScript()` 成功完成后触发 |
170
+ | `error` | 运行时错误 |
171
+
172
+ ## MCP Server 配置
173
+
174
+ 支持 `stdio`、`http`、`streamable_http` 和 `sse` 四类传输方式。
175
+
176
+ ### `stdio` 示例
177
+
178
+ ```ts
179
+ const runtime = createAutomationAgentRuntime({
180
+ mcpServers: {
181
+ filesystem: {
182
+ transport: 'stdio',
183
+ command: 'npx',
184
+ args: ['-y', '@modelcontextprotocol/server-filesystem', './workspace']
185
+ }
186
+ }
187
+ })
188
+ ```
189
+
190
+ ### HTTP 示例
191
+
192
+ ```ts
193
+ const runtime = createAutomationAgentRuntime({
194
+ mcpServers: {
195
+ remoteKnowledge: {
196
+ transport: 'streamable_http',
197
+ url: 'https://mcp.example.com',
198
+ headers: {
199
+ Authorization: `Bearer ${process.env.MCP_TOKEN}`
200
+ }
201
+ }
202
+ }
203
+ })
204
+ ```
205
+
206
+ ## 常用接口
207
+
208
+ - `createAutomationAgentRuntime(options)`:创建 runtime 实例
209
+ - `runtime.start(params)`:启动一次完整任务执行,返回 `sessionId`
210
+ - `runtime.resume(params)`:在任务暂停后继续执行
211
+ - `runtime.generateScript(sessionId)`:基于当前 session 的 execution log 生成 workflow script
212
+ - `runtime.stop(sessionId)`:停止当前 session
213
+ - `runtime.destroy()`:释放 runtime 占用资源
214
+ - `runtime.on(event, handler)` / `runtime.off(event, handler)`:订阅或取消订阅事件
215
+
216
+ ## Provider 配置
217
+
218
+ SDK 现在支持两类模型接入方式:
219
+
220
+ - OpenAI-compatible:`vendor` 使用 `openai` / `deepseek` / `azure` / `custom`,需要提供 `baseUrl`
221
+ - Native provider:`vendor` 使用 `anthropic` / `claude` 或 `google` / `gemini`,分别走 Claude / Gemini 官方接口,`baseUrl` 可选
222
+
223
+ 如果你拿到的是 OpenAI-compatible 聚合网关,即使底层模型实际是 Claude 或 Gemini,也应该继续使用 `openai` 或 `custom` 并填写 `baseUrl`。
224
+
225
+ ### OpenAI-compatible 示例
226
+
227
+ ```ts
228
+ import type { AIProviderConfig } from '@vmosedge/workflow-agent-sdk'
229
+
230
+ const provider: AIProviderConfig = {
231
+ vendor: 'openai',
232
+ baseUrl: 'https://api.openai.com/v1',
233
+ apiKey: process.env.OPENAI_API_KEY!,
234
+ model: 'gpt-4o-mini'
235
+ }
236
+ ```
237
+
238
+ ### Claude 示例
239
+
240
+ ```ts
241
+ import type { AIProviderConfig } from '@vmosedge/workflow-agent-sdk'
242
+
243
+ const provider: AIProviderConfig = {
244
+ vendor: 'claude',
245
+ apiKey: process.env.ANTHROPIC_API_KEY!,
246
+ model: 'claude-model-id'
247
+ }
248
+ ```
249
+
250
+ ### Gemini 示例
251
+
252
+ ```ts
253
+ import type { AIProviderConfig } from '@vmosedge/workflow-agent-sdk'
254
+
255
+ const provider: AIProviderConfig = {
256
+ vendor: 'gemini',
257
+ apiKey: process.env.GOOGLE_API_KEY!,
258
+ model: 'gemini-model-id'
259
+ }
260
+ ```
261
+
262
+ ## 可靠性策略
263
+
264
+ 内置默认策略包括:
265
+
266
+ - observation 工具最多重试 3 次
267
+ - action 工具最多重试 2 次
268
+ - 默认工具超时 12 秒
269
+ - 最大迭代次数 120
270
+ - 连续失败和重复工具调用达到阈值时自动暂停
271
+
272
+ 如需调优,可在 `start()` 时传入 `reliability` 覆盖默认值。
273
+
274
+ ## 使用说明
275
+
276
+ - `generateScript(sessionId)` 只有在 session 不再运行、execution log 非空且不存在待验证动作时才能成功
277
+ - `vendor` 会直接决定底层 provider:
278
+ - `openai` / `deepseek` / `azure` / `custom`:按 OpenAI-compatible 方式接入
279
+ - `anthropic` / `claude`:按 Claude 官方接口接入
280
+ - `google` / `gemini`:按 Gemini 官方接口接入
281
+
282
+ ## 推荐发布前自检
283
+
284
+ ```bash
285
+ pnpm --filter @vmosedge/workflow-agent-sdk test
286
+ pnpm --filter @vmosedge/workflow-agent-sdk build
287
+ cd packages/automation-sdk && npm pack --dry-run
288
+ ```
@@ -0,0 +1 @@
1
+ "use strict";var e=require("@langchain/core/prompts"),t=require("@langchain/core/output_parsers"),n=require("@langchain/core/runnables"),i=require("zod"),o=require("@langchain/anthropic"),r=require("@langchain/core/messages"),a=require("@langchain/core/tools"),s=require("@langchain/google-genai"),c=require("@langchain/openai"),l=require("crypto");function p(e){return e.map(e=>{if("assistant"===e.role){const t=(e.toolCalls||[]).map(e=>({type:"tool_call",id:e.id,name:e.name,args:e.arguments}));return r.coerceMessageLikeToMessage({role:"assistant",content:e.content,...t.length>0?{tool_calls:t}:{}})}return"tool"===e.role?r.coerceMessageLikeToMessage({role:"tool",content:e.content,tool_call_id:e.toolCallId||""}):r.coerceMessageLikeToMessage({role:e.role,content:e.content})})}function d(e){return a.tool(async()=>"schema-only tool placeholder",{name:e.name,description:e.description,schema:e.schema})}function u(e,t){if("string"!=typeof e||0===e.trim().length)throw new Error(`${t} is required`)}function _(e,t,n){return"anthropic"===e?function(e,t){return new o.ChatAnthropic({model:e.model,apiKey:e.apiKey,temperature:.2,...e.baseUrl?{anthropicApiUrl:e.baseUrl}:{},...Object.keys(t).length>0?{clientOptions:{defaultHeaders:t}}:{}})}(t,n):"google"===e?function(e,t){return new s.ChatGoogleGenerativeAI({model:e.model,apiKey:e.apiKey,temperature:.2,...e.baseUrl?{baseUrl:e.baseUrl}:{},...Object.keys(t).length>0?{customHeaders:t}:{}})}(t,n):function(e,t){return new c.ChatOpenAI({model:e.model,apiKey:e.apiKey,temperature:.2,configuration:{...e.baseUrl?{baseURL:e.baseUrl}:{},...Object.keys(t).length>0?{defaultHeaders:t}:{}}})}(t,n)}class m{capabilities;vendor;model;constructor(e){const t={...e.headers||{}},n=function(e){const t=function(e){const t=(e||"").toLowerCase();return t.includes("deepseek")?"deepseek":t.includes("openai")?"openai":t.includes("custom")||t.includes("other")?"custom":t.includes("anthropic")||t.includes("claude")?"anthropic":t.includes("google")||t.includes("gemini")?"google":t.includes("azure")?"azure":"openai"}(String(e.vendor));return u(e.apiKey,"provider.apiKey"),u(e.model,"provider.model"),"anthropic"===t||"google"===t||u(e.baseUrl,"provider.baseUrl"),t}(e);this.vendor=n,this.capabilities=function(e){return{toolCalling:!0,structuredJson:!0,jsonMode:"openai"===e||"deepseek"===e||"azure"===e||"custom"===e}}(n),this.model=_(n,e,t)}getModel(){return this.model}async chatWithTools(e,t,n){const i=p(e),o=function(e){return e.map(e=>function(e){if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.name&&"string"==typeof t.description&&!!t.schema&&"object"==typeof t.schema}(e)?d(e):e)}(t),r=o.length>0&&"function"==typeof this.model.bindTools?this.model.bindTools(o):this.model,a=await r.invoke(i,{signal:n}),s="string"==typeof(c=a).text&&c.text.length>0?c.text:"string"==typeof c.content?c.content:Array.isArray(c.content)?c.content.map(e=>"string"==typeof e?e:e&&"object"==typeof e&&"text"in e&&"string"==typeof e.text?e.text:"").filter(e=>e.length>0).join("\n"):"";var c;const l=a.tool_calls||[],u=a.response_metadata&&"object"==typeof a.response_metadata?a.response_metadata:null;return{content:s,toolCalls:l.filter(e=>"string"==typeof e.name&&e.name.length>0).map((e,t)=>{return{id:"string"==typeof e.id&&e.id.length>0?e.id:`call_${Date.now()}_${t}`,name:e.name,arguments:(n=e.args,n&&"object"==typeof n&&!Array.isArray(n)?n:{})};var n}),finishReason:"string"==typeof u?.finish_reason&&u.finish_reason||"string"==typeof u?.stop_reason&&u.stop_reason||void 0}}async chatStructuredJson(e,t,n){const i=p(e),o="anthropic"===(r=this.vendor)||"google"===r?[{method:"jsonSchema"},{method:"functionCalling"}]:[{method:"jsonSchema",strict:!0},{method:"functionCalling",strict:!0},{method:"jsonMode"}];var r;let a=null;for(const e of o)try{const o=this.model.withStructuredOutput(t,{name:"structured_output",method:e.method,..."boolean"==typeof e.strict?{strict:e.strict}:{}}),r=await o.invoke(i,{signal:n});return t.parse(r)}catch(e){a=e}const s=a instanceof Error&&a.message?a.message:"langchain structured output failed with all methods";throw new Error(s)}}function f(e){return new m(e)}const g=/[\u3400-\u9fff]/u,y=[/\b\d+\s*(次|遍|个|条|张)\b/u,/第\s*\d+\s*(个|条|张)/u,/\b\d+\s*(times?|rounds?|steps?|items?)\b/i,/\bfor\s+\d+\s*(times?|rounds?)\b/i],h=[/直到/u,/等到/u,/出现/u,/找到/u,/\buntil\b/i,/\bwhen\b/i,/\bunless\b/i,/\bappears?\b/i,/\bfound\b/i],b=[/一直/u,/不停/u,/持续/u,/无限/u,/永远/u,/\bforever\b/i,/\bcontinuously\b/i,/\bendless(?:ly)?\b/i,/\bindefinitely\b/i,/\bkeep\b.*\bloop(?:ing)?\b/i];function x(e){return g.test(e||"")?"zh":"en"}function v(e){const t=e.trim();return t?y.some(e=>e.test(t))?"fixed_count":h.some(e=>e.test(t))?"until_condition":b.some(e=>e.test(t))?"infinite":"single_flow":"single_flow"}function w(e,t){return`## ${e}\n${t.map((e,t)=>`${t+1}. ${e}`).join("\n")}`}const k=["首次动作前先 observe_screen,禁止盲操作。","每个会引发状态变化的动作后(包括 tap_element、set_text、input_text、start_app 等),必须执行 observe_screen(wait_ms) -> verify_ui_state,验证后才能进入下一动作。跳过 verify_ui_state 会导致录制信息缺失,降低脚本生成成功率。","单轮最多一个状态变化动作;必须按”动作 -> wait(自适应时长) -> observe_screen -> verify_ui_state”闭环后再进入下一动作。","运行中仅执行与用户目标直接相关的动作;与任务无关的事情不要自行处理,禁止无关探索、浏览或演示性操作。","启动应用时优先使用包名;若当前只有应用名,先调用 get_installed_apps 确认 package_name,再使用 start_app,禁止臆测包名。","selector 必须来自最新 observe_screen 的真实内容。","selector 选择遵循“脚本优先”阶梯:稳定 resource_id -> 稳定 text -> 稳定 content_desc -> 最小组合 -> action_index。","校验场景(verify_ui_state / accessibility-node 纯查找)同样遵循最小唯一稳定策略,但每一步只用单字段(resource_id -> text -> content_desc),不做组合。","resource_id 唯一时只用 resource_id;若 resource_id 疑似混淆(如 obfuscated/数字前缀/高熵短串),不得单独依赖,退回稳定 text/content_desc。","组合唯一时优先 resource_id + 稳定 text/content_desc;禁止叠加动态值(时间、计数、row/column 等易变化文案)。bounds 仅作兜底消歧,不应作为通用脚本主 selector。","收到 VALIDATION 或 RUNTIME_GUARD 错误时,禁止原参数盲重试;必须先 observe_screen 刷新证据并调整 selector/动作。","禁止在无新观察证据的情况下重复完全相同的工具调用(同 tool + 同参数)。","界面方位只能依据 bounds 坐标与屏幕尺寸判断,禁止按节点顺序/缩进或经验臆测”顶部/底部”。","动作失败后最多允许一次替代路径,仍失败则暂停。","坐标点击仅作为 selector 无法稳定定位时的最后手段。","swipe 前必须先调用 record_search_context 记录查找目标与页面锚点;跳过会导致录制信息缺失,影响脚本生成。","index 仅在“同屏同语义节点多匹配且必须按顺序区分”时使用,且必须与 resource_id/text/content_desc/class_name/bounds 之一组合,禁止单独使用 index。","tap_element/long_press_element/set_text 的序号定位必须使用顶层 action_index,禁止 selector.index;verify_ui_state / record_search_context 可记录元素 index 作为补充证据。","当 text/content_desc 不稳定但仍需顺序区分时,且列表顺序稳定,使用 resource_id + action_index;若顺序不稳定则必须回退到更稳定锚点。","列表可能刷新、重排或异步插入时禁止依赖 index,优先稳定 resource_id 或稳定控件标签;若无法证明顺序稳定,则不要把该定位策略带入脚本。",'pre_context.step_intent 必须包含:(1)当前页面位置 (2)操作目的 (3)预期结果。例如"在抖音搜索结果页,点击第一个视频进入播放页",而非"点击视频"。record_search_context 的 step_intent 同理。',"若你能把当前动作抽象成稳定可复用的步骤语义,请显式填写 pre_context / record_search_context 中的 merge_key 和 expected_result。merge_key 应是稳定短语,不要直接抄界面文案。","若你明确知道该步骤通常需要额外等待再验证,请显式填写 wait_ms_hint;不要让脚本生成阶段再猜等待时长。","若你能明确判断页面类型、目标角色、步骤关键性或是否属于异常处理,请显式填写 pre_context / record_search_context 中的 page_kind、target_role、criticality_hint、include_mode_hint;若动作后页面类型也能明确判断,可填写 verify_ui_state.page_kind。不确定就省略,禁止靠文案关键词猜。",'pre_context.target.desc 和 anchor.desc 必须描述元素的视觉特征和语义角色,例如"底部导航栏中的首页按钮"或"搜索结果列表中的第一个视频卡片",不可只写"按钮"或"文本"。','verify_ui_state 中 screen_desc 必填,用一句话描述当前页面(如"抖音视频播放页"、"设置-通用页面"),帮助脚本生成理解上下文。',"当操作导致页面切换时,verify_ui_state 中必须包含 page_anchor(新页面的标志性元素),帮助脚本生成确认导航成功。"],A=["Call observe_screen before the first action; no blind operations.","After each state-changing action (including tap_element, set_text, input_text, start_app, etc.), you must do observe_screen(wait_ms) -> verify_ui_state before the next action. Skipping verify_ui_state causes missing recording data and lowers script generation success rate.","At most one state-changing action per turn; enforce the loop “act -> wait(adaptive) -> observe_screen -> verify_ui_state” before any next action.","During execution, only perform actions directly related to the user goal; do not handle unrelated tasks on your own, and avoid unrelated exploration, browsing, or demo-only actions.","Prefer package names when starting apps; if you only know the app name, call get_installed_apps to confirm package_name before using start_app, and never guess package names.","Selectors must come from the latest observe_screen evidence.","Selector choice must follow the script-safe ladder: stable resource_id -> stable text -> stable content_desc -> minimal combination -> action_index.","For verification scenarios (verify_ui_state / accessibility-node pure find), apply the same minimum-unique stability ladder, but single-field only (resource_id -> text -> content_desc), with no combinations.","If resource_id is unique, use only resource_id; if resource_id looks obfuscated (e.g. obfuscated/numeric prefix/high-entropy short token), do not rely on it alone and fall back to stable text/content_desc.","When combinations are required, prefer resource_id + stable text/content_desc; avoid dynamic values (time/count/row/column or other volatile copy). bounds is fallback-only and should not become a reusable script selector.","On VALIDATION or RUNTIME_GUARD errors, never blind-retry with the same arguments; observe_screen first, then adjust selector/action.","Do not repeat identical tool calls (same tool + same args) without new observation evidence.","Infer UI position only from bounds + screen size; never infer top/bottom from node order, indentation, or prior assumptions.","After an action failure, allow at most one alternative path; then pause.","Coordinate taps are last resort only when selector-based targeting is unstable.","Before swipe, you must call record_search_context to record the search target and page anchor; skipping causes missing recording data.","Use index only when same-screen nodes are still ambiguous and order is required; index must be combined with at least one of resource_id/text/content_desc/class_name/bounds, never used alone.","For tap_element/long_press_element/set_text, ordinal targeting must use top-level action_index, not selector.index; verify_ui_state / record_search_context may carry node index as supplemental evidence.","If text/content_desc is unstable but order still matters and list order is stable, use resource_id + action_index; if order is unstable, fall back to more stable anchors instead.","Do not rely on index when list order can change (refresh/reorder/async insertion); if order stability cannot be justified, do not carry that locator strategy into the script.",'pre_context.step_intent must include: (1) current page location (2) action purpose (3) expected result. Example: "On TikTok search results page, tap the first video to enter playback page", not just "tap video". Same for record_search_context step_intent.',"If you can abstract the current action into a stable reusable step semantic, fill merge_key and expected_result in pre_context or record_search_context. merge_key should be a stable short phrase, not copied screen text.","If you explicitly know the step usually needs additional wait before verification, fill wait_ms_hint so script generation does not have to guess timing.","If you can explicitly determine page kind, target role, step criticality, or whether a record is an exception handler candidate, fill page_kind/target_role/criticality_hint/include_mode_hint in pre_context or record_search_context. If the resulting page kind is also explicit after an action, you may fill verify_ui_state.page_kind. If unsure, omit them; never guess from copy keywords.",'pre_context.target.desc and anchor.desc must describe the element\'s visual characteristics and semantic role, e.g. "Home button in bottom navigation bar" or "First video card in search results list", not just "button" or "text".','verify_ui_state screen_desc is required — describe the current page in one sentence (e.g. "TikTok video playback page", "Settings - General page") to help script generation understand context.',"When an action causes a page transition, verify_ui_state must include page_anchor (a landmark element of the new page) to help script generation confirm navigation success."],S={zh:{fixed_count:["严格按目标次数执行,不得提前结束。","每轮结束后验证累计进度。"],until_condition:["每轮都必须“检查 -> 动作 -> 等待 -> 再检查”。","达到条件立即收敛,未命中到上限后结束。"],infinite:["仅当目标明确要求持续执行时允许无限循环。"],single_flow:["完成主线后立即结束,不做无关探索。"]},en:{fixed_count:["Execute exactly the requested count and never finish early.","Validate cumulative progress after each loop."],until_condition:['Every loop must follow "check -> act -> wait -> re-check".',"Converge immediately when condition is met; stop at limit otherwise."],infinite:["Infinite loop is allowed only when explicitly requested."],single_flow:["Finish immediately after the main path; avoid unrelated exploration."]}};function z(e,t){if(!e||0===e.subtasks.length)return"";const n=e.subtasks.map(e=>"zh"===t?`- ${e.id}. ${e.description} (成功标准: ${e.successCriteria})`:`- ${e.id}. ${e.description} (Success: ${e.successCriteria})`).join("\n");return"zh"===t?`## 任务计划参考\n${n}`:`## Plan Reference\n${n}`}const O=["输出必须是合法 JSON 对象,禁止 markdown 代码块。","计划必须基于当前屏幕状态,不允许假设未观察到的页面。","计划仅包含与用户目标直接相关的步骤,禁止加入与目标无关的探索性动作。","每个子任务都要有可观察的 successCriteria。","若目标存在前置状态依赖,必须先规划“进入前置状态 -> 验证前置状态”,再执行终态动作。","子任务按执行顺序排列,保持最短稳定路径。"],$=["Output must be a valid JSON object, never markdown.","Plan must be grounded in the current observed screen only.","Plan must include only steps directly related to the user goal; do not add unrelated exploratory actions.","Each subtask must include observable successCriteria.",'If goal has prerequisite state dependencies, plan "enter prerequisite state -> verify prerequisite state" before terminal actions.',"Subtasks must be ordered for the shortest stable path."],I={zh:{fixed_count:["识别用户指定次数,子任务中体现计数闭环。","避免把固定次数任务改写为条件循环。"],until_condition:["计划中显式写出停止条件和最大探索轮次。","停止条件必须可观察,不能用抽象描述。"],infinite:["仅在目标明确要求持续执行时使用无限模式。","仍需给出安全中断条件与风险说明。"],single_flow:["保持单次流程简洁,避免过度拆分。"]},en:{fixed_count:["Capture user-requested counts and enforce count closure in subtasks.","Do not rewrite fixed-count goals into condition loops."],until_condition:["State explicit stop condition and max exploration rounds.","Stop condition must be observable, not abstract."],infinite:["Use infinite mode only when user explicitly requests continuous execution.","Still provide safety stop condition and risks."],single_flow:["Keep single-flow plans concise and avoid over-splitting."]}};const R=new Set(["start_app","stop_app","tap","tap_element","long_press_element","press_key"]),j=new Set(["wait"]),q=new Set(["verify_ui_state"]),T=new Set(["tap_element","long_press_element","set_text"]),N=new Set(["tap_element","long_press_element","set_text"]),C=new Set([...N,"verify_ui_state","record_search_context"]),E=N,M=new Set([...N,"input_text"]),L=new Set(["start_app","stop_app","tap","tap_element","long_press_element","set_text","input_text","swipe","press_key"]);function P(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function U(e){const t={};let n=!1;for(const[i,o]of Object.entries(e))null!=o&&""!==o&&(t[i]=o,n=!0);return n?t:void 0}function D(e){if("number"==typeof e&&Number.isInteger(e)&&e>=0)return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isInteger(t)&&t>=0)return t}}function W(e){if("number"==typeof e&&Number.isInteger(e)&&e>0)return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isInteger(t)&&t>0)return t}}function F(e,t){for(const n of t){const t=e[n];if("string"==typeof t){const e=t.trim();if(e.length>0)return e}}}function J(e){const t=P(e);if(t)return U({index:D(t.index),resource_id:F(t,["resource_id","resource-id","resourceId","id"]),text:F(t,["text"]),content_desc:F(t,["content_desc","content-desc","contentDesc"]),class_name:F(t,["class_name","class","className"]),bounds:F(t,["bounds"])})}function G(e){const t=P(e?.script_selector);if(!t)return;const n=P(t.selector);return n?{selector:n,action_index:D(t.action_index),stability:"string"==typeof t.stability?t.stability:void 0,reusable:"boolean"==typeof t.reusable?t.reusable:void 0,reason:"string"==typeof t.reason?t.reason:void 0,scope:"string"==typeof t.scope?t.scope:void 0,policy_decision:"string"==typeof t.policy_decision?t.policy_decision:void 0,policy_reason:"string"==typeof t.policy_reason?t.policy_reason:void 0}:void 0}function K(e){const t=P(e);if(!t)return;const n=F(t,["page_kind"]),i=F(t,["criticality_hint"]),o=F(t,["include_mode_hint"]),r=F(t,["valid_until"]);return{context_id:F(t,["context_id"]),page_kind:"home"===n||"list"===n||"detail"===n||"dialog"===n||"search"===n||"form"===n||"unknown"===n?n:void 0,target_role:F(t,["target_role"]),criticality_hint:"critical"===i||"supporting"===i||"noise"===i?i:void 0,include_mode_hint:"main_flow"===o||"log_only"===o||"exception_handler_candidate"===o?o:void 0,valid_until:"next_action"===r||"next_verified_transition"===r||"manual_close"===r?r:void 0}}function Y(e,t){if(e||t)return{context_id:t?.context_id||e?.context_id,page_kind:t?.page_kind||e?.page_kind,target_role:t?.target_role||e?.target_role,criticality_hint:t?.criticality_hint||e?.criticality_hint,include_mode_hint:t?.include_mode_hint||e?.include_mode_hint,valid_until:t?.valid_until||e?.valid_until}}function V(e){const t=new Set,n=[];for(const i of e)if(i&&!t.has(i)&&(t.add(i),n.push(i),n.length>=6))break;return n}function H(e){switch(e){case"start_app":return 1e4;case"tap":case"tap_element":case"long_press_element":case"press_key":return 6e3;case"set_text":case"input_text":return 3e3;case"swipe":return 1e3;default:return}}const B=new Set(["tap_element","long_press_element","set_text"]),Q=new Set(["start_app","stop_app","tap_element","long_press_element","set_text","input_text","press_key","swipe","tap","wait"]);function X(e,t){const n=function(e){const t=[];let n,i=null;for(const o of e){const e=o.toolName,r=P(o.arguments)||{},a=P(o.result.data);if("get_screen_info"===e&&o.result.success){const e="number"==typeof a?.width?a.width:void 0,t="number"==typeof a?.height?a.height:void 0;e&&t&&(n={width:e,height:t});continue}if("observe_screen"===e||"get_installed_apps"===e)continue;if("record_search_context"===e){i={step_intent:F(r,["step_intent"]),merge_key:F(r,["merge_key"]),expected_result:F(r,["expected_result"]),wait_ms_hint:W(r.wait_ms_hint),target_desc:F(r,["target_desc"]),anchor:U(P(r.anchor_element)||{}),anchor_plan:P(a?.anchor_plan)||void 0,target:U(P(r.target_element)||{}),target_plan:P(a?.target_plan)||void 0,recorded_context:K(a?.recorded_context)};continue}if("verify_ui_state"===e){const e=t[t.length-1];if(!e)continue;e.verify=U(P(r.verify_element)||{}),e.verify_desc=F(r,["step_desc"]),e.page_anchor=U(P(r.page_anchor)||{}),e.screen_desc=F(r,["screen_desc"]);const n=F(P(a?.recorded_page)||{},["page_kind"])||F(r,["page_kind"]);e.verified_wait_ms_hint=W(r.wait_ms_hint),e.verified_page_kind="home"===n||"list"===n||"detail"===n||"dialog"===n||"search"===n||"form"===n||"unknown"===n?n:void 0,e.verification_plan=P(a?.verification_plan)||void 0,e.page_anchor_plan=P(a?.page_anchor_plan)||void 0,"next_verified_transition"===i?.recorded_context?.valid_until&&(i=null);continue}const s={seq:t.length+1,tool:e,success:o.result.success};o.result.success||"string"!=typeof o.result.error||(s.error=o.result.error),i&&(s.step_intent=i.step_intent,s.intent_merge_key=i.merge_key,s.expected_result_hint=i.expected_result,s.wait_ms_hint=i.wait_ms_hint,s.search_context=i.target_desc,!s.anchor&&i.anchor&&(s.anchor=i.anchor),i.anchor_plan&&(s.anchor_plan=i.anchor_plan),!s.target&&i.target&&(s.target=i.target),i.target_plan&&(s.target_plan=i.target_plan),i.recorded_context&&(s.recorded_context=i.recorded_context),"next_action"===i.recorded_context?.valid_until&&(i=null));const c=P(r.pre_context);c&&(s.step_intent=F(c,["step_intent"])||s.step_intent,s.intent_merge_key=F(c,["merge_key"])||s.intent_merge_key,s.expected_result_hint=F(c,["expected_result"])||s.expected_result_hint,s.wait_ms_hint=W(c.wait_ms_hint)||s.wait_ms_hint,s.target=U(P(c.target)||{})||s.target,s.anchor=U(P(c.anchor)||{})||s.anchor,s.recorded_context=Y(s.recorded_context,K(c))),s.selector=U(P(r.selector)||{}),s.action_index=D(r.action_index),"string"==typeof r.text&&(s.text=r.text),"number"==typeof r.key_code&&(s.key_code=r.key_code),"string"==typeof r.package_name&&(s.package_name=r.package_name),"number"==typeof r.duration&&"wait"===e&&(s.wait_duration=r.duration),"number"==typeof r.x&&"number"==typeof r.y&&"tap"===e&&(s.tap_position={x:r.x,y:r.y}),"swipe"===e&&(s.swipe={start_x:Number(r.start_x),start_y:Number(r.start_y),end_x:Number(r.end_x),end_y:Number(r.end_y),duration:"number"==typeof r.duration?r.duration:300});const l=P(a?.runtime_selector_evidence);if(l){const e=U(P(l.requested_selector)||{});!s.selector&&e&&(s.selector=e),s.runtime_selected_node=J(l.selected_node),s.runtime_candidate_nodes=Array.isArray(l.candidate_nodes)?l.candidate_nodes.map(e=>J(e)).filter(e=>!!e):void 0,s.runtime_matched_count="number"==typeof l.matched_count?l.matched_count:void 0,s.selector_profile=P(l.selector_profile)||void 0;const t=D(l.action_index);void 0!==t&&(s.action_index=t)}t.push(s)}return{screen:n,actions:t}}(t),i=n.actions.map(e=>{const t=G(e.selector_profile),n=function(e){const t=G(e.page_anchor_plan),n=G(e.verification_plan);return t?{required:!0,materialization:"ready",signal_type:"page_arrived",rationale:"page transition uses page_anchor as the primary arrival signal",primary:t,fallback:n}:n?{required:!0,materialization:"ready",signal_type:"set_text"===e.tool||"input_text"===e.tool?"input_committed":"swipe"===e.tool?"list_progressed":"element_visible",rationale:"verify_ui_state provided a recording-time approved verification selector",primary:n}:e.verify?{required:!0,materialization:"missing_plan",signal_type:"element_visible",rationale:"raw verify evidence exists but recording-time verify plan is missing"}:e.success&&L.has(e.tool)?{required:!0,materialization:"missing_plan",signal_type:"set_text"===e.tool||"input_text"===e.tool?"input_committed":"swipe"===e.tool?"list_progressed":"state_changed",rationale:"state-changing action requires explicit post-action verification for unattended replay"}:{required:!1,materialization:"not_required",signal_type:e.success?"action_completed":"state_changed",rationale:"no post-action verification artifact recorded"}}(e),i=function(e,t){switch(e.tool){case"start_app":{const t=e.package_name;return t?[{path:"permission/set",params:{package_name:t,grant_all:!0}},{path:"activity/start",params:{package_name:t}}]:[]}case"stop_app":{const t=e.package_name;return t?[{path:"activity/stop",params:{package_name:t}}]:[]}case"tap_element":return t?[{path:"accessibility/node",params:U({selector:t.selector,action:"click",action_index:t.action_index,wait_timeout:2e3})}]:[];case"long_press_element":return t?[{path:"accessibility/node",params:U({selector:t.selector,action:"long_click",action_index:t.action_index,wait_timeout:2e3})}]:[];case"set_text":return t?[{path:"accessibility/node",params:U({selector:t.selector,action:"set_text",action_index:t.action_index,wait_timeout:2e3,action_params:e.text?{text:e.text}:void 0})}]:[];case"input_text":return e.text?[{path:"input/text",params:{text:e.text}}]:[];case"press_key":return"number"==typeof e.key_code?[{path:"input/keyevent",params:{key_code:e.key_code}}]:[];case"swipe":return e.swipe?[{path:"input/scroll_bezier",params:e.swipe}]:[];case"tap":return e.tap_position?[{path:"input/click",params:e.tap_position}]:[];case"wait":return"number"==typeof e.wait_duration?[{path:"base/sleep",params:{duration:e.wait_duration}}]:[];default:return[]}}(e,t),o=e.verified_wait_ms_hint||e.wait_ms_hint,r=function(e,t,n){if("ready"===e.materialization&&e.primary)return{path:"accessibility/node",params:{selector:e.primary.selector,wait_timeout:n||H(t)||5e3},throw_if_empty:["nodes"]}}(n,e.tool,o),a=function(e){const t=[],{record:n,scriptSelector:i,replayActions:o,successCriteria:r}=e;return B.has(n.tool)&&!i&&t.push(`missing_script_selector: ${n.tool} requires a recording-approved script_selector`),Q.has(n.tool)&&0===o.length&&t.push(`missing_replay_action: ${n.tool} has no replay_plan.actions`),r.required&&"ready"!==r.materialization&&t.push(`missing_verification_plan: verification is required but materialization=${r.materialization}`),t}({record:e,scriptSelector:t,replayActions:i,successCriteria:n}),s=F(e.anchor||{},["desc"])||e.search_context||e.step_intent,c=e.screen_desc||F(e.page_anchor||{},["desc"]),l=e.recorded_context?.page_kind||"unknown",p="page_arrived"===n.signal_type?e.verified_page_kind||"unknown":l,d=e.recorded_context?.criticality_hint,u=d||function(e,t){return"wait"===e.tool?"noise":"page_arrived"===t?"critical":e.success?"supporting":"noise"}(e,n.signal_type),_=e.recorded_context?.include_mode_hint,m=_||function(e){return"wait"===e.tool?"log_only":e.success?"main_flow":"log_only"}(e),f=e.step_intent||e.verify_desc||F(e.target||{},["desc"])||e.search_context||`${e.tool} #${e.seq}`,g=e.expected_result_hint||F(e.page_anchor||{},["desc"])||e.screen_desc||e.verify_desc||f,y=(e.intent_merge_key||f).normalize("NFKC").toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/gu,"_").replace(/^_+|_+$/gu,"")||"step";const h=o||H(e.tool),b=o?"explicit":h?"fallback":void 0;return{id:`action_${e.seq}`,seq:e.seq,tool:e.tool,success:e.success,error:e.error,page:{before:{kind:l,description:s,anchor:e.anchor_plan||e.anchor},after:e.screen_desc||e.page_anchor?{kind:p,description:c,anchor:e.page_anchor_plan||e.page_anchor||e.verify}:void 0,key_features:V([s,c,e.search_context,F(e.target||{},["desc","text","content_desc"]),F(e.anchor||{},["desc","text","content_desc"])])},intent:{summary:f,purpose:f,expected_result:g,merge_key:y,sources:{expected_result:e.expected_result_hint?"explicit":"derived",merge_key:e.intent_merge_key?"explicit":"derived"}},target:{desc:F(e.target||{},["desc"])||e.search_context,role:e.recorded_context?.target_role||"generic_target",runtime_selector:e.selector,runtime_action_index:e.action_index,runtime_selected_node:e.runtime_selected_node,selector_profile:e.selector_profile,script_selector:t,selector_candidates:Array.isArray(e.selector_profile?.selector_candidates)?e.selector_profile?.selector_candidates:void 0},success_criteria:n,classification:{criticality:u,include_mode:m,step_is_key:"critical"===u,sources:{criticality:d?"explicit":"fallback",include_mode:_?"explicit":"fallback"}},replay_plan:{actions:i,suggested_wait_ms:h,suggested_wait_source:b,verification:r},diagnostics:{matched_count:e.runtime_matched_count,selector_reusable:t?.reusable},blockers:a.length>0?a:void 0}}),o=i.flatMap(e=>(e.blockers||[]).map(t=>`${e.id}: ${t}`));return{version:"recording-artifact/v2",goal:e,screen:n.screen,design_principle:"Recording artifact already contains approved script selectors, verification plans, merge keys, and replay actions. Script generation must translate, not infer.",actions:i,blockers:o.length>0?o:void 0}}function Z(e){return"zh"===e?"你是 Android 自动化需求优化助手。\n\n要求:\n1. 输出语言与输入一致。\n2. 只做表达优化,不新增用户未提及的目标。\n3. 保留次数、顺序、停止条件、应用名和关键文本。\n4. 输出仅保留优化后的最终描述,不要解释。":"You are an Android automation intent refinement assistant.\n\nRequirements:\n1. Keep output language consistent with input.\n2. Improve expression only; do not add new goals not mentioned by user.\n3. Preserve count, order, stop conditions, app names, and key text.\n4. Output only the final refined description without explanations."}function ee(e){return function(e){const t=x(e),n=v(e),i=I[t][n]||I[t].single_flow;return"zh"===t?["你是 Android 自动化任务规划助手。","根据用户目标和当前屏幕,生成可执行、可验证、可收敛的任务计划。",w("全局规则",O),w(`模式规则(${n})`,i),"## 输出 JSON 结构","{",' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',' "estimatedSteps": 1',"}"].join("\n\n"):["You are an Android automation planning assistant.","Generate an executable, verifiable, and convergent task plan based on goal and current screen.",w("Global Rules",$),w(`Mode Rules (${n})`,i),"## Output JSON Schema","{",' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',' "estimatedSteps": 1',"}"].join("\n\n")}(e)}function te(e,t){return function(e,t){const n=x(e),i=v(e),o=S[n][i]||S[n].single_flow;return"zh"===n?["你是 Android 自动化执行 Agent。你的职责是在真实设备上高成功率完成目标。",w("核心执行规则",k),w(`任务模式规则(${i})`,o),"## 输出约束","- 优先一次只推进一个可验证子目标;若发生状态变化,必须先完成 wait -> observe_screen -> verify_ui_state 再继续。","- 非必要不输出长文本,优先通过 tool_calls 执行动作。","- 当缺少关键参数或验证证据时,立即暂停并明确提问。",z(t,n)].filter(Boolean).join("\n\n"):["You are an Android automation execution agent. Your objective is maximum completion reliability on real devices.",w("Core Execution Rules",A),w(`Task Mode Rules (${i})`,o),"## Output Constraints","- Advance one verifiable sub-goal at a time; after state changes, complete wait -> observe_screen -> verify_ui_state before continuing.","- Keep text brief unless necessary; prefer tool_calls for actions.","- If critical parameters or verification evidence are missing, pause and ask explicitly.",z(t,n)].filter(Boolean).join("\n\n")}(e,t)}function ne(e){return function(e){return"zh"===x(e)?'你是 Android WorkflowScript 组装器。输入不是原始日志,而是录制阶段已经提纯好的 RecordingArtifact。\n\n你的职责不是推理,而是翻译:\n1. 只消费 RecordingArtifact 中已经批准进入脚本的结构化信息。\n2. 不自行猜 selector,不自行猜页面意图,不自行猜成功判定条件。\n3. 不从 diagnostics、raw desc、候选节点中发明新 selector。\n\n## 输入格式\nRecordingArtifact.actions[*] 已经包含:\n- intent.merge_key: 建议的步骤合并键\n- target.script_selector: 录制阶段批准进入脚本的目标 selector\n- success_criteria.primary: 录制阶段批准的成功判定 selector\n- success_criteria.materialization: 验证条件是否已经具备可回放计划\n- replay_plan.actions: 已经整理好的主动作 skeleton\n- replay_plan.verification: 已经整理好的验证动作 skeleton\n- replay_plan.suggested_wait_source: explicit / fallback\n- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- blockers: 录制阶段已经识别出的阻断问题\n\n## 绝对规则\n1. 生成脚本时,优先直接使用 replay_plan.actions;不要自己重建 path/params。\n2. 只要 main_flow 记录存在 replay_plan.verification,就必须在该记录的 replay_plan.actions 之后立刻生成验证动作;两者之间只允许插入 base/sleep。\n3. 当 replay_plan.suggested_wait_source="explicit" 且 suggested_wait_ms 存在时,必须先插入 base/sleep,再执行 replay_plan.verification。\n4. classification.include_mode="log_only" 的记录默认不进入主流程。\n5. classification.include_mode="exception_handler_candidate" 的记录优先抽成 workflow 顶层 exception_handlers,而不是普通步骤。\n6. selector 只能来自 target.script_selector 或 success_criteria.primary/fallback;禁止从 target.desc、screen_desc、diagnostics 反推。\n7. 不得把 bounds、动态文案、候选节点文本重新拼成新 selector。\n8. 相邻且 merge_key 相同的 main_flow 记录优先合并为同一 step。\n9. 如果 replay_plan.actions 为空,不要编造动作;跳过该记录。\n10. completed 只允许字面量 "success",且仅在 loop.max_count 场景使用。\n11. 如果 RecordingArtifact.blockers 存在,或 action.blockers 非空,不要自行发明补救方案。\n12. 如果 success_criteria.required=true 但 materialization 不是 "ready",视为录制不完整。\n13. 输出纯 JSON,不要解释。\n\n## 工作流约束\n- 合法 path 仅限:activity/start, permission/set, activity/stop, accessibility/node, input/scroll_bezier, input/text, input/keyevent, input/click, base/sleep\n- accessibility/node 必须带正整数 wait_timeout\n- throw_if_empty 只能是 ["nodes"]\n- exception_handlers 只能放在 workflow 顶层\n\n## 输出格式(WorkflowScript)\n你必须输出一个合法的 WorkflowScript JSON,对象结构如下:\n\n```json\n{\n "id": "workflow_id",\n "name": "工作流名称",\n "version": "1.0.0",\n "description": "工作流描述",\n "exception_handlers": [\n {\n "name": "handler_name",\n "selector": { "text": "关闭" },\n "action": "click",\n "max_trigger_count": 3\n }\n ],\n "steps": {\n "open_result_detail": {\n "description": "步骤描述",\n "actions": [\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/video_card" }, "action": "click", "action_index": 0, "wait_timeout": 2000 } },\n { "path": "base/sleep", "params": { "duration": 1500 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 1500 }, "throw_if_empty": ["nodes"] }\n ]\n }\n },\n "flow": ["open_result_detail"]\n}\n```\n\n补充约束:\n- `steps` 必须是对象,key 为 step id,value 为 step 定义\n- 每个 step 必须有 `actions`,且至少 1 个 action\n- `flow` 必须按执行顺序列出 step id,且每个 id 都必须存在于 `steps`\n- 仅当使用 `loop.max_count` 时,才允许 `completed: "success"`\n- `exception_handlers` 可以是空数组,但每个 handler 都必须满足合法字段结构\n\n## 生成策略\n- 主流程:按 actions 顺序消费 include_mode="main_flow" 的记录\n- 异常处理:从 include_mode="exception_handler_candidate" 中提取\n- 步骤命名:基于 intent.merge_key 生成稳定英文 step id\n- 步骤描述:使用 intent.summary 或 page.before/after.description 组织一句简洁描述\n- 验证:使用 replay_plan.verification,禁止回退到人工推理\n\n输出 pure JSON only.':'You are an Android WorkflowScript assembler. The input is not a raw log. It is a RecordingArtifact that has already been refined during recording.\n\nYour job is translation, not investigation:\n1. Consume only the structured fields already approved for script generation.\n2. Do not infer selectors, page intent, or success criteria on your own.\n3. Do not invent selectors from diagnostics, raw descriptions, or candidate nodes.\n\n## Input format\nEach RecordingArtifact.actions[*] already includes:\n- intent.merge_key: suggested step merge key\n- target.script_selector: recording-approved selector for script usage\n- success_criteria.primary: recording-approved verification selector\n- success_criteria.materialization: whether verification is actually ready for replay\n- replay_plan.actions: prebuilt action skeletons\n- replay_plan.verification: prebuilt verification action skeleton\n- replay_plan.suggested_wait_source: explicit / fallback\n- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- blockers: recording-time blockers that make script generation unsafe\n\n## Hard rules\n1. Use replay_plan.actions directly whenever present; do not rebuild action params manually.\n2. Whenever a main_flow record has replay_plan.verification, you must emit that verification immediately after the record\'s replay_plan.actions. Only base/sleep may appear between them.\n3. When replay_plan.suggested_wait_source is "explicit" and suggested_wait_ms exists, you must insert base/sleep before replay_plan.verification.\n4. Records with classification.include_mode="log_only" should normally stay out of the main flow.\n5. Records with classification.include_mode="exception_handler_candidate" should become top-level exception_handlers first, not normal steps.\n6. Selectors may only come from target.script_selector or success_criteria.primary/fallback.\n7. Never reconstruct selectors from bounds, dynamic text, or candidate node text.\n8. Prefer merging adjacent main_flow records with the same merge_key into one step.\n9. If replay_plan.actions is empty, do not fabricate an action. Skip that record.\n10. completed may only be the literal "success", and only for loop.max_count.\n11. If RecordingArtifact.blockers exists or action.blockers is non-empty, do not invent a workaround.\n12. If success_criteria.required=true but materialization is not "ready", treat the recording as incomplete.\n13. Output pure JSON only.\n\n## Workflow constraints\n- Legal paths only: activity/start, permission/set, activity/stop, accessibility/node, input/scroll_bezier, input/text, input/keyevent, input/click, base/sleep\n- accessibility/node requires a positive integer wait_timeout\n- throw_if_empty must be exactly ["nodes"]\n- exception_handlers are top-level only\n\n## Output format (WorkflowScript)\nYou must output one valid WorkflowScript JSON object with this shape:\n\n```json\n{\n "id": "workflow_id",\n "name": "Workflow name",\n "version": "1.0.0",\n "description": "Workflow description",\n "exception_handlers": [\n {\n "name": "handler_name",\n "selector": { "text": "Close" },\n "action": "click",\n "max_trigger_count": 3\n }\n ],\n "steps": {\n "open_result_detail": {\n "description": "Step description",\n "actions": [\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/video_card" }, "action": "click", "action_index": 0, "wait_timeout": 2000 } },\n { "path": "base/sleep", "params": { "duration": 1500 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 1500 }, "throw_if_empty": ["nodes"] }\n ]\n }\n },\n "flow": ["open_result_detail"]\n}\n```\n\nAdditional constraints:\n- `steps` must be an object keyed by step id\n- every step must contain `actions` with at least one action\n- `flow` must list step ids in execution order, and every id must exist in `steps`\n- `completed: "success"` is only allowed with `loop.max_count`\n- `exception_handlers` may be an empty array, but every handler must follow the legal field shape\n\n## Generation strategy\n- Main flow: consume records with include_mode="main_flow" in order\n- Exception handling: extract from include_mode="exception_handler_candidate"\n- Step IDs: generate stable English ids from intent.merge_key\n- Step descriptions: use intent.summary or page.before/after.description\n- Verification: use replay_plan.verification only; never fall back to manual inference\n\nOutput pure JSON only.'}(e)}function ie(e,t,n){return function(e,t){const n=x(e),i=X(e,t),o=[];return i.screen&&o.push(`[Screen]\n${i.screen.width}x${i.screen.height}`),o.push(`[Goal]\n${e}`),o.push(`[RecordingArtifact]\n${JSON.stringify(i,null,2)}`),o.push("zh"===n?"请将 RecordingArtifact 翻译成最终 WorkflowScript。只使用 replay_plan、target.script_selector、success_criteria。不要自行推导 selector,不要补编未记录动作。如果 blockers 存在,不要发明补救方案。只输出 WorkflowScript JSON。":"Translate the RecordingArtifact into the final WorkflowScript. Use replay_plan, target.script_selector, and success_criteria only. Do not derive selectors or invent unrecorded actions. If blockers exist, do not invent a workaround. Output WorkflowScript JSON only."),o.join("\n\n")}(e,t)}const oe=ee(""),re=te(""),ae=ne(""),se=ae.length;l.createHash("sha256").update([Z("zh"),Z("en"),oe,re,ae,String(se)].join("\n\n-----\n\n")).digest("hex");const ce=i.z.object({path:i.z.string().min(1),params:i.z.record(i.z.unknown()).optional(),throw_if_empty:i.z.array(i.z.string()).optional()}),le=i.z.object({description:i.z.string().optional(),completed:i.z.literal("success").optional(),loop:i.z.union([i.z.object({count:i.z.number().int(),interval:i.z.number().int().optional()}),i.z.object({max_count:i.z.number().int(),interval:i.z.number().int().optional()})]).optional(),actions:i.z.array(ce).min(1)}),pe=i.z.object({id:i.z.string().min(1),name:i.z.string().min(1),version:i.z.string().min(1),description:i.z.string().optional(),timeout:i.z.number().int().positive().optional(),exception_handlers:i.z.array(i.z.object({description:i.z.string().optional(),name:i.z.string().optional(),selector:i.z.record(i.z.unknown()),action:i.z.string().min(1),action_params:i.z.record(i.z.unknown()).optional(),max_trigger_count:i.z.number().int().positive().optional()})).optional(),steps:i.z.record(le),flow:i.z.array(i.z.string()).min(1)}),de=new Set(["activity/start","permission/set","activity/stop","accessibility/node","input/scroll_bezier","input/text","input/keyevent","input/click","base/sleep"]);function ue(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function _e(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function me(e){if("number"==typeof e&&Number.isInteger(e))return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isFinite(t)&&Number.isInteger(t))return t}return null}function fe(e){const t=ue(e.loop);if(!t)return void(_e(e,"completed")&&delete e.completed);const n=(Array.isArray(e.actions)?e.actions:[]).map(e=>ue(e)).filter(e=>!!e);if(_e(t,"count")){return 1===me(t.count)?(delete e.loop,void delete e.completed):void(_e(e,"completed")&&delete e.completed)}if(!_e(t,"max_count"))return;e.completed="success";if(n.some(e=>ge(e.throw_if_empty)))return;const i=n.find(e=>{if("accessibility/node"!==e.path)return!1;const t=ue(e.params);return!t||!_e(t,"action")});if(i)return void(i.throw_if_empty=["nodes"]);const o=n.find(e=>"accessibility/node"===e.path);if(o)return void(o.throw_if_empty=["nodes"]);const r=me(t.max_count);if(null!==r){if(1===r)return delete e.loop,void delete e.completed;e.loop={count:r},delete e.completed}}function ge(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]&&"nodes"===e[0]}function ye(e,t,n,i){const o=`${e}.actions[${n}]`;if(de.has(t.path)||i.push(`${o}: illegal path "${t.path}"`),t.throw_if_empty&&!ge(t.throw_if_empty)&&i.push(`${o}: throw_if_empty must be exactly ["nodes"]`),"accessibility/node"===t.path){const e=t.params?.wait_timeout;"number"==typeof(r=e)&&Number.isInteger(r)&&r>0||i.push(`${o}: accessibility/node requires positive integer params.wait_timeout`)}var r}function he(e,t,n){const i=t.steps[e],o=i.loop,r=i.completed;if(!o)return void(void 0!==r&&n.push(`${e}: steps without loop must not define completed`));if("count"in o)return 1===o.count&&n.push(`${e}: loop.count=1 is forbidden; remove loop`),void(void 0!==r&&n.push(`${e}: count loop must not define completed`));"success"!==r&&n.push(`${e}: max_count loop must define completed="success"`);i.actions.some(e=>ge(e.throw_if_empty))||n.push(`${e}: max_count loop requires at least one action with throw_if_empty=["nodes"]`)}function be(e){const t=[];for(const[n,i]of Object.entries(e.steps)){he(n,e,t);for(let e=0;e<i.actions.length;e++)ye(n,i.actions[e],e,t)}if(function(e,t){const n=new Set;for(const i of e.flow){const o=e.steps[i];for(let e=0;e<o.actions.length;e++){const r=o.actions[e],a=r.params||{},s=`${i}.actions[${e}]`;if("permission/set"===r.path){const e=a.package_name;if("string"!=typeof e||!e){t.push(`${s}: permission/set requires params.package_name`);continue}!0!==a.grant_all&&t.push(`${s}: permission/set requires params.grant_all=true`),n.add(e)}if("activity/start"===r.path){const e=a.package_name;if("string"!=typeof e||!e){t.push(`${s}: activity/start requires params.package_name`);continue}n.has(e)||t.push(`${s}: missing prior permission/set for package "${e}"`)}}}}(e,t),t.length>0)throw new Error(`workflow semantic validation failed:\n- ${t.join("\n- ")}`)}function xe(e){if(Array.isArray(e))return e.map(e=>xe(e));if(!e||"object"!=typeof e)return e;const t=e;return Object.keys(t).sort().reduce((e,n)=>(e[n]=xe(t[n]),e),{})}function ve(e){return JSON.stringify(xe({selector:e.selector,action:e.action,action_params:e.action_params}))}function we(e){return JSON.stringify(xe(e))}function ke(e){const t=e.start_x,n=e.start_y,i=e.end_x,o=e.end_y;return"number"!=typeof t||"number"!=typeof n||"number"!=typeof i||"number"!=typeof o?null:we({start_x:t,start_y:n,end_x:i,end_y:o})}function Ae(e){const t=e.x,n=e.y;return"number"!=typeof t||"number"!=typeof n?null:we({x:t,y:n})}function Se(e){const t=e.params||{},n=ue(t.selector);if(!n)return null;const i="string"==typeof t.action?t.action:void 0,o=me(t.action_index);return{selector:n,action:i,action_index:null===o?void 0:o,action_params:ue(t.action_params)||void 0,throw_if_empty:e.throw_if_empty}}function ze(e,t){if("accessibility/node"!==t.path)return!1;const n=ue(t.params),i=ue(n?.selector);if(!i)return!1;if(we(e.selector)!==we(i))return!1;const o="string"==typeof n?.action?n.action:void 0;if(e.action!==o)return!1;const r=me(n?.action_index),a=null===r?void 0:r;if(e.action_index!==a)return!1;const s=ue(n?.action_params)||void 0;return we(e.action_params||{})===we(s||{})&&we(e.throw_if_empty||void 0)===we(t.throw_if_empty||void 0)}function Oe(e,t){if(e.path!==t.path)return!1;switch(e.path){case"accessibility/node":{const n=Se(e);return!!n&&ze(n,t)}case"permission/set":{const n=e.params||{},i=t.params||{};return"string"==typeof n.package_name&&n.package_name===i.package_name&&!0===n.grant_all&&!0===i.grant_all}case"activity/start":case"activity/stop":{const n=e.params?.package_name,i=t.params?.package_name;return"string"==typeof n&&n===i}case"input/text":return"string"==typeof e.params?.text&&e.params.text===t.params?.text;case"input/keyevent":return"number"==typeof e.params?.key_code&&e.params.key_code===t.params?.key_code;case"input/click":{const n=Ae(e.params||{}),i=Ae(t.params||{});return!!n&&n===i}case"input/scroll_bezier":{const n=ke(e.params||{}),i=ke(t.params||{});return!!n&&n===i}case"base/sleep":return!0;default:return!1}}function $e(e,t){const n=[],i=function(e){const t=[],n=new Set,i=new Set,o=new Set,r=new Set,a=new Set,s=new Set,c=new Set,l=new Set;for(const p of e.actions){for(const e of p.replay_plan.actions){const l=e.params||{};switch(e.path){case"accessibility/node":{const n=Se(e);n&&t.push(n);break}case"permission/set":"string"==typeof l.package_name&&!0===l.grant_all&&n.add(l.package_name);break;case"activity/start":"string"==typeof l.package_name&&i.add(l.package_name);break;case"activity/stop":"string"==typeof l.package_name&&o.add(l.package_name);break;case"input/text":"string"==typeof l.text&&r.add(l.text);break;case"input/keyevent":"number"==typeof l.key_code&&a.add(l.key_code);break;case"input/click":{const e=Ae(l);e&&s.add(e);break}case"input/scroll_bezier":{const e=ke(l);e&&c.add(e);break}}}if(p.replay_plan.verification){const e=Se(p.replay_plan.verification);e&&t.push(e)}if("exception_handler_candidate"!==p.classification.include_mode)continue;const e=ue(p.target.script_selector?.selector);if(!e)continue;const d=p.replay_plan.actions.find(e=>"accessibility/node"===e.path),u=ue(d?.params);l.add(ve({selector:e,action:"string"==typeof u?.action&&u.action.length>0?u.action:"click",action_params:ue(u?.action_params)||void 0}))}return{nodeActions:t,permissionPackages:n,startedPackages:i,stoppedPackages:o,inputTexts:r,keyCodes:a,clickPoints:s,scrollVectors:c,exceptionHandlers:l}}(t);for(const[t,o]of Object.entries(e.steps))for(let e=0;e<o.actions.length;e++){const r=o.actions[e],a=`${t}.actions[${e}]`;if("base/sleep"===r.path)continue;if("accessibility/node"===r.path){i.nodeActions.some(e=>ze(e,r))||n.push(`${a}: accessibility/node contract is not approved by RecordingArtifact`);continue}const s=r.params||{};if("permission/set"!==r.path)if("activity/start"!==r.path)if("activity/stop"!==r.path)if("input/text"!==r.path)if("input/keyevent"!==r.path){if("input/click"===r.path){const e=Ae(s);e&&i.clickPoints.has(e)||n.push(`${a}: input/click contract is not approved by RecordingArtifact`);continue}if("input/scroll_bezier"===r.path){const e=ke(s);e&&i.scrollVectors.has(e)||n.push(`${a}: input/scroll_bezier contract is not approved by RecordingArtifact`)}}else"number"==typeof s.key_code&&i.keyCodes.has(s.key_code)||n.push(`${a}: input/keyevent contract is not approved by RecordingArtifact`);else"string"==typeof s.text&&i.inputTexts.has(s.text)||n.push(`${a}: input/text contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&i.stoppedPackages.has(s.package_name)||n.push(`${a}: activity/stop contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&i.startedPackages.has(s.package_name)||n.push(`${a}: activity/start contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&!0===s.grant_all&&i.permissionPackages.has(s.package_name)||n.push(`${a}: permission/set contract is not approved by RecordingArtifact`)}for(let t=0;t<(e.exception_handlers||[]).length;t++){const o=e.exception_handlers[t],r=ve({selector:o.selector,action:o.action,action_params:o.action_params});i.exceptionHandlers.has(r)||n.push(`exception_handlers[${t}]: handler is not approved by RecordingArtifact`)}if(function(e,t,n){const i=function(e){const t=[];for(const n of e.flow){const i=e.steps[n];if(i)for(let e=0;e<i.actions.length;e++)t.push({stepId:n,actionIndex:e,action:i.actions[e]})}return t}(e);let o=0;for(const e of t.actions){if("main_flow"!==e.classification.include_mode)continue;if(0===e.replay_plan.actions.length)continue;for(const t of e.replay_plan.actions){for(;o<i.length&&("base/sleep"===i[o].action.path&&"base/sleep"!==t.path);)o++;const r=i[o];if(!r||!Oe(t,r.action))return void n.push(`${e.id}: replay_plan.actions are incomplete or out of order in workflow flow`);o++}const t=e.replay_plan.verification;if(!t||!0!==e.success_criteria.required||"ready"!==e.success_criteria.materialization)continue;let r=!1;for(;o<i.length&&"base/sleep"===i[o].action.path;)r=!0,o++;"explicit"===e.replay_plan.suggested_wait_source&&e.replay_plan.suggested_wait_ms&&!r&&n.push(`${e.id}: explicit pre-verification wait is missing in workflow flow`);const a=i[o];a&&Oe(t,a.action)?o++:n.push(`${e.id}: required post-action verification is missing from workflow flow`)}for(;o<i.length;o++){const e=i[o];"base/sleep"!==e.action.path&&n.push(`${e.stepId}.actions[${e.actionIndex}]: extra main-flow action is not required by RecordingArtifact sequence`)}}(e,t,n),n.length>0)throw new Error(`workflow artifact provenance validation failed:\n- ${n.join("\n- ")}`)}function Ie(e,t){const n=function(e){const t=e.steps;if(!t||"object"!=typeof t||Array.isArray(t))return e;const n=e.exception_handlers;if(Array.isArray(n))for(const e of n)if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;t.action||(t.action="click")}for(const e of Object.values(t)){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e,n=t.actions;if(Array.isArray(n)){for(const e of n){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;"wait"===t.path&&(t.path="base/sleep");const n=t.throw_if_empty;Array.isArray(n)&&n.includes("nodes")&&(t.throw_if_empty=["nodes"])}fe(t)}}return e}(e),i=pe.parse(n),o={...i,id:i.id||`workflow_${Date.now()}`,version:i.version||"1.0.0"};return function(e){for(const t of e.flow)if(!e.steps[t])throw new Error(`missing step referenced in flow: ${t}`)}(o),be(o),$e(o,t),o}async function Re(i,o,r,a,s){const c=f(r).getModel(),l=Math.max(1,a.scriptGeneration.maxAttempts),p=X(i,o),d=ne(i),u=ie(i,o),_=e.ChatPromptTemplate.fromMessages([["system","{systemPrompt}"],["human","{userMessage}"]]).pipe(c).pipe(new t.JsonOutputParser).pipe(new n.RunnableLambda({func:e=>Ie(e,p)})).withRetry({stopAfterAttempt:l});try{!function(e){const t=e.blockers||[];if(t.length>0)throw new Error(`recording artifact has blockers:\n- ${t.join("\n- ")}`)}(p);return{workflow:await _.invoke({systemPrompt:d,userMessage:u},{signal:s}),attempts:1,errors:[]}}catch(e){const t=function(e){const t=e?.message||String(e),n=t.toLowerCase();return n.includes("json")||n.includes("schema")||n.includes("zod")||n.includes("strict")?{category:"STRUCTURE",message:t}:n.includes("missing step")||n.includes("flow")||n.includes("workflow semantic validation")||n.includes("recording artifact has blockers")||n.includes("artifact provenance validation")?{category:"SEMANTIC",message:t}:{category:"UNKNOWN",message:t}}(e);throw new Error(`script generation failed (maxAttempts=${l}): [${t.category}] ${t.message}`)}}const je={retries:{observation:3,action:2},timeouts:{defaultMs:12e3,observeScreenMs:18e3,startAppMs:25e3},limits:{maxIterations:120,maxConsecutiveFailures:8,maxSameToolFingerprint:3},planner:{maxAttempts:3},scriptGeneration:{maxAttempts:3}};exports.ACCESSIBILITY_NODE_TOOLS=T,exports.DEFAULT_PROVIDER_CONFIG={vendor:"openai",baseUrl:"https://api.openai.com/v1",apiKey:"",model:"gpt-4o-mini"},exports.DEFAULT_RELIABILITY_CONFIG=je,exports.POST_VERIFY_HINT_TOOLS=M,exports.PRE_CONTEXT_REQUIRED_TOOLS=E,exports.SELECTOR_ACTION_TOOLS=N,exports.SOFT_VERIFY_ACTIONS=L,exports.STRICT_SCHEMA_TOOLS=C,exports.TOOL_TIMEOUT_OVERRIDES_MS={observe_screen:"observeScreenMs",start_app:"startAppMs"},exports.VERIFY_COMPLETION_TOOLS=q,exports.VERIFY_GATE_ALLOWED_TOOLS=j,exports.VERIFY_REQUIRED_ACTIONS=R,exports.buildAgentSystemPrompt=te,exports.buildOptimizeIntentPrompt=function(e){return Z(x(e))},exports.buildTaskPlanPrompt=ee,exports.createProvider=f,exports.detectPromptLocale=x,exports.generateScript=async function(e,t,n,i){return(await Re(e,t,n,je,i)).workflow},exports.generateScriptWithReliability=async function(e,t,n,i,o){return Re(e,t,n,i,o)};
@@ -0,0 +1 @@
1
+ import{ChatPromptTemplate as e}from"@langchain/core/prompts";import{JsonOutputParser as t}from"@langchain/core/output_parsers";import{RunnableLambda as n}from"@langchain/core/runnables";import{z as i}from"zod";import{ChatAnthropic as o}from"@langchain/anthropic";import{coerceMessageLikeToMessage as r}from"@langchain/core/messages";import{tool as a}from"@langchain/core/tools";import{ChatGoogleGenerativeAI as s}from"@langchain/google-genai";import{ChatOpenAI as c}from"@langchain/openai";import{createHash as l}from"crypto";function p(e){return e.map(e=>{if("assistant"===e.role){const t=(e.toolCalls||[]).map(e=>({type:"tool_call",id:e.id,name:e.name,args:e.arguments}));return r({role:"assistant",content:e.content,...t.length>0?{tool_calls:t}:{}})}return"tool"===e.role?r({role:"tool",content:e.content,tool_call_id:e.toolCallId||""}):r({role:e.role,content:e.content})})}function d(e){return e.map(e=>{return function(e){if(!e||"object"!=typeof e)return!1;const t=e;return"string"==typeof t.name&&"string"==typeof t.description&&!!t.schema&&"object"==typeof t.schema}(e)?a(async()=>"schema-only tool placeholder",{name:(t=e).name,description:t.description,schema:t.schema}):e;var t})}function u(e,t){if("string"!=typeof e||0===e.trim().length)throw new Error(`${t} is required`)}function _(e,t,n){return"anthropic"===e?function(e,t){return new o({model:e.model,apiKey:e.apiKey,temperature:.2,...e.baseUrl?{anthropicApiUrl:e.baseUrl}:{},...Object.keys(t).length>0?{clientOptions:{defaultHeaders:t}}:{}})}(t,n):"google"===e?function(e,t){return new s({model:e.model,apiKey:e.apiKey,temperature:.2,...e.baseUrl?{baseUrl:e.baseUrl}:{},...Object.keys(t).length>0?{customHeaders:t}:{}})}(t,n):function(e,t){return new c({model:e.model,apiKey:e.apiKey,temperature:.2,configuration:{...e.baseUrl?{baseURL:e.baseUrl}:{},...Object.keys(t).length>0?{defaultHeaders:t}:{}}})}(t,n)}class m{capabilities;vendor;model;constructor(e){const t={...e.headers||{}},n=function(e){const t=function(e){const t=(e||"").toLowerCase();return t.includes("deepseek")?"deepseek":t.includes("openai")?"openai":t.includes("custom")||t.includes("other")?"custom":t.includes("anthropic")||t.includes("claude")?"anthropic":t.includes("google")||t.includes("gemini")?"google":t.includes("azure")?"azure":"openai"}(String(e.vendor));return u(e.apiKey,"provider.apiKey"),u(e.model,"provider.model"),"anthropic"===t||"google"===t||u(e.baseUrl,"provider.baseUrl"),t}(e);this.vendor=n,this.capabilities=function(e){return{toolCalling:!0,structuredJson:!0,jsonMode:"openai"===e||"deepseek"===e||"azure"===e||"custom"===e}}(n),this.model=_(n,e,t)}getModel(){return this.model}async chatWithTools(e,t,n){const i=p(e),o=d(t),r=o.length>0&&"function"==typeof this.model.bindTools?this.model.bindTools(o):this.model,a=await r.invoke(i,{signal:n}),s="string"==typeof(c=a).text&&c.text.length>0?c.text:"string"==typeof c.content?c.content:Array.isArray(c.content)?c.content.map(e=>"string"==typeof e?e:e&&"object"==typeof e&&"text"in e&&"string"==typeof e.text?e.text:"").filter(e=>e.length>0).join("\n"):"";var c;const l=a.tool_calls||[],u=a.response_metadata&&"object"==typeof a.response_metadata?a.response_metadata:null;return{content:s,toolCalls:l.filter(e=>"string"==typeof e.name&&e.name.length>0).map((e,t)=>{return{id:"string"==typeof e.id&&e.id.length>0?e.id:`call_${Date.now()}_${t}`,name:e.name,arguments:(n=e.args,n&&"object"==typeof n&&!Array.isArray(n)?n:{})};var n}),finishReason:"string"==typeof u?.finish_reason&&u.finish_reason||"string"==typeof u?.stop_reason&&u.stop_reason||void 0}}async chatStructuredJson(e,t,n){const i=p(e),o="anthropic"===(r=this.vendor)||"google"===r?[{method:"jsonSchema"},{method:"functionCalling"}]:[{method:"jsonSchema",strict:!0},{method:"functionCalling",strict:!0},{method:"jsonMode"}];var r;let a=null;for(const e of o)try{const o=this.model.withStructuredOutput(t,{name:"structured_output",method:e.method,..."boolean"==typeof e.strict?{strict:e.strict}:{}}),r=await o.invoke(i,{signal:n});return t.parse(r)}catch(e){a=e}const s=a instanceof Error&&a.message?a.message:"langchain structured output failed with all methods";throw new Error(s)}}function f(e){return new m(e)}const g=/[\u3400-\u9fff]/u,y=[/\b\d+\s*(次|遍|个|条|张)\b/u,/第\s*\d+\s*(个|条|张)/u,/\b\d+\s*(times?|rounds?|steps?|items?)\b/i,/\bfor\s+\d+\s*(times?|rounds?)\b/i],h=[/直到/u,/等到/u,/出现/u,/找到/u,/\buntil\b/i,/\bwhen\b/i,/\bunless\b/i,/\bappears?\b/i,/\bfound\b/i],b=[/一直/u,/不停/u,/持续/u,/无限/u,/永远/u,/\bforever\b/i,/\bcontinuously\b/i,/\bendless(?:ly)?\b/i,/\bindefinitely\b/i,/\bkeep\b.*\bloop(?:ing)?\b/i];function x(e){return g.test(e||"")?"zh":"en"}function v(e){const t=e.trim();return t?y.some(e=>e.test(t))?"fixed_count":h.some(e=>e.test(t))?"until_condition":b.some(e=>e.test(t))?"infinite":"single_flow":"single_flow"}function w(e,t){return`## ${e}\n${t.map((e,t)=>`${t+1}. ${e}`).join("\n")}`}const k=["首次动作前先 observe_screen,禁止盲操作。","每个会引发状态变化的动作后(包括 tap_element、set_text、input_text、start_app 等),必须执行 observe_screen(wait_ms) -> verify_ui_state,验证后才能进入下一动作。跳过 verify_ui_state 会导致录制信息缺失,降低脚本生成成功率。","单轮最多一个状态变化动作;必须按”动作 -> wait(自适应时长) -> observe_screen -> verify_ui_state”闭环后再进入下一动作。","运行中仅执行与用户目标直接相关的动作;与任务无关的事情不要自行处理,禁止无关探索、浏览或演示性操作。","启动应用时优先使用包名;若当前只有应用名,先调用 get_installed_apps 确认 package_name,再使用 start_app,禁止臆测包名。","selector 必须来自最新 observe_screen 的真实内容。","selector 选择遵循“脚本优先”阶梯:稳定 resource_id -> 稳定 text -> 稳定 content_desc -> 最小组合 -> action_index。","校验场景(verify_ui_state / accessibility-node 纯查找)同样遵循最小唯一稳定策略,但每一步只用单字段(resource_id -> text -> content_desc),不做组合。","resource_id 唯一时只用 resource_id;若 resource_id 疑似混淆(如 obfuscated/数字前缀/高熵短串),不得单独依赖,退回稳定 text/content_desc。","组合唯一时优先 resource_id + 稳定 text/content_desc;禁止叠加动态值(时间、计数、row/column 等易变化文案)。bounds 仅作兜底消歧,不应作为通用脚本主 selector。","收到 VALIDATION 或 RUNTIME_GUARD 错误时,禁止原参数盲重试;必须先 observe_screen 刷新证据并调整 selector/动作。","禁止在无新观察证据的情况下重复完全相同的工具调用(同 tool + 同参数)。","界面方位只能依据 bounds 坐标与屏幕尺寸判断,禁止按节点顺序/缩进或经验臆测”顶部/底部”。","动作失败后最多允许一次替代路径,仍失败则暂停。","坐标点击仅作为 selector 无法稳定定位时的最后手段。","swipe 前必须先调用 record_search_context 记录查找目标与页面锚点;跳过会导致录制信息缺失,影响脚本生成。","index 仅在“同屏同语义节点多匹配且必须按顺序区分”时使用,且必须与 resource_id/text/content_desc/class_name/bounds 之一组合,禁止单独使用 index。","tap_element/long_press_element/set_text 的序号定位必须使用顶层 action_index,禁止 selector.index;verify_ui_state / record_search_context 可记录元素 index 作为补充证据。","当 text/content_desc 不稳定但仍需顺序区分时,且列表顺序稳定,使用 resource_id + action_index;若顺序不稳定则必须回退到更稳定锚点。","列表可能刷新、重排或异步插入时禁止依赖 index,优先稳定 resource_id 或稳定控件标签;若无法证明顺序稳定,则不要把该定位策略带入脚本。",'pre_context.step_intent 必须包含:(1)当前页面位置 (2)操作目的 (3)预期结果。例如"在抖音搜索结果页,点击第一个视频进入播放页",而非"点击视频"。record_search_context 的 step_intent 同理。',"若你能把当前动作抽象成稳定可复用的步骤语义,请显式填写 pre_context / record_search_context 中的 merge_key 和 expected_result。merge_key 应是稳定短语,不要直接抄界面文案。","若你明确知道该步骤通常需要额外等待再验证,请显式填写 wait_ms_hint;不要让脚本生成阶段再猜等待时长。","若你能明确判断页面类型、目标角色、步骤关键性或是否属于异常处理,请显式填写 pre_context / record_search_context 中的 page_kind、target_role、criticality_hint、include_mode_hint;若动作后页面类型也能明确判断,可填写 verify_ui_state.page_kind。不确定就省略,禁止靠文案关键词猜。",'pre_context.target.desc 和 anchor.desc 必须描述元素的视觉特征和语义角色,例如"底部导航栏中的首页按钮"或"搜索结果列表中的第一个视频卡片",不可只写"按钮"或"文本"。','verify_ui_state 中 screen_desc 必填,用一句话描述当前页面(如"抖音视频播放页"、"设置-通用页面"),帮助脚本生成理解上下文。',"当操作导致页面切换时,verify_ui_state 中必须包含 page_anchor(新页面的标志性元素),帮助脚本生成确认导航成功。"],A=["Call observe_screen before the first action; no blind operations.","After each state-changing action (including tap_element, set_text, input_text, start_app, etc.), you must do observe_screen(wait_ms) -> verify_ui_state before the next action. Skipping verify_ui_state causes missing recording data and lowers script generation success rate.","At most one state-changing action per turn; enforce the loop “act -> wait(adaptive) -> observe_screen -> verify_ui_state” before any next action.","During execution, only perform actions directly related to the user goal; do not handle unrelated tasks on your own, and avoid unrelated exploration, browsing, or demo-only actions.","Prefer package names when starting apps; if you only know the app name, call get_installed_apps to confirm package_name before using start_app, and never guess package names.","Selectors must come from the latest observe_screen evidence.","Selector choice must follow the script-safe ladder: stable resource_id -> stable text -> stable content_desc -> minimal combination -> action_index.","For verification scenarios (verify_ui_state / accessibility-node pure find), apply the same minimum-unique stability ladder, but single-field only (resource_id -> text -> content_desc), with no combinations.","If resource_id is unique, use only resource_id; if resource_id looks obfuscated (e.g. obfuscated/numeric prefix/high-entropy short token), do not rely on it alone and fall back to stable text/content_desc.","When combinations are required, prefer resource_id + stable text/content_desc; avoid dynamic values (time/count/row/column or other volatile copy). bounds is fallback-only and should not become a reusable script selector.","On VALIDATION or RUNTIME_GUARD errors, never blind-retry with the same arguments; observe_screen first, then adjust selector/action.","Do not repeat identical tool calls (same tool + same args) without new observation evidence.","Infer UI position only from bounds + screen size; never infer top/bottom from node order, indentation, or prior assumptions.","After an action failure, allow at most one alternative path; then pause.","Coordinate taps are last resort only when selector-based targeting is unstable.","Before swipe, you must call record_search_context to record the search target and page anchor; skipping causes missing recording data.","Use index only when same-screen nodes are still ambiguous and order is required; index must be combined with at least one of resource_id/text/content_desc/class_name/bounds, never used alone.","For tap_element/long_press_element/set_text, ordinal targeting must use top-level action_index, not selector.index; verify_ui_state / record_search_context may carry node index as supplemental evidence.","If text/content_desc is unstable but order still matters and list order is stable, use resource_id + action_index; if order is unstable, fall back to more stable anchors instead.","Do not rely on index when list order can change (refresh/reorder/async insertion); if order stability cannot be justified, do not carry that locator strategy into the script.",'pre_context.step_intent must include: (1) current page location (2) action purpose (3) expected result. Example: "On TikTok search results page, tap the first video to enter playback page", not just "tap video". Same for record_search_context step_intent.',"If you can abstract the current action into a stable reusable step semantic, fill merge_key and expected_result in pre_context or record_search_context. merge_key should be a stable short phrase, not copied screen text.","If you explicitly know the step usually needs additional wait before verification, fill wait_ms_hint so script generation does not have to guess timing.","If you can explicitly determine page kind, target role, step criticality, or whether a record is an exception handler candidate, fill page_kind/target_role/criticality_hint/include_mode_hint in pre_context or record_search_context. If the resulting page kind is also explicit after an action, you may fill verify_ui_state.page_kind. If unsure, omit them; never guess from copy keywords.",'pre_context.target.desc and anchor.desc must describe the element\'s visual characteristics and semantic role, e.g. "Home button in bottom navigation bar" or "First video card in search results list", not just "button" or "text".','verify_ui_state screen_desc is required — describe the current page in one sentence (e.g. "TikTok video playback page", "Settings - General page") to help script generation understand context.',"When an action causes a page transition, verify_ui_state must include page_anchor (a landmark element of the new page) to help script generation confirm navigation success."],S={zh:{fixed_count:["严格按目标次数执行,不得提前结束。","每轮结束后验证累计进度。"],until_condition:["每轮都必须“检查 -> 动作 -> 等待 -> 再检查”。","达到条件立即收敛,未命中到上限后结束。"],infinite:["仅当目标明确要求持续执行时允许无限循环。"],single_flow:["完成主线后立即结束,不做无关探索。"]},en:{fixed_count:["Execute exactly the requested count and never finish early.","Validate cumulative progress after each loop."],until_condition:['Every loop must follow "check -> act -> wait -> re-check".',"Converge immediately when condition is met; stop at limit otherwise."],infinite:["Infinite loop is allowed only when explicitly requested."],single_flow:["Finish immediately after the main path; avoid unrelated exploration."]}};function $(e,t){if(!e||0===e.subtasks.length)return"";const n=e.subtasks.map(e=>"zh"===t?`- ${e.id}. ${e.description} (成功标准: ${e.successCriteria})`:`- ${e.id}. ${e.description} (Success: ${e.successCriteria})`).join("\n");return"zh"===t?`## 任务计划参考\n${n}`:`## Plan Reference\n${n}`}const j=["输出必须是合法 JSON 对象,禁止 markdown 代码块。","计划必须基于当前屏幕状态,不允许假设未观察到的页面。","计划仅包含与用户目标直接相关的步骤,禁止加入与目标无关的探索性动作。","每个子任务都要有可观察的 successCriteria。","若目标存在前置状态依赖,必须先规划“进入前置状态 -> 验证前置状态”,再执行终态动作。","子任务按执行顺序排列,保持最短稳定路径。"],q=["Output must be a valid JSON object, never markdown.","Plan must be grounded in the current observed screen only.","Plan must include only steps directly related to the user goal; do not add unrelated exploratory actions.","Each subtask must include observable successCriteria.",'If goal has prerequisite state dependencies, plan "enter prerequisite state -> verify prerequisite state" before terminal actions.',"Subtasks must be ordered for the shortest stable path."],N={zh:{fixed_count:["识别用户指定次数,子任务中体现计数闭环。","避免把固定次数任务改写为条件循环。"],until_condition:["计划中显式写出停止条件和最大探索轮次。","停止条件必须可观察,不能用抽象描述。"],infinite:["仅在目标明确要求持续执行时使用无限模式。","仍需给出安全中断条件与风险说明。"],single_flow:["保持单次流程简洁,避免过度拆分。"]},en:{fixed_count:["Capture user-requested counts and enforce count closure in subtasks.","Do not rewrite fixed-count goals into condition loops."],until_condition:["State explicit stop condition and max exploration rounds.","Stop condition must be observable, not abstract."],infinite:["Use infinite mode only when user explicitly requests continuous execution.","Still provide safety stop condition and risks."],single_flow:["Keep single-flow plans concise and avoid over-splitting."]}};const z=new Set(["start_app","stop_app","tap","tap_element","long_press_element","press_key"]),I=new Set(["wait"]),O=new Set(["verify_ui_state"]),R=new Set(["tap_element","long_press_element","set_text"]),C=new Set(["tap_element","long_press_element","set_text"]),U=new Set([...C,"verify_ui_state","record_search_context"]),T=C,E=new Set([...C,"input_text"]),M=new Set(["start_app","stop_app","tap","tap_element","long_press_element","set_text","input_text","swipe","press_key"]),W={observe_screen:"observeScreenMs",start_app:"startAppMs"};function P(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function J(e){const t={};let n=!1;for(const[i,o]of Object.entries(e))null!=o&&""!==o&&(t[i]=o,n=!0);return n?t:void 0}function D(e){if("number"==typeof e&&Number.isInteger(e)&&e>=0)return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isInteger(t)&&t>=0)return t}}function K(e){if("number"==typeof e&&Number.isInteger(e)&&e>0)return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isInteger(t)&&t>0)return t}}function G(e,t){for(const n of t){const t=e[n];if("string"==typeof t){const e=t.trim();if(e.length>0)return e}}}function F(e){const t=P(e);if(t)return J({index:D(t.index),resource_id:G(t,["resource_id","resource-id","resourceId","id"]),text:G(t,["text"]),content_desc:G(t,["content_desc","content-desc","contentDesc"]),class_name:G(t,["class_name","class","className"]),bounds:G(t,["bounds"])})}function H(e){const t=P(e?.script_selector);if(!t)return;const n=P(t.selector);return n?{selector:n,action_index:D(t.action_index),stability:"string"==typeof t.stability?t.stability:void 0,reusable:"boolean"==typeof t.reusable?t.reusable:void 0,reason:"string"==typeof t.reason?t.reason:void 0,scope:"string"==typeof t.scope?t.scope:void 0,policy_decision:"string"==typeof t.policy_decision?t.policy_decision:void 0,policy_reason:"string"==typeof t.policy_reason?t.policy_reason:void 0}:void 0}function L(e){const t=P(e);if(!t)return;const n=G(t,["page_kind"]),i=G(t,["criticality_hint"]),o=G(t,["include_mode_hint"]),r=G(t,["valid_until"]);return{context_id:G(t,["context_id"]),page_kind:"home"===n||"list"===n||"detail"===n||"dialog"===n||"search"===n||"form"===n||"unknown"===n?n:void 0,target_role:G(t,["target_role"]),criticality_hint:"critical"===i||"supporting"===i||"noise"===i?i:void 0,include_mode_hint:"main_flow"===o||"log_only"===o||"exception_handler_candidate"===o?o:void 0,valid_until:"next_action"===r||"next_verified_transition"===r||"manual_close"===r?r:void 0}}function V(e,t){if(e||t)return{context_id:t?.context_id||e?.context_id,page_kind:t?.page_kind||e?.page_kind,target_role:t?.target_role||e?.target_role,criticality_hint:t?.criticality_hint||e?.criticality_hint,include_mode_hint:t?.include_mode_hint||e?.include_mode_hint,valid_until:t?.valid_until||e?.valid_until}}function Y(e){const t=new Set,n=[];for(const i of e)if(i&&!t.has(i)&&(t.add(i),n.push(i),n.length>=6))break;return n}function B(e){switch(e){case"start_app":return 1e4;case"tap":case"tap_element":case"long_press_element":case"press_key":return 6e3;case"set_text":case"input_text":return 3e3;case"swipe":return 1e3;default:return}}const Q=new Set(["tap_element","long_press_element","set_text"]),X=new Set(["start_app","stop_app","tap_element","long_press_element","set_text","input_text","press_key","swipe","tap","wait"]);function Z(e,t){const n=function(e){const t=[];let n,i=null;for(const o of e){const e=o.toolName,r=P(o.arguments)||{},a=P(o.result.data);if("get_screen_info"===e&&o.result.success){const e="number"==typeof a?.width?a.width:void 0,t="number"==typeof a?.height?a.height:void 0;e&&t&&(n={width:e,height:t});continue}if("observe_screen"===e||"get_installed_apps"===e)continue;if("record_search_context"===e){i={step_intent:G(r,["step_intent"]),merge_key:G(r,["merge_key"]),expected_result:G(r,["expected_result"]),wait_ms_hint:K(r.wait_ms_hint),target_desc:G(r,["target_desc"]),anchor:J(P(r.anchor_element)||{}),anchor_plan:P(a?.anchor_plan)||void 0,target:J(P(r.target_element)||{}),target_plan:P(a?.target_plan)||void 0,recorded_context:L(a?.recorded_context)};continue}if("verify_ui_state"===e){const e=t[t.length-1];if(!e)continue;e.verify=J(P(r.verify_element)||{}),e.verify_desc=G(r,["step_desc"]),e.page_anchor=J(P(r.page_anchor)||{}),e.screen_desc=G(r,["screen_desc"]);const n=G(P(a?.recorded_page)||{},["page_kind"])||G(r,["page_kind"]);e.verified_wait_ms_hint=K(r.wait_ms_hint),e.verified_page_kind="home"===n||"list"===n||"detail"===n||"dialog"===n||"search"===n||"form"===n||"unknown"===n?n:void 0,e.verification_plan=P(a?.verification_plan)||void 0,e.page_anchor_plan=P(a?.page_anchor_plan)||void 0,"next_verified_transition"===i?.recorded_context?.valid_until&&(i=null);continue}const s={seq:t.length+1,tool:e,success:o.result.success};o.result.success||"string"!=typeof o.result.error||(s.error=o.result.error),i&&(s.step_intent=i.step_intent,s.intent_merge_key=i.merge_key,s.expected_result_hint=i.expected_result,s.wait_ms_hint=i.wait_ms_hint,s.search_context=i.target_desc,!s.anchor&&i.anchor&&(s.anchor=i.anchor),i.anchor_plan&&(s.anchor_plan=i.anchor_plan),!s.target&&i.target&&(s.target=i.target),i.target_plan&&(s.target_plan=i.target_plan),i.recorded_context&&(s.recorded_context=i.recorded_context),"next_action"===i.recorded_context?.valid_until&&(i=null));const c=P(r.pre_context);c&&(s.step_intent=G(c,["step_intent"])||s.step_intent,s.intent_merge_key=G(c,["merge_key"])||s.intent_merge_key,s.expected_result_hint=G(c,["expected_result"])||s.expected_result_hint,s.wait_ms_hint=K(c.wait_ms_hint)||s.wait_ms_hint,s.target=J(P(c.target)||{})||s.target,s.anchor=J(P(c.anchor)||{})||s.anchor,s.recorded_context=V(s.recorded_context,L(c))),s.selector=J(P(r.selector)||{}),s.action_index=D(r.action_index),"string"==typeof r.text&&(s.text=r.text),"number"==typeof r.key_code&&(s.key_code=r.key_code),"string"==typeof r.package_name&&(s.package_name=r.package_name),"number"==typeof r.duration&&"wait"===e&&(s.wait_duration=r.duration),"number"==typeof r.x&&"number"==typeof r.y&&"tap"===e&&(s.tap_position={x:r.x,y:r.y}),"swipe"===e&&(s.swipe={start_x:Number(r.start_x),start_y:Number(r.start_y),end_x:Number(r.end_x),end_y:Number(r.end_y),duration:"number"==typeof r.duration?r.duration:300});const l=P(a?.runtime_selector_evidence);if(l){const e=J(P(l.requested_selector)||{});!s.selector&&e&&(s.selector=e),s.runtime_selected_node=F(l.selected_node),s.runtime_candidate_nodes=Array.isArray(l.candidate_nodes)?l.candidate_nodes.map(e=>F(e)).filter(e=>!!e):void 0,s.runtime_matched_count="number"==typeof l.matched_count?l.matched_count:void 0,s.selector_profile=P(l.selector_profile)||void 0;const t=D(l.action_index);void 0!==t&&(s.action_index=t)}t.push(s)}return{screen:n,actions:t}}(t),i=n.actions.map(e=>{const t=H(e.selector_profile),n=function(e){const t=H(e.page_anchor_plan),n=H(e.verification_plan);return t?{required:!0,materialization:"ready",signal_type:"page_arrived",rationale:"page transition uses page_anchor as the primary arrival signal",primary:t,fallback:n}:n?{required:!0,materialization:"ready",signal_type:"set_text"===e.tool||"input_text"===e.tool?"input_committed":"swipe"===e.tool?"list_progressed":"element_visible",rationale:"verify_ui_state provided a recording-time approved verification selector",primary:n}:e.verify?{required:!0,materialization:"missing_plan",signal_type:"element_visible",rationale:"raw verify evidence exists but recording-time verify plan is missing"}:e.success&&M.has(e.tool)?{required:!0,materialization:"missing_plan",signal_type:"set_text"===e.tool||"input_text"===e.tool?"input_committed":"swipe"===e.tool?"list_progressed":"state_changed",rationale:"state-changing action requires explicit post-action verification for unattended replay"}:{required:!1,materialization:"not_required",signal_type:e.success?"action_completed":"state_changed",rationale:"no post-action verification artifact recorded"}}(e),i=function(e,t){switch(e.tool){case"start_app":{const t=e.package_name;return t?[{path:"permission/set",params:{package_name:t,grant_all:!0}},{path:"activity/start",params:{package_name:t}}]:[]}case"stop_app":{const t=e.package_name;return t?[{path:"activity/stop",params:{package_name:t}}]:[]}case"tap_element":return t?[{path:"accessibility/node",params:J({selector:t.selector,action:"click",action_index:t.action_index,wait_timeout:2e3})}]:[];case"long_press_element":return t?[{path:"accessibility/node",params:J({selector:t.selector,action:"long_click",action_index:t.action_index,wait_timeout:2e3})}]:[];case"set_text":return t?[{path:"accessibility/node",params:J({selector:t.selector,action:"set_text",action_index:t.action_index,wait_timeout:2e3,action_params:e.text?{text:e.text}:void 0})}]:[];case"input_text":return e.text?[{path:"input/text",params:{text:e.text}}]:[];case"press_key":return"number"==typeof e.key_code?[{path:"input/keyevent",params:{key_code:e.key_code}}]:[];case"swipe":return e.swipe?[{path:"input/scroll_bezier",params:e.swipe}]:[];case"tap":return e.tap_position?[{path:"input/click",params:e.tap_position}]:[];case"wait":return"number"==typeof e.wait_duration?[{path:"base/sleep",params:{duration:e.wait_duration}}]:[];default:return[]}}(e,t),o=e.verified_wait_ms_hint||e.wait_ms_hint,r=function(e,t,n){if("ready"===e.materialization&&e.primary)return{path:"accessibility/node",params:{selector:e.primary.selector,wait_timeout:n||B(t)||5e3},throw_if_empty:["nodes"]}}(n,e.tool,o),a=function(e){const t=[],{record:n,scriptSelector:i,replayActions:o,successCriteria:r}=e;return Q.has(n.tool)&&!i&&t.push(`missing_script_selector: ${n.tool} requires a recording-approved script_selector`),X.has(n.tool)&&0===o.length&&t.push(`missing_replay_action: ${n.tool} has no replay_plan.actions`),r.required&&"ready"!==r.materialization&&t.push(`missing_verification_plan: verification is required but materialization=${r.materialization}`),t}({record:e,scriptSelector:t,replayActions:i,successCriteria:n}),s=G(e.anchor||{},["desc"])||e.search_context||e.step_intent,c=e.screen_desc||G(e.page_anchor||{},["desc"]),l=e.recorded_context?.page_kind||"unknown",p="page_arrived"===n.signal_type?e.verified_page_kind||"unknown":l,d=e.recorded_context?.criticality_hint,u=d||function(e,t){return"wait"===e.tool?"noise":"page_arrived"===t?"critical":e.success?"supporting":"noise"}(e,n.signal_type),_=e.recorded_context?.include_mode_hint,m=_||function(e){return"wait"===e.tool?"log_only":e.success?"main_flow":"log_only"}(e),f=e.step_intent||e.verify_desc||G(e.target||{},["desc"])||e.search_context||`${e.tool} #${e.seq}`,g=e.expected_result_hint||G(e.page_anchor||{},["desc"])||e.screen_desc||e.verify_desc||f,y=(e.intent_merge_key||f).normalize("NFKC").toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/gu,"_").replace(/^_+|_+$/gu,"")||"step";const h=o||B(e.tool),b=o?"explicit":h?"fallback":void 0;return{id:`action_${e.seq}`,seq:e.seq,tool:e.tool,success:e.success,error:e.error,page:{before:{kind:l,description:s,anchor:e.anchor_plan||e.anchor},after:e.screen_desc||e.page_anchor?{kind:p,description:c,anchor:e.page_anchor_plan||e.page_anchor||e.verify}:void 0,key_features:Y([s,c,e.search_context,G(e.target||{},["desc","text","content_desc"]),G(e.anchor||{},["desc","text","content_desc"])])},intent:{summary:f,purpose:f,expected_result:g,merge_key:y,sources:{expected_result:e.expected_result_hint?"explicit":"derived",merge_key:e.intent_merge_key?"explicit":"derived"}},target:{desc:G(e.target||{},["desc"])||e.search_context,role:e.recorded_context?.target_role||"generic_target",runtime_selector:e.selector,runtime_action_index:e.action_index,runtime_selected_node:e.runtime_selected_node,selector_profile:e.selector_profile,script_selector:t,selector_candidates:Array.isArray(e.selector_profile?.selector_candidates)?e.selector_profile?.selector_candidates:void 0},success_criteria:n,classification:{criticality:u,include_mode:m,step_is_key:"critical"===u,sources:{criticality:d?"explicit":"fallback",include_mode:_?"explicit":"fallback"}},replay_plan:{actions:i,suggested_wait_ms:h,suggested_wait_source:b,verification:r},diagnostics:{matched_count:e.runtime_matched_count,selector_reusable:t?.reusable},blockers:a.length>0?a:void 0}}),o=i.flatMap(e=>(e.blockers||[]).map(t=>`${e.id}: ${t}`));return{version:"recording-artifact/v2",goal:e,screen:n.screen,design_principle:"Recording artifact already contains approved script selectors, verification plans, merge keys, and replay actions. Script generation must translate, not infer.",actions:i,blockers:o.length>0?o:void 0}}function ee(e){return"zh"===e?"你是 Android 自动化需求优化助手。\n\n要求:\n1. 输出语言与输入一致。\n2. 只做表达优化,不新增用户未提及的目标。\n3. 保留次数、顺序、停止条件、应用名和关键文本。\n4. 输出仅保留优化后的最终描述,不要解释。":"You are an Android automation intent refinement assistant.\n\nRequirements:\n1. Keep output language consistent with input.\n2. Improve expression only; do not add new goals not mentioned by user.\n3. Preserve count, order, stop conditions, app names, and key text.\n4. Output only the final refined description without explanations."}function te(e){return ee(x(e))}function ne(e){return function(e){const t=x(e),n=v(e),i=N[t][n]||N[t].single_flow;return"zh"===t?["你是 Android 自动化任务规划助手。","根据用户目标和当前屏幕,生成可执行、可验证、可收敛的任务计划。",w("全局规则",j),w(`模式规则(${n})`,i),"## 输出 JSON 结构","{",' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',' "estimatedSteps": 1',"}"].join("\n\n"):["You are an Android automation planning assistant.","Generate an executable, verifiable, and convergent task plan based on goal and current screen.",w("Global Rules",q),w(`Mode Rules (${n})`,i),"## Output JSON Schema","{",' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',' "estimatedSteps": 1',"}"].join("\n\n")}(e)}function ie(e,t){return function(e,t){const n=x(e),i=v(e),o=S[n][i]||S[n].single_flow;return"zh"===n?["你是 Android 自动化执行 Agent。你的职责是在真实设备上高成功率完成目标。",w("核心执行规则",k),w(`任务模式规则(${i})`,o),"## 输出约束","- 优先一次只推进一个可验证子目标;若发生状态变化,必须先完成 wait -> observe_screen -> verify_ui_state 再继续。","- 非必要不输出长文本,优先通过 tool_calls 执行动作。","- 当缺少关键参数或验证证据时,立即暂停并明确提问。",$(t,n)].filter(Boolean).join("\n\n"):["You are an Android automation execution agent. Your objective is maximum completion reliability on real devices.",w("Core Execution Rules",A),w(`Task Mode Rules (${i})`,o),"## Output Constraints","- Advance one verifiable sub-goal at a time; after state changes, complete wait -> observe_screen -> verify_ui_state before continuing.","- Keep text brief unless necessary; prefer tool_calls for actions.","- If critical parameters or verification evidence are missing, pause and ask explicitly.",$(t,n)].filter(Boolean).join("\n\n")}(e,t)}function oe(e){return function(e){return"zh"===x(e)?'你是 Android WorkflowScript 组装器。输入不是原始日志,而是录制阶段已经提纯好的 RecordingArtifact。\n\n你的职责不是推理,而是翻译:\n1. 只消费 RecordingArtifact 中已经批准进入脚本的结构化信息。\n2. 不自行猜 selector,不自行猜页面意图,不自行猜成功判定条件。\n3. 不从 diagnostics、raw desc、候选节点中发明新 selector。\n\n## 输入格式\nRecordingArtifact.actions[*] 已经包含:\n- intent.merge_key: 建议的步骤合并键\n- target.script_selector: 录制阶段批准进入脚本的目标 selector\n- success_criteria.primary: 录制阶段批准的成功判定 selector\n- success_criteria.materialization: 验证条件是否已经具备可回放计划\n- replay_plan.actions: 已经整理好的主动作 skeleton\n- replay_plan.verification: 已经整理好的验证动作 skeleton\n- replay_plan.suggested_wait_source: explicit / fallback\n- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- blockers: 录制阶段已经识别出的阻断问题\n\n## 绝对规则\n1. 生成脚本时,优先直接使用 replay_plan.actions;不要自己重建 path/params。\n2. 只要 main_flow 记录存在 replay_plan.verification,就必须在该记录的 replay_plan.actions 之后立刻生成验证动作;两者之间只允许插入 base/sleep。\n3. 当 replay_plan.suggested_wait_source="explicit" 且 suggested_wait_ms 存在时,必须先插入 base/sleep,再执行 replay_plan.verification。\n4. classification.include_mode="log_only" 的记录默认不进入主流程。\n5. classification.include_mode="exception_handler_candidate" 的记录优先抽成 workflow 顶层 exception_handlers,而不是普通步骤。\n6. selector 只能来自 target.script_selector 或 success_criteria.primary/fallback;禁止从 target.desc、screen_desc、diagnostics 反推。\n7. 不得把 bounds、动态文案、候选节点文本重新拼成新 selector。\n8. 相邻且 merge_key 相同的 main_flow 记录优先合并为同一 step。\n9. 如果 replay_plan.actions 为空,不要编造动作;跳过该记录。\n10. completed 只允许字面量 "success",且仅在 loop.max_count 场景使用。\n11. 如果 RecordingArtifact.blockers 存在,或 action.blockers 非空,不要自行发明补救方案。\n12. 如果 success_criteria.required=true 但 materialization 不是 "ready",视为录制不完整。\n13. 输出纯 JSON,不要解释。\n\n## 工作流约束\n- 合法 path 仅限:activity/start, permission/set, activity/stop, accessibility/node, input/scroll_bezier, input/text, input/keyevent, input/click, base/sleep\n- accessibility/node 必须带正整数 wait_timeout\n- throw_if_empty 只能是 ["nodes"]\n- exception_handlers 只能放在 workflow 顶层\n\n## 输出格式(WorkflowScript)\n你必须输出一个合法的 WorkflowScript JSON,对象结构如下:\n\n```json\n{\n "id": "workflow_id",\n "name": "工作流名称",\n "version": "1.0.0",\n "description": "工作流描述",\n "exception_handlers": [\n {\n "name": "handler_name",\n "selector": { "text": "关闭" },\n "action": "click",\n "max_trigger_count": 3\n }\n ],\n "steps": {\n "open_result_detail": {\n "description": "步骤描述",\n "actions": [\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/video_card" }, "action": "click", "action_index": 0, "wait_timeout": 2000 } },\n { "path": "base/sleep", "params": { "duration": 1500 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 1500 }, "throw_if_empty": ["nodes"] }\n ]\n }\n },\n "flow": ["open_result_detail"]\n}\n```\n\n补充约束:\n- `steps` 必须是对象,key 为 step id,value 为 step 定义\n- 每个 step 必须有 `actions`,且至少 1 个 action\n- `flow` 必须按执行顺序列出 step id,且每个 id 都必须存在于 `steps`\n- 仅当使用 `loop.max_count` 时,才允许 `completed: "success"`\n- `exception_handlers` 可以是空数组,但每个 handler 都必须满足合法字段结构\n\n## 生成策略\n- 主流程:按 actions 顺序消费 include_mode="main_flow" 的记录\n- 异常处理:从 include_mode="exception_handler_candidate" 中提取\n- 步骤命名:基于 intent.merge_key 生成稳定英文 step id\n- 步骤描述:使用 intent.summary 或 page.before/after.description 组织一句简洁描述\n- 验证:使用 replay_plan.verification,禁止回退到人工推理\n\n输出 pure JSON only.':'You are an Android WorkflowScript assembler. The input is not a raw log. It is a RecordingArtifact that has already been refined during recording.\n\nYour job is translation, not investigation:\n1. Consume only the structured fields already approved for script generation.\n2. Do not infer selectors, page intent, or success criteria on your own.\n3. Do not invent selectors from diagnostics, raw descriptions, or candidate nodes.\n\n## Input format\nEach RecordingArtifact.actions[*] already includes:\n- intent.merge_key: suggested step merge key\n- target.script_selector: recording-approved selector for script usage\n- success_criteria.primary: recording-approved verification selector\n- success_criteria.materialization: whether verification is actually ready for replay\n- replay_plan.actions: prebuilt action skeletons\n- replay_plan.verification: prebuilt verification action skeleton\n- replay_plan.suggested_wait_source: explicit / fallback\n- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- blockers: recording-time blockers that make script generation unsafe\n\n## Hard rules\n1. Use replay_plan.actions directly whenever present; do not rebuild action params manually.\n2. Whenever a main_flow record has replay_plan.verification, you must emit that verification immediately after the record\'s replay_plan.actions. Only base/sleep may appear between them.\n3. When replay_plan.suggested_wait_source is "explicit" and suggested_wait_ms exists, you must insert base/sleep before replay_plan.verification.\n4. Records with classification.include_mode="log_only" should normally stay out of the main flow.\n5. Records with classification.include_mode="exception_handler_candidate" should become top-level exception_handlers first, not normal steps.\n6. Selectors may only come from target.script_selector or success_criteria.primary/fallback.\n7. Never reconstruct selectors from bounds, dynamic text, or candidate node text.\n8. Prefer merging adjacent main_flow records with the same merge_key into one step.\n9. If replay_plan.actions is empty, do not fabricate an action. Skip that record.\n10. completed may only be the literal "success", and only for loop.max_count.\n11. If RecordingArtifact.blockers exists or action.blockers is non-empty, do not invent a workaround.\n12. If success_criteria.required=true but materialization is not "ready", treat the recording as incomplete.\n13. Output pure JSON only.\n\n## Workflow constraints\n- Legal paths only: activity/start, permission/set, activity/stop, accessibility/node, input/scroll_bezier, input/text, input/keyevent, input/click, base/sleep\n- accessibility/node requires a positive integer wait_timeout\n- throw_if_empty must be exactly ["nodes"]\n- exception_handlers are top-level only\n\n## Output format (WorkflowScript)\nYou must output one valid WorkflowScript JSON object with this shape:\n\n```json\n{\n "id": "workflow_id",\n "name": "Workflow name",\n "version": "1.0.0",\n "description": "Workflow description",\n "exception_handlers": [\n {\n "name": "handler_name",\n "selector": { "text": "Close" },\n "action": "click",\n "max_trigger_count": 3\n }\n ],\n "steps": {\n "open_result_detail": {\n "description": "Step description",\n "actions": [\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/video_card" }, "action": "click", "action_index": 0, "wait_timeout": 2000 } },\n { "path": "base/sleep", "params": { "duration": 1500 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 1500 }, "throw_if_empty": ["nodes"] }\n ]\n }\n },\n "flow": ["open_result_detail"]\n}\n```\n\nAdditional constraints:\n- `steps` must be an object keyed by step id\n- every step must contain `actions` with at least one action\n- `flow` must list step ids in execution order, and every id must exist in `steps`\n- `completed: "success"` is only allowed with `loop.max_count`\n- `exception_handlers` may be an empty array, but every handler must follow the legal field shape\n\n## Generation strategy\n- Main flow: consume records with include_mode="main_flow" in order\n- Exception handling: extract from include_mode="exception_handler_candidate"\n- Step IDs: generate stable English ids from intent.merge_key\n- Step descriptions: use intent.summary or page.before/after.description\n- Verification: use replay_plan.verification only; never fall back to manual inference\n\nOutput pure JSON only.'}(e)}function re(e,t,n){return function(e,t){const n=x(e),i=Z(e,t),o=[];return i.screen&&o.push(`[Screen]\n${i.screen.width}x${i.screen.height}`),o.push(`[Goal]\n${e}`),o.push(`[RecordingArtifact]\n${JSON.stringify(i,null,2)}`),o.push("zh"===n?"请将 RecordingArtifact 翻译成最终 WorkflowScript。只使用 replay_plan、target.script_selector、success_criteria。不要自行推导 selector,不要补编未记录动作。如果 blockers 存在,不要发明补救方案。只输出 WorkflowScript JSON。":"Translate the RecordingArtifact into the final WorkflowScript. Use replay_plan, target.script_selector, and success_criteria only. Do not derive selectors or invent unrecorded actions. If blockers exist, do not invent a workaround. Output WorkflowScript JSON only."),o.join("\n\n")}(e,t)}const ae=ne(""),se=ie(""),ce=oe(""),le=ce.length;l("sha256").update([ee("zh"),ee("en"),ae,se,ce,String(le)].join("\n\n-----\n\n")).digest("hex");const pe=i.object({path:i.string().min(1),params:i.record(i.unknown()).optional(),throw_if_empty:i.array(i.string()).optional()}),de=i.object({description:i.string().optional(),completed:i.literal("success").optional(),loop:i.union([i.object({count:i.number().int(),interval:i.number().int().optional()}),i.object({max_count:i.number().int(),interval:i.number().int().optional()})]).optional(),actions:i.array(pe).min(1)}),ue=i.object({id:i.string().min(1),name:i.string().min(1),version:i.string().min(1),description:i.string().optional(),timeout:i.number().int().positive().optional(),exception_handlers:i.array(i.object({description:i.string().optional(),name:i.string().optional(),selector:i.record(i.unknown()),action:i.string().min(1),action_params:i.record(i.unknown()).optional(),max_trigger_count:i.number().int().positive().optional()})).optional(),steps:i.record(de),flow:i.array(i.string()).min(1)}),_e=new Set(["activity/start","permission/set","activity/stop","accessibility/node","input/scroll_bezier","input/text","input/keyevent","input/click","base/sleep"]);function me(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function fe(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function ge(e){if("number"==typeof e&&Number.isInteger(e))return e;if("string"==typeof e&&e.trim().length>0){const t=Number(e);if(Number.isFinite(t)&&Number.isInteger(t))return t}return null}function ye(e){const t=me(e.loop);if(!t)return void(fe(e,"completed")&&delete e.completed);const n=(Array.isArray(e.actions)?e.actions:[]).map(e=>me(e)).filter(e=>!!e);if(fe(t,"count")){return 1===ge(t.count)?(delete e.loop,void delete e.completed):void(fe(e,"completed")&&delete e.completed)}if(!fe(t,"max_count"))return;e.completed="success";if(n.some(e=>he(e.throw_if_empty)))return;const i=n.find(e=>{if("accessibility/node"!==e.path)return!1;const t=me(e.params);return!t||!fe(t,"action")});if(i)return void(i.throw_if_empty=["nodes"]);const o=n.find(e=>"accessibility/node"===e.path);if(o)return void(o.throw_if_empty=["nodes"]);const r=ge(t.max_count);if(null!==r){if(1===r)return delete e.loop,void delete e.completed;e.loop={count:r},delete e.completed}}function he(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]&&"nodes"===e[0]}function be(e,t,n,i){const o=`${e}.actions[${n}]`;if(_e.has(t.path)||i.push(`${o}: illegal path "${t.path}"`),t.throw_if_empty&&!he(t.throw_if_empty)&&i.push(`${o}: throw_if_empty must be exactly ["nodes"]`),"accessibility/node"===t.path){const e=t.params?.wait_timeout;"number"==typeof(r=e)&&Number.isInteger(r)&&r>0||i.push(`${o}: accessibility/node requires positive integer params.wait_timeout`)}var r}function xe(e,t,n){const i=t.steps[e],o=i.loop,r=i.completed;if(!o)return void(void 0!==r&&n.push(`${e}: steps without loop must not define completed`));if("count"in o)return 1===o.count&&n.push(`${e}: loop.count=1 is forbidden; remove loop`),void(void 0!==r&&n.push(`${e}: count loop must not define completed`));"success"!==r&&n.push(`${e}: max_count loop must define completed="success"`);i.actions.some(e=>he(e.throw_if_empty))||n.push(`${e}: max_count loop requires at least one action with throw_if_empty=["nodes"]`)}function ve(e){const t=[];for(const[n,i]of Object.entries(e.steps)){xe(n,e,t);for(let e=0;e<i.actions.length;e++)be(n,i.actions[e],e,t)}if(function(e,t){const n=new Set;for(const i of e.flow){const o=e.steps[i];for(let e=0;e<o.actions.length;e++){const r=o.actions[e],a=r.params||{},s=`${i}.actions[${e}]`;if("permission/set"===r.path){const e=a.package_name;if("string"!=typeof e||!e){t.push(`${s}: permission/set requires params.package_name`);continue}!0!==a.grant_all&&t.push(`${s}: permission/set requires params.grant_all=true`),n.add(e)}if("activity/start"===r.path){const e=a.package_name;if("string"!=typeof e||!e){t.push(`${s}: activity/start requires params.package_name`);continue}n.has(e)||t.push(`${s}: missing prior permission/set for package "${e}"`)}}}}(e,t),t.length>0)throw new Error(`workflow semantic validation failed:\n- ${t.join("\n- ")}`)}function we(e){if(Array.isArray(e))return e.map(e=>we(e));if(!e||"object"!=typeof e)return e;const t=e;return Object.keys(t).sort().reduce((e,n)=>(e[n]=we(t[n]),e),{})}function ke(e){return JSON.stringify(we({selector:e.selector,action:e.action,action_params:e.action_params}))}function Ae(e){return JSON.stringify(we(e))}function Se(e){const t=e.start_x,n=e.start_y,i=e.end_x,o=e.end_y;return"number"!=typeof t||"number"!=typeof n||"number"!=typeof i||"number"!=typeof o?null:Ae({start_x:t,start_y:n,end_x:i,end_y:o})}function $e(e){const t=e.x,n=e.y;return"number"!=typeof t||"number"!=typeof n?null:Ae({x:t,y:n})}function je(e){const t=e.params||{},n=me(t.selector);if(!n)return null;const i="string"==typeof t.action?t.action:void 0,o=ge(t.action_index);return{selector:n,action:i,action_index:null===o?void 0:o,action_params:me(t.action_params)||void 0,throw_if_empty:e.throw_if_empty}}function qe(e,t){if("accessibility/node"!==t.path)return!1;const n=me(t.params),i=me(n?.selector);if(!i)return!1;if(Ae(e.selector)!==Ae(i))return!1;const o="string"==typeof n?.action?n.action:void 0;if(e.action!==o)return!1;const r=ge(n?.action_index),a=null===r?void 0:r;if(e.action_index!==a)return!1;const s=me(n?.action_params)||void 0;return Ae(e.action_params||{})===Ae(s||{})&&Ae(e.throw_if_empty||void 0)===Ae(t.throw_if_empty||void 0)}function Ne(e,t){if(e.path!==t.path)return!1;switch(e.path){case"accessibility/node":{const n=je(e);return!!n&&qe(n,t)}case"permission/set":{const n=e.params||{},i=t.params||{};return"string"==typeof n.package_name&&n.package_name===i.package_name&&!0===n.grant_all&&!0===i.grant_all}case"activity/start":case"activity/stop":{const n=e.params?.package_name,i=t.params?.package_name;return"string"==typeof n&&n===i}case"input/text":return"string"==typeof e.params?.text&&e.params.text===t.params?.text;case"input/keyevent":return"number"==typeof e.params?.key_code&&e.params.key_code===t.params?.key_code;case"input/click":{const n=$e(e.params||{}),i=$e(t.params||{});return!!n&&n===i}case"input/scroll_bezier":{const n=Se(e.params||{}),i=Se(t.params||{});return!!n&&n===i}case"base/sleep":return!0;default:return!1}}function ze(e,t){const n=[],i=function(e){const t=[],n=new Set,i=new Set,o=new Set,r=new Set,a=new Set,s=new Set,c=new Set,l=new Set;for(const p of e.actions){for(const e of p.replay_plan.actions){const l=e.params||{};switch(e.path){case"accessibility/node":{const n=je(e);n&&t.push(n);break}case"permission/set":"string"==typeof l.package_name&&!0===l.grant_all&&n.add(l.package_name);break;case"activity/start":"string"==typeof l.package_name&&i.add(l.package_name);break;case"activity/stop":"string"==typeof l.package_name&&o.add(l.package_name);break;case"input/text":"string"==typeof l.text&&r.add(l.text);break;case"input/keyevent":"number"==typeof l.key_code&&a.add(l.key_code);break;case"input/click":{const e=$e(l);e&&s.add(e);break}case"input/scroll_bezier":{const e=Se(l);e&&c.add(e);break}}}if(p.replay_plan.verification){const e=je(p.replay_plan.verification);e&&t.push(e)}if("exception_handler_candidate"!==p.classification.include_mode)continue;const e=me(p.target.script_selector?.selector);if(!e)continue;const d=p.replay_plan.actions.find(e=>"accessibility/node"===e.path),u=me(d?.params);l.add(ke({selector:e,action:"string"==typeof u?.action&&u.action.length>0?u.action:"click",action_params:me(u?.action_params)||void 0}))}return{nodeActions:t,permissionPackages:n,startedPackages:i,stoppedPackages:o,inputTexts:r,keyCodes:a,clickPoints:s,scrollVectors:c,exceptionHandlers:l}}(t);for(const[t,o]of Object.entries(e.steps))for(let e=0;e<o.actions.length;e++){const r=o.actions[e],a=`${t}.actions[${e}]`;if("base/sleep"===r.path)continue;if("accessibility/node"===r.path){i.nodeActions.some(e=>qe(e,r))||n.push(`${a}: accessibility/node contract is not approved by RecordingArtifact`);continue}const s=r.params||{};if("permission/set"!==r.path)if("activity/start"!==r.path)if("activity/stop"!==r.path)if("input/text"!==r.path)if("input/keyevent"!==r.path){if("input/click"===r.path){const e=$e(s);e&&i.clickPoints.has(e)||n.push(`${a}: input/click contract is not approved by RecordingArtifact`);continue}if("input/scroll_bezier"===r.path){const e=Se(s);e&&i.scrollVectors.has(e)||n.push(`${a}: input/scroll_bezier contract is not approved by RecordingArtifact`)}}else"number"==typeof s.key_code&&i.keyCodes.has(s.key_code)||n.push(`${a}: input/keyevent contract is not approved by RecordingArtifact`);else"string"==typeof s.text&&i.inputTexts.has(s.text)||n.push(`${a}: input/text contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&i.stoppedPackages.has(s.package_name)||n.push(`${a}: activity/stop contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&i.startedPackages.has(s.package_name)||n.push(`${a}: activity/start contract is not approved by RecordingArtifact`);else"string"==typeof s.package_name&&!0===s.grant_all&&i.permissionPackages.has(s.package_name)||n.push(`${a}: permission/set contract is not approved by RecordingArtifact`)}for(let t=0;t<(e.exception_handlers||[]).length;t++){const o=e.exception_handlers[t],r=ke({selector:o.selector,action:o.action,action_params:o.action_params});i.exceptionHandlers.has(r)||n.push(`exception_handlers[${t}]: handler is not approved by RecordingArtifact`)}if(function(e,t,n){const i=function(e){const t=[];for(const n of e.flow){const i=e.steps[n];if(i)for(let e=0;e<i.actions.length;e++)t.push({stepId:n,actionIndex:e,action:i.actions[e]})}return t}(e);let o=0;for(const e of t.actions){if("main_flow"!==e.classification.include_mode)continue;if(0===e.replay_plan.actions.length)continue;for(const t of e.replay_plan.actions){for(;o<i.length&&("base/sleep"===i[o].action.path&&"base/sleep"!==t.path);)o++;const r=i[o];if(!r||!Ne(t,r.action))return void n.push(`${e.id}: replay_plan.actions are incomplete or out of order in workflow flow`);o++}const t=e.replay_plan.verification;if(!t||!0!==e.success_criteria.required||"ready"!==e.success_criteria.materialization)continue;let r=!1;for(;o<i.length&&"base/sleep"===i[o].action.path;)r=!0,o++;"explicit"===e.replay_plan.suggested_wait_source&&e.replay_plan.suggested_wait_ms&&!r&&n.push(`${e.id}: explicit pre-verification wait is missing in workflow flow`);const a=i[o];a&&Ne(t,a.action)?o++:n.push(`${e.id}: required post-action verification is missing from workflow flow`)}for(;o<i.length;o++){const e=i[o];"base/sleep"!==e.action.path&&n.push(`${e.stepId}.actions[${e.actionIndex}]: extra main-flow action is not required by RecordingArtifact sequence`)}}(e,t,n),n.length>0)throw new Error(`workflow artifact provenance validation failed:\n- ${n.join("\n- ")}`)}function Ie(e,t){const n=function(e){const t=e.steps;if(!t||"object"!=typeof t||Array.isArray(t))return e;const n=e.exception_handlers;if(Array.isArray(n))for(const e of n)if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;t.action||(t.action="click")}for(const e of Object.values(t)){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e,n=t.actions;if(Array.isArray(n)){for(const e of n){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;"wait"===t.path&&(t.path="base/sleep");const n=t.throw_if_empty;Array.isArray(n)&&n.includes("nodes")&&(t.throw_if_empty=["nodes"])}ye(t)}}return e}(e),i=ue.parse(n),o={...i,id:i.id||`workflow_${Date.now()}`,version:i.version||"1.0.0"};return function(e){for(const t of e.flow)if(!e.steps[t])throw new Error(`missing step referenced in flow: ${t}`)}(o),ve(o),ze(o,t),o}async function Oe(i,o,r,a,s){const c=f(r).getModel(),l=Math.max(1,a.scriptGeneration.maxAttempts),p=Z(i,o),d=oe(i),u=re(i,o),_=e.fromMessages([["system","{systemPrompt}"],["human","{userMessage}"]]).pipe(c).pipe(new t).pipe(new n({func:e=>Ie(e,p)})).withRetry({stopAfterAttempt:l});try{!function(e){const t=e.blockers||[];if(t.length>0)throw new Error(`recording artifact has blockers:\n- ${t.join("\n- ")}`)}(p);return{workflow:await _.invoke({systemPrompt:d,userMessage:u},{signal:s}),attempts:1,errors:[]}}catch(e){const t=function(e){const t=e?.message||String(e),n=t.toLowerCase();return n.includes("json")||n.includes("schema")||n.includes("zod")||n.includes("strict")?{category:"STRUCTURE",message:t}:n.includes("missing step")||n.includes("flow")||n.includes("workflow semantic validation")||n.includes("recording artifact has blockers")||n.includes("artifact provenance validation")?{category:"SEMANTIC",message:t}:{category:"UNKNOWN",message:t}}(e);throw new Error(`script generation failed (maxAttempts=${l}): [${t.category}] ${t.message}`)}}const Re={vendor:"openai",baseUrl:"https://api.openai.com/v1",apiKey:"",model:"gpt-4o-mini"},Ce={retries:{observation:3,action:2},timeouts:{defaultMs:12e3,observeScreenMs:18e3,startAppMs:25e3},limits:{maxIterations:120,maxConsecutiveFailures:8,maxSameToolFingerprint:3},planner:{maxAttempts:3},scriptGeneration:{maxAttempts:3}};async function Ue(e,t,n,i,o){return Oe(e,t,n,i,o)}async function Te(e,t,n,i){return(await Oe(e,t,n,Ce,i)).workflow}export{R as A,Ce as D,E as P,U as S,W as T,I as V,C as a,T as b,f as c,ne as d,x as e,ie as f,Te as g,Ue as h,O as i,z as j,M as k,te as l,Re as m};
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e=require("./chunks/scriptGenerator-0SXH7-UK.cjs");require("@langchain/core/prompts"),require("@langchain/core/output_parsers"),require("@langchain/core/runnables"),require("zod"),require("@langchain/anthropic"),require("@langchain/core/messages"),require("@langchain/core/tools"),require("@langchain/google-genai"),require("@langchain/openai"),require("crypto"),exports.generateScript=e.generateScript;