@vmosedge/workflow-agent-sdk 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/scriptGenerator-Dwf4MEHV.js +1 -0
- package/dist/chunks/scriptGenerator-uuk6ZSss.cjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/runtime/index.cjs +1 -1
- package/dist/runtime/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/scriptGenerator-CtSwPsED.js +0 -1
- package/dist/chunks/scriptGenerator-D_hIqEQc.cjs +0 -1
|
@@ -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 r}from"@langchain/anthropic";import{coerceMessageLikeToMessage as o}from"@langchain/core/messages";import{tool as s}from"@langchain/core/tools";import{ChatGoogleGenerativeAI as a}from"@langchain/google-genai";import{ChatOpenAI as c}from"@langchain/openai";import{zodToJsonSchema as l}from"zod-to-json-schema";function p(e){return"integer"===e||Array.isArray(e)&&e.includes("integer")}function _(e){if(Array.isArray(e))return e.map(e=>_(e));if(!e||"object"!=typeof e)return e;const t=e,n={};for(const[e,i]of Object.entries(t))"$schema"!==e&&"$defs"!==e&&"definitions"!==e&&"$ref"!==e&&"additionalProperties"!==e&&"strict"!==e&&"exclusiveMinimum"!==e&&"exclusiveMaximum"!==e&&(n[e]=_(i));const i=function(e){const t=e.exclusiveMinimum;if("number"==typeof t)return p(e.type)&&Number.isInteger(t)?t+1:t}(t);void 0!==i&&"number"!=typeof n.minimum&&(n.minimum=i);const r=function(e){const t=e.exclusiveMaximum;if("number"==typeof t)return p(e.type)&&Number.isInteger(t)?t-1:t}(t);return void 0!==r&&"number"!=typeof n.maximum&&(n.maximum=r),n}function u(e){return _(l(e,{$refStrategy:"none"}))}function d(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 o({role:"assistant",content:e.content,...t.length>0?{tool_calls:t}:{}})}return"tool"===e.role?o({role:"tool",content:e.content,tool_call_id:e.toolCallId||""}):o({role:e.role,content:e.content})})}function m(e,t){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)?function(e,t){return"google"===t?{type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}}:s(async()=>"schema-only tool placeholder",{name:e.name,description:e.description,schema:e.schema})}(e,t):e)}function f(e,t){if("string"!=typeof e||0===e.trim().length)throw new Error(`${t} is required`)}function g(e,t,n){return"anthropic"===e?function(e,t){return new r({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 a({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 y{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 f(e.apiKey,"provider.apiKey"),f(e.model,"provider.model"),"anthropic"===t||"google"===t||f(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=g(n,e,t)}getModel(){return this.model}async chatWithTools(e,t,n){const i=d(e),r=m(t,this.vendor),o=r.length>0&&"function"==typeof this.model.bindTools?this.model.bindTools(r):this.model,s=await o.invoke(i,{signal:n}),a="string"==typeof(c=s).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=s.tool_calls||[],p=s.response_metadata&&"object"==typeof s.response_metadata?s.response_metadata:null;return{content:a,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 p?.finish_reason&&p.finish_reason||"string"==typeof p?.stop_reason&&p.stop_reason||void 0}}async chatStructuredJson(e,t,n){const i=d(e),r="anthropic"===(o=this.vendor)||"google"===o?[{method:"jsonSchema"},{method:"functionCalling"}]:[{method:"jsonSchema",strict:!0},{method:"functionCalling",strict:!0},{method:"jsonMode"}];var o;let s=null;for(const e of r)try{const r="google"===this.vendor?"functionCalling"===e.method?{name:"structured_output",description:t.description||"Structured output schema",parameters:u(t)}:u(t):t,o=this.model.withStructuredOutput(r,{name:"structured_output",method:e.method,..."boolean"==typeof e.strict?{strict:e.strict}:{}}),s=await o.invoke(i,{signal:n});return t.parse(s)}catch(e){s=e}const a=s instanceof Error&&s.message?s.message:"langchain structured output failed with all methods";throw new Error(a)}}function h(e){return new y(e)}const x=/[\u3400-\u9fff]/u,b=[/\b\d+\s*(次|遍|个|条|张)\b/u,/第\s*\d+\s*(个|条|张)/u],v=[/直到/u,/等到/u,/出现/u,/找到/u],w=[/一直/u,/不停/u,/持续/u,/无限/u,/永远/u];function k(e){return x.test(e||"")?"zh":"en"}function $(e){const t=e.trim();return t?b.some(e=>e.test(t))?"fixed_count":v.some(e=>e.test(t))?"until_condition":w.some(e=>e.test(t))?"infinite":"single_flow":"single_flow"}function A(e,t){return`## ${e}\n${t.map((e,t)=>`${t+1}. ${e}`).join("\n")}`}const S=["每步前必须有最新 observe_screen / dump 证据,禁止基于过期界面继续执行。","每步后必须完成 success check;优先补齐 observe_screen -> verify_ui_state 再进入下一步。","页面发生变化后必须重新定位,不允许沿用旧页面的 selector 或入口。","必须按 stage 推进:一次只推进一个已验证子目标,不允许无限漂移或脱离当前阶段。","连续失败必须进入恢复分支,优先收敛、重试替代入口或暂停,而不是盲目重复。","高风险动作必须有显式授权,若用户目标未明确授权,不得擅自执行。"],N=["selector 优先最小唯一。","优先稳定属性,避免动态 text。","优先语义定位,不行再坐标。","可处理弹窗、广告、权限框。","可根据页面变化自动切换入口。"],j=["首次动作前先 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(新页面的标志性元素),帮助脚本生成确认导航成功。","如果 verify_ui_state 返回“补录提醒”或提示未形成可回放 verification_plan,优先重新 observe_screen 并补一个可唯一回放的 verify_element/page_anchor;若当前主流程已明确,也可以继续执行,但要接受录制质量下降。"],q={fixed_count:["严格按目标次数执行,不得提前结束。","每轮结束后验证累计进度。"],until_condition:["每轮都必须“检查 -> 动作 -> 等待 -> 再检查”。","达到条件立即收敛,未命中到上限后结束。"],infinite:["仅当目标明确要求持续执行时允许无限循环。"],single_flow:["完成主线后立即结束,不做无关探索。"]};function z(e){if(!e||0===e.subtasks.length)return"";return`## 任务计划参考\n${e.subtasks.map(e=>`- ${e.id}. ${e.description} (成功标准: ${e.successCriteria})`).join("\n")}`}const M=["输出必须是合法 JSON 对象,禁止 markdown 代码块。","计划必须基于当前屏幕状态,不允许假设未观察到的页面。","计划仅包含与用户目标直接相关的步骤,禁止加入与目标无关的探索性动作。","每个子任务都要有可观察的 successCriteria。","若目标存在前置状态依赖,必须先规划“进入前置状态 -> 验证前置状态”,再执行终态动作。","子任务按执行顺序排列,保持最短稳定路径。"],O={fixed_count:["识别用户指定次数,子任务中体现计数闭环。","避免把固定次数任务改写为条件循环。"],until_condition:["计划中显式写出停止条件和最大探索轮次。","停止条件必须可观察,不能用抽象描述。"],infinite:["仅在目标明确要求持续执行时使用无限模式。","仍需给出安全中断条件与风险说明。"],single_flow:["保持单次流程简洁,避免过度拆分。"]};const C=new Set(["start_app","stop_app","tap","tap_element","long_press_element","press_key"]),I=new Set(["verify_ui_state"]),U=new Set(["tap_element","long_press_element","set_text"]),R=new Set(["tap_element","long_press_element","set_text"]);new Set([...R,"verify_ui_state","record_search_context"]);const J=R,W=new Set([...R,"input_text"]),K=new Set(["start_app","stop_app","tap","tap_element","long_press_element","set_text","input_text","swipe","press_key"]),E={observe_screen:"observeScreenMs",start_app:"startAppMs"};function T(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function D(e){const t={};let n=!1;for(const[i,r]of Object.entries(e))null!=r&&""!==r&&(t[i]=r,n=!0);return n?t:void 0}function L(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){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 P(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 G(e){const t=T(e);if(t)return D({index:L(t.index),resource_id:P(t,["resource_id","resource-id","resourceId","id"]),text:P(t,["text"]),content_desc:P(t,["content_desc","content-desc","contentDesc"]),class_name:P(t,["class_name","class","className"]),bounds:P(t,["bounds"])})}function H(e){const t=T(e?.script_selector);if(!t)return;const n=T(t.selector);return n?{selector:n,action_index:L(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 V(e){const t=T(e);if(!t)return;const n=P(t,["page_kind"]),i=P(t,["criticality_hint"]),r=P(t,["include_mode_hint"]),o=P(t,["valid_until"]);return{context_id:P(t,["context_id"]),page_kind:"home"===n||"list"===n||"detail"===n||"dialog"===n||"search"===n||"form"===n||"unknown"===n?n:void 0,target_role:P(t,["target_role"]),criticality_hint:"critical"===i||"supporting"===i||"noise"===i?i:void 0,include_mode_hint:"main_flow"===r||"log_only"===r||"exception_handler_candidate"===r?r:void 0,valid_until:"next_action"===o||"next_verified_transition"===o||"manual_close"===o?o:void 0}}function B(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 Q(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}const X=3e3;const Y=new Set(["tap_element","long_press_element","set_text"]),Z=new Set(["start_app","stop_app","tap_element","long_press_element","set_text","input_text","press_key","swipe","tap","wait"]);function ee(e,t){const n=function(e){const t=[];let n,i=null;for(const r of e){const e=r.toolName,o=T(r.arguments)||{},s=T(r.result.data);if("get_screen_info"===e&&r.result.success){const e="number"==typeof s?.width?s.width:void 0,t="number"==typeof s?.height?s.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:P(o,["step_intent"]),merge_key:P(o,["merge_key"]),expected_result:P(o,["expected_result"]),wait_ms_hint:F(o.wait_ms_hint),target_desc:P(o,["target_desc"]),anchor:D(T(o.anchor_element)||{}),anchor_plan:T(s?.anchor_plan)||void 0,target:D(T(o.target_element)||{}),target_plan:T(s?.target_plan)||void 0,recorded_context:V(s?.recorded_context)};continue}if("verify_ui_state"===e){const e=t[t.length-1];if(!e)continue;e.verify=D(T(o.verify_element)||{}),e.verify_desc=P(o,["step_desc"]),e.page_anchor=D(T(o.page_anchor)||{}),e.screen_desc=P(o,["screen_desc"]);const n=P(T(s?.recorded_page)||{},["page_kind"])||P(o,["page_kind"]);e.verified_wait_ms_hint=F(o.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=T(s?.verification_plan)||void 0,e.page_anchor_plan=T(s?.page_anchor_plan)||void 0,"next_verified_transition"===i?.recorded_context?.valid_until&&(i=null);continue}const a={seq:t.length+1,tool:e,success:r.result.success};r.result.success||"string"!=typeof r.result.error||(a.error=r.result.error),i&&(a.step_intent=i.step_intent,a.intent_merge_key=i.merge_key,a.expected_result_hint=i.expected_result,a.wait_ms_hint=i.wait_ms_hint,a.search_context=i.target_desc,!a.anchor&&i.anchor&&(a.anchor=i.anchor),i.anchor_plan&&(a.anchor_plan=i.anchor_plan),!a.target&&i.target&&(a.target=i.target),i.target_plan&&(a.target_plan=i.target_plan),i.recorded_context&&(a.recorded_context=i.recorded_context),"next_action"===i.recorded_context?.valid_until&&(i=null));const c=T(o.pre_context);c&&(a.step_intent=P(c,["step_intent"])||a.step_intent,a.intent_merge_key=P(c,["merge_key"])||a.intent_merge_key,a.expected_result_hint=P(c,["expected_result"])||a.expected_result_hint,a.wait_ms_hint=F(c.wait_ms_hint)||a.wait_ms_hint,a.target=D(T(c.target)||{})||a.target,a.anchor=D(T(c.anchor)||{})||a.anchor,a.recorded_context=B(a.recorded_context,V(c))),a.selector=D(T(o.selector)||{}),a.action_index=L(o.action_index),"string"==typeof o.text&&(a.text=o.text),"number"==typeof o.key_code&&(a.key_code=o.key_code),"string"==typeof o.package_name&&(a.package_name=o.package_name),"number"==typeof o.duration&&"wait"===e&&(a.wait_duration=o.duration),"number"==typeof o.x&&"number"==typeof o.y&&"tap"===e&&(a.tap_position={x:o.x,y:o.y}),"swipe"===e&&(a.swipe={start_x:Number(o.start_x),start_y:Number(o.start_y),end_x:Number(o.end_x),end_y:Number(o.end_y),duration:"number"==typeof o.duration?o.duration:300});const l=T(s?.runtime_selector_evidence);if(l){const e=D(T(l.requested_selector)||{});!a.selector&&e&&(a.selector=e),a.runtime_selected_node=G(l.selected_node),a.runtime_candidate_nodes=Array.isArray(l.candidate_nodes)?l.candidate_nodes.map(e=>G(e)).filter(e=>!!e):void 0,a.runtime_matched_count="number"==typeof l.matched_count?l.matched_count:void 0,a.selector_profile=T(l.selector_profile)||void 0;const t=L(l.action_index);void 0!==t&&(a.action_index=t)}t.push(a)}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&&K.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:D({selector:t.selector,action:"click",action_index:t.action_index,wait_timeout:X})}]:[];case"long_press_element":return t?[{path:"accessibility/node",params:D({selector:t.selector,action:"long_click",action_index:t.action_index,wait_timeout:X})}]:[];case"set_text":return t?[{path:"accessibility/node",params:D({selector:t.selector,action:"set_text",action_index:t.action_index,wait_timeout:X,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),r=function(e,t){if("ready"!==e.materialization||!e.primary)return;const n="number"==typeof t&&Number.isFinite(t)&&t>0?Math.max(5e3,Math.trunc(t)):5e3;return{path:"accessibility/node",params:{selector:e.primary.selector,wait_timeout:n},throw_if_empty:["nodes"]}}(n,e.verified_wait_ms_hint||e.wait_ms_hint),o=function(e){const t=[],{record:n,scriptSelector:i,replayActions:r,successCriteria:o}=e;return Y.has(n.tool)&&!i&&t.push(`missing_script_selector: ${n.tool} requires a recording-approved script_selector`),Z.has(n.tool)&&0===r.length&&t.push(`missing_replay_action: ${n.tool} has no replay_plan.actions`),o.required&&"ready"!==o.materialization&&t.push(`missing_verification_plan: verification is required but materialization=${o.materialization}`),t}({record:e,scriptSelector:t,replayActions:i,successCriteria:n}),s=P(e.anchor||{},["desc"])||e.search_context||e.step_intent,a=e.screen_desc||P(e.page_anchor||{},["desc"]),c=e.recorded_context?.page_kind||"unknown",l="page_arrived"===n.signal_type?e.verified_page_kind||"unknown":c,p=e.recorded_context?.criticality_hint,_=p||function(e,t){return"wait"===e.tool?"noise":"page_arrived"===t?"critical":e.success?"supporting":"noise"}(e,n.signal_type),u=e.recorded_context?.include_mode_hint,d=u||function(e){return"wait"===e.tool?"log_only":e.success?"main_flow":"log_only"}(e),m=e.step_intent||e.verify_desc||P(e.target||{},["desc"])||e.search_context||`${e.tool} #${e.seq}`,f=e.expected_result_hint||P(e.page_anchor||{},["desc"])||e.screen_desc||e.verify_desc||m,g=(e.intent_merge_key||m).normalize("NFKC").toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/gu,"_").replace(/^_+|_+$/gu,"")||"step";return{id:`action_${e.seq}`,seq:e.seq,tool:e.tool,success:e.success,error:e.error,page:{before:{kind:c,description:s,anchor:e.anchor_plan||e.anchor},after:e.screen_desc||e.page_anchor?{kind:l,description:a,anchor:e.page_anchor_plan||e.page_anchor||e.verify}:void 0,key_features:Q([s,a,e.search_context,P(e.target||{},["desc","text","content_desc"]),P(e.anchor||{},["desc","text","content_desc"])])},intent:{summary:m,purpose:m,expected_result:f,merge_key:g,sources:{expected_result:e.expected_result_hint?"explicit":"derived",merge_key:e.intent_merge_key?"explicit":"derived"}},target:{desc:P(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:_,include_mode:d,step_is_key:"critical"===_,sources:{criticality:p?"explicit":"fallback",include_mode:u?"explicit":"fallback"}},replay_plan:{actions:i,verification:r},diagnostics:{matched_count:e.runtime_matched_count,selector_reusable:t?.reusable},warnings:o.length>0?o:void 0}}),r=i.flatMap(e=>(e.warnings||[]).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,warnings:r.length>0?r:void 0}}const te="顶层字段:\n- version: RecordingArtifact 格式版本。\n- goal: 用户最终目标,决定脚本整体意图。\n- screen: 录制时屏幕尺寸,仅用于理解坐标类动作。\n- design_principle: 录制产物的设计原则,提醒你“翻译已有信息,而不是补推未知信息”。\n- warnings: 全局录制质量提醒,不阻塞生成。\n- actions: 已格式化的执行记录数组,按 seq 升序排列。\n\n单条 actions[*] 字段:\n- id: 该条记录的稳定 ID。\n- seq: 原始执行顺序。主流程应尊重这个顺序。\n- tool: 录制时调用的工具名。\n- success: 该次工具调用是否成功。\n- error: 失败时的错误信息;success=true 时通常为 null。\n\n页面上下文 page:\n- page.before: 动作前页面信息。\n- page.after: 动作后页面信息;如果缺失,表示录制时没有拿到可靠的动作后页面证据。\n- page.key_features: 页面关键特征摘要,只用于帮助你理解页面,不用于拼 selector。\n- page.before/after.kind: 粗粒度页面类型,例如 list/detail/dialog。\n- page.before/after.description: 页面自然语言描述,可用于步骤描述。\n- page.before/after.anchor: 录制时的页面锚点证据,可帮助理解页面是否切换。\n\n步骤意图 intent:\n- intent.summary: 最短步骤摘要。优先用于 step description。\n- intent.purpose: 该动作为什么执行。\n- intent.expected_result: 该动作执行后希望达到的结果。\n- intent.merge_key: 步骤命名和合并时可参考的稳定键,仅作提示,不足以单独决定是否合并或生成循环。\n\n目标与选择器 target:\n- target.desc: 人类可读的目标描述。\n- target.role: 目标元素角色提示。\n- target.runtime_selector: 录制时实际传入的 selector,仅作背景参考,不等于最终脚本批准 selector。\n- target.runtime_action_index: 录制时使用的序号定位。\n- target.runtime_selected_node: 录制时命中的真实节点快照。\n- target.selector_profile: 录制阶段对 selector 质量的分析信息。\n- target.script_selector: 已批准进入脚本的 selector。这是生成主动作时最优先的 selector 来源。\n- target.selector_candidates: 候选 selector 证据,仅用于理解,不用于重新拼接新 selector。\n\n成功判定 success_criteria:\n- success_criteria.required: 该动作理论上是否需要动作后验证。\n- success_criteria.materialization: ready 表示验证动作已可回放;missing_plan 表示需要验证但录制没形成可回放方案;not_required 表示无需验证。\n- success_criteria.signal_type: 该动作成功信号的类型,如 page_arrived / element_visible / state_changed。\n- success_criteria.rationale: 为什么采用这个成功判定策略。\n- success_criteria.primary/fallback: 已批准的验证 selector。只有存在时才能用于生成验证步骤。\n\n流程分类 classification:\n- classification.criticality: 对整体任务的重要程度。\n- classification.include_mode: main_flow / exception_handler_candidate / log_only。\n- classification.step_is_key: 是否是关键里程碑动作。\n\n可回放计划 replay_plan:\n- replay_plan.actions: 已批准的主动作 skeleton。生成主流程时优先直接使用。\n- replay_plan.verification: 已批准的验证动作 skeleton。只有存在时才生成验证。\n\n诊断 diagnostics 与 warnings:\n- diagnostics.matched_count: 录制时 selector 命中的节点数。\n- diagnostics.selector_reusable: selector 是否看起来可复用。\n- warnings: 当前 action 的录制质量提醒,不阻塞生成。\n\n解释原则:\n- null / [] / {} 表示“没录到”或“无可用信息”,直接忽略。\n- 最高可信字段是 replay_plan、target.script_selector、success_criteria.primary/fallback。\n- diagnostics、runtime_selector、runtime_selected_node、selector_candidates 主要用于理解上下文,不用于发明新 selector。",ne=`你是 Android 无人值守 WorkflowScript 工程师。输入不是原始日志,而是录制阶段已经提纯好的 RecordingArtifact。\n\n角色:\n- 你负责把已有执行记录翻译成稳定、可靠、可无人值守执行的 WorkflowScript。\n\n任务:\n- 基于 RecordingArtifact 生成最终脚本。\n- 优先复用已批准的 replay_plan、script_selector、verification selector。\n- 在信息缺失时继续基于已有记录生成,而不是阻塞或补编未知信息。\n\n上下文:\n1. 只消费 RecordingArtifact 中已经批准进入脚本的结构化信息。\n2. 不自行猜 selector,不自行猜页面意图,不自行猜成功判定条件。\n3. 不从 diagnostics、raw desc、候选节点中发明新 selector。\n4. 你的目标是无人值守稳定回放,所以优先选择可回放、可验证、可复用的记录,而不是追求“看起来完整”。\n\n期望:\n- 输出纯 JSON WorkflowScript。\n- 主流程顺序清晰,验证闭环尽量完整。\n- 对缺失字段容忍,但绝不凭空补造。\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- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- warnings: 录制阶段已经识别出的非阻断缺口(例如缺少可回放 verification_plan)\n- 某些字段可能是 null / [] / {}:这表示录制阶段没有拿到该信息,直接忽略,不要自行补编\n\n## 字段解释\n${te}\n\n## 无人值守优先级\n1. 可回放性优先于表面完整性。\n2. 有 replay_plan.actions 就优先生成动作;没有就跳过,不补编。\n3. 有 replay_plan.verification 才生成验证;没有就接受该缺口,但不要发明验证。\n4. 有 target.script_selector / success_criteria.primary 才把 selector 写进脚本;没有就不要从其他字段反推。\n5. log_only 默认不进入主流程;exception_handler_candidate 优先转成 exception_handlers。\n\n## 绝对规则\n1. 生成脚本时,优先直接使用 replay_plan.actions;不要自己重建 path/params。\n2. 只要 main_flow 记录存在 replay_plan.verification,就必须在该记录的 replay_plan.actions 之后生成“base/sleep -> 验证动作”的闭环。\n3. base/sleep 的 duration 优先由你根据当前场景生成;若你无法判断,可省略 duration,系统会兜底为 2000ms。\n4. 带 action 的 accessibility/node params.wait_timeout 至少 3000ms;replay_plan.verification 的 params.wait_timeout 优先使用已给值,若你输出时缺失、不合法或低于最小值,系统会对纯校验 accessibility/node 兜底并提升到至少 5000ms。\n5. classification.include_mode="log_only" 的记录默认不进入主流程。\n6. classification.include_mode="exception_handler_candidate" 的记录优先抽成 workflow 顶层 exception_handlers,而不是普通步骤。\n7. selector 只能来自 target.script_selector 或 success_criteria.primary/fallback;禁止从 target.desc、screen_desc、diagnostics 反推。\n8. 不得把 bounds、动态文案、候选节点文本重新拼成新 selector。\n9. merge_key 仅作为 step 分组提示;只有当相邻 main_flow 记录确实表达同一可复用步骤语义、且目标模式一致时,才合并为同一 step。\n10. 如果 replay_plan.actions 为空,不要编造动作;跳过该记录。\n11. completed 只允许字面量 "success",且仅在 loop.max_count 场景使用。\n12. 如果 RecordingArtifact.warnings 存在,或 action.warnings 非空,继续基于已有 replay_plan 生成,但不要补编缺失的 selector / 验证动作。\n13. 如果 success_criteria.required=true 但 materialization 不是 "ready",视为录制不完整;保留已批准的 replay_plan.actions,但不要发明 replay_plan.verification。\n14. 输出纯 JSON,不要解释。\n15. WorkflowScript 中所有自然语言字段(如 name、description、step.description、exception_handlers[].name/description)必须与用户目标语言一致;step id 仍保持稳定英文。\n\n## 循环生成规则(LoopConfig)\nstep 支持 loop 字段实现循环执行,两种模式二选一:\n\n模式一:固定次数循环 \`{ "count": N }\`\n- 执行固定 N 次,不检查 completed。N=-1 表示无限循环(需外部取消)。\n- 禁止 count=1(无意义)或 count=0。\n- 禁止搭配 completed。\n\n模式二:条件循环 \`{ "max_count": N }\` + \`"completed": "success"\`\n- 每次循环后检查 completed 条件(由 throw_if_empty=["nodes"] 的 accessibility/node 驱动),找到目标元素即退出。\n- max_count=-1 表示无限循环直到成功。\n- 必须搭配 completed="success" 和至少一个带 throw_if_empty=["nodes"] 的 accessibility/node 动作。\n\n两种模式均可加 \`"interval": ms\` 指定循环间隔。\n\n### 何时生成循环\n1. 只有当用户目标和录制动作都清楚表达“同一操作需要重复执行”时,才生成 loop;不要仅凭 merge_key 相同就强制抽象成循环。\n2. 如果相邻动作虽然相似,但点击目标、selector、action_index、预期结果或页面语义不同,应保持为展开步骤,不要合并成 loop。\n3. 如果用户目标中包含"刷N个""滑动N次""连续播放"等明确重复意图,用 loop.count=N 或 loop.max_count=N。\n4. 如果目标是"找到某个元素"类的搜索滑动(swipe + 找目标),使用 max_count + completed="success",在 actions 中放置: 滑动动作 → base/sleep → accessibility/node(查找目标, throw_if_empty=["nodes"])。\n5. 如果用户目标含"一直""持续""不断"等无限循环意图,使用 count=-1 或 max_count=-1。\n6. 对不涉及重复的普通步骤,不要添加 loop。\n\n### 循环步骤示例\n\n滑动查找目标(条件循环):\n\`\`\`json\n{\n "scroll_find_target": {\n "description": "向下滑动列表查找目标内容",\n "loop": { "max_count": 10, "interval": 500 },\n "completed": "success",\n "actions": [\n { "path": "input/scroll_bezier", "params": { "start_x": 540, "start_y": 1600, "end_x": 540, "end_y": 600, "duration": 300 } },\n { "path": "base/sleep", "params": { "duration": 1000 } },\n { "path": "accessibility/node", "params": { "selector": { "text": "目标文本" }, "wait_timeout": 5000 }, "throw_if_empty": ["nodes"] }\n ]\n }\n}\n\`\`\`\n\n固定次数滑动浏览:\n\`\`\`json\n{\n "swipe_browse": {\n "description": "连续刷5个视频",\n "loop": { "count": 5, "interval": 2000 },\n "actions": [\n { "path": "input/scroll_bezier", "params": { "start_x": 540, "start_y": 1600, "end_x": 540, "end_y": 400, "duration": 300 } },\n { "path": "base/sleep", "params": { "duration": 1500 } }\n ]\n }\n}\n\`\`\`\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- 带 action 的 accessibility/node wait_timeout 至少 3000ms;纯校验 accessibility/node 至少 5000ms\n- throw_if_empty 只能是 ["nodes"]\n- exception_handlers 只能放在 workflow 顶层\n- loop.count 和 loop.max_count 只能是正整数或 -1(无限)\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": 3000 } },\n { "path": "base/sleep", "params": { "duration": 2000 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 5000 }, "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- **循环决策**:仅当 goal 与录制动作共同表明“同一操作需要重复执行”时,才生成 loop。merge_key 只能作为弱提示,不能单独作为循环依据;若目标元素或语义发生变化,保持为展开步骤。\n\n输出 pure JSON only.`;function ie(e){return"你是 Android 自动化需求优化助手。\n\n要求:\n1. 系统提示词固定使用中文,但最终输出语言必须与用户输入一致。\n2. 只做表达优化,不新增用户未提及的目标。\n3. 保留次数、顺序、停止条件、应用名和关键文本。\n4. 输出仅保留优化后的最终描述,不要解释。"}function re(e,t=!0){return function(e,t=!0){const n=$(e),i=O[n]||O.single_flow;return["你是 Android 自动化任务规划助手。","根据用户目标和当前屏幕,生成可执行、可验证、可收敛的任务计划。","规划规则统一用中文描述,但输出 JSON 中的自然语言内容必须跟随用户输入语言。","输出 JSON 中所有自然语言字段都必须与用户输入语言一致;系统提示词本身固定为中文。",...t?["若缺少规划所需的关键信息,必须暂停并明确提出一个最小问题,禁止编造计划。"]:[],A("全局规则",M),A(`模式规则(${n})`,i),"## 输出 JSON 结构","{",...t?[' "status": "completed | paused(默认 completed)",']:[],' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',...t?[' "estimatedSteps": 1,',' "pauseMessage": "string,可选,仅 status=paused 时填写",',' "pauseReason": "string,可选,仅 status=paused 时填写"']:[' "estimatedSteps": 1'],"}"].join("\n\n")}(e,t)}function oe(e,t){return function(e,t){const n=$(e),i=q[n]||q.single_flow;return["你是 Android 自动化执行 Agent。你的职责是在真实设备上高成功率完成目标。","以下规则统一用中文描述,但你回复用户时必须沿用用户输入语言。","系统提示词固定使用中文,但你面向用户输出的自然语言文本必须与用户输入语言一致。",A("强制层",S),A("建议层",N),A("核心执行规则",j),A(`任务模式规则(${n})`,i),"## 输出约束","- 优先一次只推进一个可验证子目标;若发生状态变化,必须先完成 wait -> observe_screen -> verify_ui_state 再继续。","- 非必要不输出长文本,优先通过 tool_calls 执行动作。","- 当缺少关键参数或验证证据时,先尝试基于当前界面补齐;确实无法判断时再暂停并明确提问。",z(t)].filter(Boolean).join("\n\n")}(e,t)}function se(e){return ne}function ae(e,t,n){return function(e,t,n){const i=(o=ee(e,t),{version:o.version,goal:o.goal,screen:o.screen??null,design_principle:o.design_principle,warnings:o.warnings??[],actions:o.actions.map(e=>({id:e.id,seq:e.seq,tool:e.tool,success:e.success,error:e.error??null,page:{before:{kind:e.page.before.kind,description:e.page.before.description??null,anchor:e.page.before.anchor??{}},after:e.page.after?{kind:e.page.after.kind,description:e.page.after.description??null,anchor:e.page.after.anchor??{}}:null,key_features:e.page.key_features??[]},intent:{summary:e.intent.summary,purpose:e.intent.purpose,expected_result:e.intent.expected_result,merge_key:e.intent.merge_key},target:{desc:e.target.desc??null,role:e.target.role,runtime_selector:e.target.runtime_selector??{},runtime_action_index:e.target.runtime_action_index??null,runtime_selected_node:e.target.runtime_selected_node??{},selector_profile:e.target.selector_profile??{},script_selector:e.target.script_selector??null,selector_candidates:e.target.selector_candidates??[]},success_criteria:{required:e.success_criteria.required,materialization:e.success_criteria.materialization,signal_type:e.success_criteria.signal_type,rationale:e.success_criteria.rationale,primary:e.success_criteria.primary??null,fallback:e.success_criteria.fallback??null},classification:{criticality:e.classification.criticality,include_mode:e.classification.include_mode,step_is_key:e.classification.step_is_key},replay_plan:{actions:e.replay_plan.actions??[],verification:e.replay_plan.verification??null},diagnostics:{matched_count:e.diagnostics.matched_count??null,selector_reusable:e.diagnostics.selector_reusable??null},warnings:e.warnings??[]}))}),r=[];var o;return i.screen&&r.push(`[屏幕]\n${i.screen.width}x${i.screen.height}`),r.push(`[目标]\n${e}`),r.push(`[录制产物字段说明]\n${te}`),r.push(`[录制产物]\n${JSON.stringify(i,null,2)}`),n&&r.push(`[上一次失败]\n${n}`),r.push("请将 RecordingArtifact 翻译成最终 WorkflowScript。只使用 replay_plan、target.script_selector、success_criteria。不要自行推导 selector,不要补编未记录动作。遇到 warnings 时继续基于已批准信息生成,但不要发明缺失验证。WorkflowScript 中所有自然语言字段必须与用户目标语言一致,step id 仍保持稳定英文。只输出 WorkflowScript JSON。"),r.join("\n\n")}(e,t,n)}const ce=i.object({path:i.string().min(1),params:i.record(i.unknown()).optional(),throw_if_empty:i.array(i.string()).optional()}),le=i.number().int().refine(e=>-1===e||e>0,{message:"loop count must be a positive integer or -1 (infinite)"}),pe=i.object({description:i.string().optional(),completed:i.literal("success").optional(),loop:i.union([i.object({count:le,interval:i.number().int().min(0).optional()}),i.object({max_count:le,interval:i.number().int().min(0).optional()})]).optional(),actions:i.array(ce).min(1)}),_e=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(pe),flow:i.array(i.string()).min(1)}),ue=new Set(["activity/start","permission/set","activity/stop","accessibility/node","input/scroll_bezier","input/text","input/keyevent","input/click","base/sleep"]);function de(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function me(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function fe(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 ge(e){const t=de(e.loop);if(!t)return void(me(e,"completed")&&delete e.completed);const n=(Array.isArray(e.actions)?e.actions:[]).map(e=>de(e)).filter(e=>!!e),i=fe(t.interval),r=null!==i&&i>0?{interval:i}:{};if(me(t,"count")){return 1===fe(t.count)?(delete e.loop,void delete e.completed):void(me(e,"completed")&&delete e.completed)}if(!me(t,"max_count"))return;e.completed="success";if(n.some(e=>he(e.throw_if_empty)))return;const o=n.find(e=>{if("accessibility/node"!==e.path)return!1;const t=de(e.params);return!t||!me(t,"action")});if(o)return void(o.throw_if_empty=["nodes"]);const s=n.find(e=>"accessibility/node"===e.path);if(s)return void(s.throw_if_empty=["nodes"]);const a=fe(t.max_count);if(null!==a){if(1===a)return delete e.loop,void delete e.completed;e.loop={count:a,...r},delete e.completed}}function ye(e){return"number"==typeof e&&Number.isInteger(e)&&e>0}function he(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]&&"nodes"===e[0]}function xe(e,t,n,i){const r=`${e}.actions[${n}]`;if(ue.has(t.path)||i.push(`${r}: illegal path "${t.path}"`),t.throw_if_empty&&!he(t.throw_if_empty)&&i.push(`${r}: throw_if_empty must be exactly ["nodes"]`),"accessibility/node"===t.path){const e=t.params?.wait_timeout;ye(e)||i.push(`${r}: accessibility/node requires positive integer params.wait_timeout`)}if("base/sleep"===t.path){const e=t.params?.duration;ye(e)||i.push(`${r}: base/sleep requires positive integer params.duration`)}}function be(e,t,n){const i=t.steps[e],r=i.loop,o=i.completed;if(!r)return void(void 0!==o&&n.push(`${e}: steps without loop must not define completed`));if("count"in r)return 0!==r.count&&1!==r.count||n.push(`${e}: loop.count=${r.count} is forbidden; remove loop or use count>=2 or -1`),r.count<-1&&n.push(`${e}: loop.count=${r.count} is invalid; use -1 for infinite`),void(void 0!==o&&n.push(`${e}: count loop must not define completed`));"success"!==o&&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)){be(n,e,t);for(let e=0;e<i.actions.length;e++)xe(n,i.actions[e],e,t)}if(function(e,t){const n=new Set;for(const i of e.flow){const r=e.steps[i];for(let e=0;e<r.actions.length;e++){const o=r.actions[e],s=o.params||{},a=`${i}.actions[${e}]`;if("permission/set"===o.path){const e=s.package_name;if("string"!=typeof e||!e){t.push(`${a}: permission/set requires params.package_name`);continue}!0!==s.grant_all&&t.push(`${a}: permission/set requires params.grant_all=true`),n.add(e)}if("activity/start"===o.path){const e=s.package_name;if("string"!=typeof e||!e){t.push(`${a}: activity/start requires params.package_name`);continue}n.has(e)||t.push(`${a}: 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){const t=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=de(t.params)||{};if("base/sleep"===t.path){const e=fe(n.duration);t.params={...n,duration:null!==e&&e>0?Math.max(2e3,e):2e3}}if("accessibility/node"===t.path){const e="string"==typeof n.action?n.action:void 0,i=fe(n.wait_timeout);e&&(null===i||i<=0)?t.params={...n,wait_timeout:3e3}:e&&null!==i&&i>0?t.params={...n,wait_timeout:Math.max(3e3,i)}:!e&&(null===i||i<=0)?t.params={...n,wait_timeout:5e3}:!e&&null!==i&&i>0&&(t.params={...n,wait_timeout:Math.max(5e3,i)})}const i=t.throw_if_empty;Array.isArray(i)&&i.includes("nodes")&&(t.throw_if_empty=["nodes"])}ge(t)}}return e}(e),n=_e.parse(t),i={...n,id:n.id||`workflow_${Date.now()}`,version:n.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}`)}(i),ve(i),i}function ke(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}}async function $e(i,r,o,s,a){const c=h(o).getModel(),l=Math.max(1,s.scriptGeneration.maxAttempts),p=se(),_=e.fromMessages([["system","{systemPrompt}"],["human","{userMessage}"]]).pipe(c).pipe(new t).pipe(new n({func:e=>we(e)})),u=[];let d;for(let e=1;e<=l;e++){const t=ae(i,r,d);try{return{workflow:await _.invoke({systemPrompt:p,userMessage:t},{signal:a}),attempts:e,errors:u}}catch(t){const n=ke(t);if(u.push(`[${n.category}] ${n.message}`),d=n.message,e>=l)throw new Error(`script generation failed (maxAttempts=${l}): [${n.category}] ${n.message}`)}}throw new Error(`script generation failed (maxAttempts=${l}): [UNKNOWN] unreachable`)}const Ae={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 Se(e,t,n,i,r){return $e(e,t,n,i,r)}async function Ne(e,t,n,i){return(await $e(e,t,n,Ae,i)).workflow}export{U as A,Ae as D,W as P,R as S,E as T,I as V,Se as a,J as b,h as c,re as d,k as e,oe as f,Ne as g,C as h,K as i,ie as j,u as z};
|
|
@@ -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"),r=require("@langchain/anthropic"),o=require("@langchain/core/messages"),s=require("@langchain/core/tools"),a=require("@langchain/google-genai"),c=require("@langchain/openai"),l=require("zod-to-json-schema");function p(e){return"integer"===e||Array.isArray(e)&&e.includes("integer")}function _(e){if(Array.isArray(e))return e.map(e=>_(e));if(!e||"object"!=typeof e)return e;const t=e,n={};for(const[e,i]of Object.entries(t))"$schema"!==e&&"$defs"!==e&&"definitions"!==e&&"$ref"!==e&&"additionalProperties"!==e&&"strict"!==e&&"exclusiveMinimum"!==e&&"exclusiveMaximum"!==e&&(n[e]=_(i));const i=function(e){const t=e.exclusiveMinimum;if("number"==typeof t)return p(e.type)&&Number.isInteger(t)?t+1:t}(t);void 0!==i&&"number"!=typeof n.minimum&&(n.minimum=i);const r=function(e){const t=e.exclusiveMaximum;if("number"==typeof t)return p(e.type)&&Number.isInteger(t)?t-1:t}(t);return void 0!==r&&"number"!=typeof n.maximum&&(n.maximum=r),n}function u(e){return _(l.zodToJsonSchema(e,{$refStrategy:"none"}))}function d(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 o.coerceMessageLikeToMessage({role:"assistant",content:e.content,...t.length>0?{tool_calls:t}:{}})}return"tool"===e.role?o.coerceMessageLikeToMessage({role:"tool",content:e.content,tool_call_id:e.toolCallId||""}):o.coerceMessageLikeToMessage({role:e.role,content:e.content})})}function m(e,t){return"google"===t?{type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}}:s.tool(async()=>"schema-only tool placeholder",{name:e.name,description:e.description,schema:e.schema})}function f(e,t){if("string"!=typeof e||0===e.trim().length)throw new Error(`${t} is required`)}function g(e,t,n){return"anthropic"===e?function(e,t){return new r.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 a.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 y{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 f(e.apiKey,"provider.apiKey"),f(e.model,"provider.model"),"anthropic"===t||"google"===t||f(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=g(n,e,t)}getModel(){return this.model}async chatWithTools(e,t,n){const i=d(e),r=function(e,t){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)?m(e,t):e)}(t,this.vendor),o=r.length>0&&"function"==typeof this.model.bindTools?this.model.bindTools(r):this.model,s=await o.invoke(i,{signal:n}),a="string"==typeof(c=s).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=s.tool_calls||[],p=s.response_metadata&&"object"==typeof s.response_metadata?s.response_metadata:null;return{content:a,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 p?.finish_reason&&p.finish_reason||"string"==typeof p?.stop_reason&&p.stop_reason||void 0}}async chatStructuredJson(e,t,n){const i=d(e),r="anthropic"===(o=this.vendor)||"google"===o?[{method:"jsonSchema"},{method:"functionCalling"}]:[{method:"jsonSchema",strict:!0},{method:"functionCalling",strict:!0},{method:"jsonMode"}];var o;let s=null;for(const e of r)try{const r="google"===this.vendor?"functionCalling"===e.method?{name:"structured_output",description:t.description||"Structured output schema",parameters:u(t)}:u(t):t,o=this.model.withStructuredOutput(r,{name:"structured_output",method:e.method,..."boolean"==typeof e.strict?{strict:e.strict}:{}}),s=await o.invoke(i,{signal:n});return t.parse(s)}catch(e){s=e}const a=s instanceof Error&&s.message?s.message:"langchain structured output failed with all methods";throw new Error(a)}}function h(e){return new y(e)}const x=/[\u3400-\u9fff]/u,b=[/\b\d+\s*(次|遍|个|条|张)\b/u,/第\s*\d+\s*(个|条|张)/u],v=[/直到/u,/等到/u,/出现/u,/找到/u],w=[/一直/u,/不停/u,/持续/u,/无限/u,/永远/u];function k(e){const t=e.trim();return t?b.some(e=>e.test(t))?"fixed_count":v.some(e=>e.test(t))?"until_condition":w.some(e=>e.test(t))?"infinite":"single_flow":"single_flow"}function A(e,t){return`## ${e}\n${t.map((e,t)=>`${t+1}. ${e}`).join("\n")}`}const S=["每步前必须有最新 observe_screen / dump 证据,禁止基于过期界面继续执行。","每步后必须完成 success check;优先补齐 observe_screen -> verify_ui_state 再进入下一步。","页面发生变化后必须重新定位,不允许沿用旧页面的 selector 或入口。","必须按 stage 推进:一次只推进一个已验证子目标,不允许无限漂移或脱离当前阶段。","连续失败必须进入恢复分支,优先收敛、重试替代入口或暂停,而不是盲目重复。","高风险动作必须有显式授权,若用户目标未明确授权,不得擅自执行。"],z=["selector 优先最小唯一。","优先稳定属性,避免动态 text。","优先语义定位,不行再坐标。","可处理弹窗、广告、权限框。","可根据页面变化自动切换入口。"],$=["首次动作前先 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(新页面的标志性元素),帮助脚本生成确认导航成功。","如果 verify_ui_state 返回“补录提醒”或提示未形成可回放 verification_plan,优先重新 observe_screen 并补一个可唯一回放的 verify_element/page_anchor;若当前主流程已明确,也可以继续执行,但要接受录制质量下降。"],N={fixed_count:["严格按目标次数执行,不得提前结束。","每轮结束后验证累计进度。"],until_condition:["每轮都必须“检查 -> 动作 -> 等待 -> 再检查”。","达到条件立即收敛,未命中到上限后结束。"],infinite:["仅当目标明确要求持续执行时允许无限循环。"],single_flow:["完成主线后立即结束,不做无关探索。"]};function O(e){if(!e||0===e.subtasks.length)return"";return`## 任务计划参考\n${e.subtasks.map(e=>`- ${e.id}. ${e.description} (成功标准: ${e.successCriteria})`).join("\n")}`}const j=["输出必须是合法 JSON 对象,禁止 markdown 代码块。","计划必须基于当前屏幕状态,不允许假设未观察到的页面。","计划仅包含与用户目标直接相关的步骤,禁止加入与目标无关的探索性动作。","每个子任务都要有可观察的 successCriteria。","若目标存在前置状态依赖,必须先规划“进入前置状态 -> 验证前置状态”,再执行终态动作。","子任务按执行顺序排列,保持最短稳定路径。"],q={fixed_count:["识别用户指定次数,子任务中体现计数闭环。","避免把固定次数任务改写为条件循环。"],until_condition:["计划中显式写出停止条件和最大探索轮次。","停止条件必须可观察,不能用抽象描述。"],infinite:["仅在目标明确要求持续执行时使用无限模式。","仍需给出安全中断条件与风险说明。"],single_flow:["保持单次流程简洁,避免过度拆分。"]};const I=new Set(["start_app","stop_app","tap","tap_element","long_press_element","press_key"]),T=new Set(["verify_ui_state"]),M=new Set(["tap_element","long_press_element","set_text"]),C=new Set(["tap_element","long_press_element","set_text"]);new Set([...C,"verify_ui_state","record_search_context"]);const E=C,R=new Set([...C,"input_text"]),L=new Set(["start_app","stop_app","tap","tap_element","long_press_element","set_text","input_text","swipe","press_key"]);function U(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function J(e){const t={};let n=!1;for(const[i,r]of Object.entries(e))null!=r&&""!==r&&(t[i]=r,n=!0);return n?t:void 0}function P(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 D(e){const t=U(e);if(t)return J({index:P(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 K(e){const t=U(e?.script_selector);if(!t)return;const n=U(t.selector);return n?{selector:n,action_index:P(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 G(e){const t=U(e);if(!t)return;const n=F(t,["page_kind"]),i=F(t,["criticality_hint"]),r=F(t,["include_mode_hint"]),o=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"===r||"log_only"===r||"exception_handler_candidate"===r?r:void 0,valid_until:"next_action"===o||"next_verified_transition"===o||"manual_close"===o?o: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}const H=3e3;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 r of e){const e=r.toolName,o=U(r.arguments)||{},s=U(r.result.data);if("get_screen_info"===e&&r.result.success){const e="number"==typeof s?.width?s.width:void 0,t="number"==typeof s?.height?s.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(o,["step_intent"]),merge_key:F(o,["merge_key"]),expected_result:F(o,["expected_result"]),wait_ms_hint:W(o.wait_ms_hint),target_desc:F(o,["target_desc"]),anchor:J(U(o.anchor_element)||{}),anchor_plan:U(s?.anchor_plan)||void 0,target:J(U(o.target_element)||{}),target_plan:U(s?.target_plan)||void 0,recorded_context:G(s?.recorded_context)};continue}if("verify_ui_state"===e){const e=t[t.length-1];if(!e)continue;e.verify=J(U(o.verify_element)||{}),e.verify_desc=F(o,["step_desc"]),e.page_anchor=J(U(o.page_anchor)||{}),e.screen_desc=F(o,["screen_desc"]);const n=F(U(s?.recorded_page)||{},["page_kind"])||F(o,["page_kind"]);e.verified_wait_ms_hint=W(o.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=U(s?.verification_plan)||void 0,e.page_anchor_plan=U(s?.page_anchor_plan)||void 0,"next_verified_transition"===i?.recorded_context?.valid_until&&(i=null);continue}const a={seq:t.length+1,tool:e,success:r.result.success};r.result.success||"string"!=typeof r.result.error||(a.error=r.result.error),i&&(a.step_intent=i.step_intent,a.intent_merge_key=i.merge_key,a.expected_result_hint=i.expected_result,a.wait_ms_hint=i.wait_ms_hint,a.search_context=i.target_desc,!a.anchor&&i.anchor&&(a.anchor=i.anchor),i.anchor_plan&&(a.anchor_plan=i.anchor_plan),!a.target&&i.target&&(a.target=i.target),i.target_plan&&(a.target_plan=i.target_plan),i.recorded_context&&(a.recorded_context=i.recorded_context),"next_action"===i.recorded_context?.valid_until&&(i=null));const c=U(o.pre_context);c&&(a.step_intent=F(c,["step_intent"])||a.step_intent,a.intent_merge_key=F(c,["merge_key"])||a.intent_merge_key,a.expected_result_hint=F(c,["expected_result"])||a.expected_result_hint,a.wait_ms_hint=W(c.wait_ms_hint)||a.wait_ms_hint,a.target=J(U(c.target)||{})||a.target,a.anchor=J(U(c.anchor)||{})||a.anchor,a.recorded_context=V(a.recorded_context,G(c))),a.selector=J(U(o.selector)||{}),a.action_index=P(o.action_index),"string"==typeof o.text&&(a.text=o.text),"number"==typeof o.key_code&&(a.key_code=o.key_code),"string"==typeof o.package_name&&(a.package_name=o.package_name),"number"==typeof o.duration&&"wait"===e&&(a.wait_duration=o.duration),"number"==typeof o.x&&"number"==typeof o.y&&"tap"===e&&(a.tap_position={x:o.x,y:o.y}),"swipe"===e&&(a.swipe={start_x:Number(o.start_x),start_y:Number(o.start_y),end_x:Number(o.end_x),end_y:Number(o.end_y),duration:"number"==typeof o.duration?o.duration:300});const l=U(s?.runtime_selector_evidence);if(l){const e=J(U(l.requested_selector)||{});!a.selector&&e&&(a.selector=e),a.runtime_selected_node=D(l.selected_node),a.runtime_candidate_nodes=Array.isArray(l.candidate_nodes)?l.candidate_nodes.map(e=>D(e)).filter(e=>!!e):void 0,a.runtime_matched_count="number"==typeof l.matched_count?l.matched_count:void 0,a.selector_profile=U(l.selector_profile)||void 0;const t=P(l.action_index);void 0!==t&&(a.action_index=t)}t.push(a)}return{screen:n,actions:t}}(t),i=n.actions.map(e=>{const t=K(e.selector_profile),n=function(e){const t=K(e.page_anchor_plan),n=K(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:J({selector:t.selector,action:"click",action_index:t.action_index,wait_timeout:H})}]:[];case"long_press_element":return t?[{path:"accessibility/node",params:J({selector:t.selector,action:"long_click",action_index:t.action_index,wait_timeout:H})}]:[];case"set_text":return t?[{path:"accessibility/node",params:J({selector:t.selector,action:"set_text",action_index:t.action_index,wait_timeout:H,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),r=function(e,t){if("ready"!==e.materialization||!e.primary)return;const n="number"==typeof t&&Number.isFinite(t)&&t>0?Math.max(5e3,Math.trunc(t)):5e3;return{path:"accessibility/node",params:{selector:e.primary.selector,wait_timeout:n},throw_if_empty:["nodes"]}}(n,e.verified_wait_ms_hint||e.wait_ms_hint),o=function(e){const t=[],{record:n,scriptSelector:i,replayActions:r,successCriteria:o}=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===r.length&&t.push(`missing_replay_action: ${n.tool} has no replay_plan.actions`),o.required&&"ready"!==o.materialization&&t.push(`missing_verification_plan: verification is required but materialization=${o.materialization}`),t}({record:e,scriptSelector:t,replayActions:i,successCriteria:n}),s=F(e.anchor||{},["desc"])||e.search_context||e.step_intent,a=e.screen_desc||F(e.page_anchor||{},["desc"]),c=e.recorded_context?.page_kind||"unknown",l="page_arrived"===n.signal_type?e.verified_page_kind||"unknown":c,p=e.recorded_context?.criticality_hint,_=p||function(e,t){return"wait"===e.tool?"noise":"page_arrived"===t?"critical":e.success?"supporting":"noise"}(e,n.signal_type),u=e.recorded_context?.include_mode_hint,d=u||function(e){return"wait"===e.tool?"log_only":e.success?"main_flow":"log_only"}(e),m=e.step_intent||e.verify_desc||F(e.target||{},["desc"])||e.search_context||`${e.tool} #${e.seq}`,f=e.expected_result_hint||F(e.page_anchor||{},["desc"])||e.screen_desc||e.verify_desc||m,g=(e.intent_merge_key||m).normalize("NFKC").toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/gu,"_").replace(/^_+|_+$/gu,"")||"step";return{id:`action_${e.seq}`,seq:e.seq,tool:e.tool,success:e.success,error:e.error,page:{before:{kind:c,description:s,anchor:e.anchor_plan||e.anchor},after:e.screen_desc||e.page_anchor?{kind:l,description:a,anchor:e.page_anchor_plan||e.page_anchor||e.verify}:void 0,key_features:Y([s,a,e.search_context,F(e.target||{},["desc","text","content_desc"]),F(e.anchor||{},["desc","text","content_desc"])])},intent:{summary:m,purpose:m,expected_result:f,merge_key:g,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:_,include_mode:d,step_is_key:"critical"===_,sources:{criticality:p?"explicit":"fallback",include_mode:u?"explicit":"fallback"}},replay_plan:{actions:i,verification:r},diagnostics:{matched_count:e.runtime_matched_count,selector_reusable:t?.reusable},warnings:o.length>0?o:void 0}}),r=i.flatMap(e=>(e.warnings||[]).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,warnings:r.length>0?r:void 0}}const Z="顶层字段:\n- version: RecordingArtifact 格式版本。\n- goal: 用户最终目标,决定脚本整体意图。\n- screen: 录制时屏幕尺寸,仅用于理解坐标类动作。\n- design_principle: 录制产物的设计原则,提醒你“翻译已有信息,而不是补推未知信息”。\n- warnings: 全局录制质量提醒,不阻塞生成。\n- actions: 已格式化的执行记录数组,按 seq 升序排列。\n\n单条 actions[*] 字段:\n- id: 该条记录的稳定 ID。\n- seq: 原始执行顺序。主流程应尊重这个顺序。\n- tool: 录制时调用的工具名。\n- success: 该次工具调用是否成功。\n- error: 失败时的错误信息;success=true 时通常为 null。\n\n页面上下文 page:\n- page.before: 动作前页面信息。\n- page.after: 动作后页面信息;如果缺失,表示录制时没有拿到可靠的动作后页面证据。\n- page.key_features: 页面关键特征摘要,只用于帮助你理解页面,不用于拼 selector。\n- page.before/after.kind: 粗粒度页面类型,例如 list/detail/dialog。\n- page.before/after.description: 页面自然语言描述,可用于步骤描述。\n- page.before/after.anchor: 录制时的页面锚点证据,可帮助理解页面是否切换。\n\n步骤意图 intent:\n- intent.summary: 最短步骤摘要。优先用于 step description。\n- intent.purpose: 该动作为什么执行。\n- intent.expected_result: 该动作执行后希望达到的结果。\n- intent.merge_key: 步骤命名和合并时可参考的稳定键,仅作提示,不足以单独决定是否合并或生成循环。\n\n目标与选择器 target:\n- target.desc: 人类可读的目标描述。\n- target.role: 目标元素角色提示。\n- target.runtime_selector: 录制时实际传入的 selector,仅作背景参考,不等于最终脚本批准 selector。\n- target.runtime_action_index: 录制时使用的序号定位。\n- target.runtime_selected_node: 录制时命中的真实节点快照。\n- target.selector_profile: 录制阶段对 selector 质量的分析信息。\n- target.script_selector: 已批准进入脚本的 selector。这是生成主动作时最优先的 selector 来源。\n- target.selector_candidates: 候选 selector 证据,仅用于理解,不用于重新拼接新 selector。\n\n成功判定 success_criteria:\n- success_criteria.required: 该动作理论上是否需要动作后验证。\n- success_criteria.materialization: ready 表示验证动作已可回放;missing_plan 表示需要验证但录制没形成可回放方案;not_required 表示无需验证。\n- success_criteria.signal_type: 该动作成功信号的类型,如 page_arrived / element_visible / state_changed。\n- success_criteria.rationale: 为什么采用这个成功判定策略。\n- success_criteria.primary/fallback: 已批准的验证 selector。只有存在时才能用于生成验证步骤。\n\n流程分类 classification:\n- classification.criticality: 对整体任务的重要程度。\n- classification.include_mode: main_flow / exception_handler_candidate / log_only。\n- classification.step_is_key: 是否是关键里程碑动作。\n\n可回放计划 replay_plan:\n- replay_plan.actions: 已批准的主动作 skeleton。生成主流程时优先直接使用。\n- replay_plan.verification: 已批准的验证动作 skeleton。只有存在时才生成验证。\n\n诊断 diagnostics 与 warnings:\n- diagnostics.matched_count: 录制时 selector 命中的节点数。\n- diagnostics.selector_reusable: selector 是否看起来可复用。\n- warnings: 当前 action 的录制质量提醒,不阻塞生成。\n\n解释原则:\n- null / [] / {} 表示“没录到”或“无可用信息”,直接忽略。\n- 最高可信字段是 replay_plan、target.script_selector、success_criteria.primary/fallback。\n- diagnostics、runtime_selector、runtime_selected_node、selector_candidates 主要用于理解上下文,不用于发明新 selector。",ee=`你是 Android 无人值守 WorkflowScript 工程师。输入不是原始日志,而是录制阶段已经提纯好的 RecordingArtifact。\n\n角色:\n- 你负责把已有执行记录翻译成稳定、可靠、可无人值守执行的 WorkflowScript。\n\n任务:\n- 基于 RecordingArtifact 生成最终脚本。\n- 优先复用已批准的 replay_plan、script_selector、verification selector。\n- 在信息缺失时继续基于已有记录生成,而不是阻塞或补编未知信息。\n\n上下文:\n1. 只消费 RecordingArtifact 中已经批准进入脚本的结构化信息。\n2. 不自行猜 selector,不自行猜页面意图,不自行猜成功判定条件。\n3. 不从 diagnostics、raw desc、候选节点中发明新 selector。\n4. 你的目标是无人值守稳定回放,所以优先选择可回放、可验证、可复用的记录,而不是追求“看起来完整”。\n\n期望:\n- 输出纯 JSON WorkflowScript。\n- 主流程顺序清晰,验证闭环尽量完整。\n- 对缺失字段容忍,但绝不凭空补造。\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- classification.include_mode: main_flow / exception_handler_candidate / log_only\n- warnings: 录制阶段已经识别出的非阻断缺口(例如缺少可回放 verification_plan)\n- 某些字段可能是 null / [] / {}:这表示录制阶段没有拿到该信息,直接忽略,不要自行补编\n\n## 字段解释\n${Z}\n\n## 无人值守优先级\n1. 可回放性优先于表面完整性。\n2. 有 replay_plan.actions 就优先生成动作;没有就跳过,不补编。\n3. 有 replay_plan.verification 才生成验证;没有就接受该缺口,但不要发明验证。\n4. 有 target.script_selector / success_criteria.primary 才把 selector 写进脚本;没有就不要从其他字段反推。\n5. log_only 默认不进入主流程;exception_handler_candidate 优先转成 exception_handlers。\n\n## 绝对规则\n1. 生成脚本时,优先直接使用 replay_plan.actions;不要自己重建 path/params。\n2. 只要 main_flow 记录存在 replay_plan.verification,就必须在该记录的 replay_plan.actions 之后生成“base/sleep -> 验证动作”的闭环。\n3. base/sleep 的 duration 优先由你根据当前场景生成;若你无法判断,可省略 duration,系统会兜底为 2000ms。\n4. 带 action 的 accessibility/node params.wait_timeout 至少 3000ms;replay_plan.verification 的 params.wait_timeout 优先使用已给值,若你输出时缺失、不合法或低于最小值,系统会对纯校验 accessibility/node 兜底并提升到至少 5000ms。\n5. classification.include_mode="log_only" 的记录默认不进入主流程。\n6. classification.include_mode="exception_handler_candidate" 的记录优先抽成 workflow 顶层 exception_handlers,而不是普通步骤。\n7. selector 只能来自 target.script_selector 或 success_criteria.primary/fallback;禁止从 target.desc、screen_desc、diagnostics 反推。\n8. 不得把 bounds、动态文案、候选节点文本重新拼成新 selector。\n9. merge_key 仅作为 step 分组提示;只有当相邻 main_flow 记录确实表达同一可复用步骤语义、且目标模式一致时,才合并为同一 step。\n10. 如果 replay_plan.actions 为空,不要编造动作;跳过该记录。\n11. completed 只允许字面量 "success",且仅在 loop.max_count 场景使用。\n12. 如果 RecordingArtifact.warnings 存在,或 action.warnings 非空,继续基于已有 replay_plan 生成,但不要补编缺失的 selector / 验证动作。\n13. 如果 success_criteria.required=true 但 materialization 不是 "ready",视为录制不完整;保留已批准的 replay_plan.actions,但不要发明 replay_plan.verification。\n14. 输出纯 JSON,不要解释。\n15. WorkflowScript 中所有自然语言字段(如 name、description、step.description、exception_handlers[].name/description)必须与用户目标语言一致;step id 仍保持稳定英文。\n\n## 循环生成规则(LoopConfig)\nstep 支持 loop 字段实现循环执行,两种模式二选一:\n\n模式一:固定次数循环 \`{ "count": N }\`\n- 执行固定 N 次,不检查 completed。N=-1 表示无限循环(需外部取消)。\n- 禁止 count=1(无意义)或 count=0。\n- 禁止搭配 completed。\n\n模式二:条件循环 \`{ "max_count": N }\` + \`"completed": "success"\`\n- 每次循环后检查 completed 条件(由 throw_if_empty=["nodes"] 的 accessibility/node 驱动),找到目标元素即退出。\n- max_count=-1 表示无限循环直到成功。\n- 必须搭配 completed="success" 和至少一个带 throw_if_empty=["nodes"] 的 accessibility/node 动作。\n\n两种模式均可加 \`"interval": ms\` 指定循环间隔。\n\n### 何时生成循环\n1. 只有当用户目标和录制动作都清楚表达“同一操作需要重复执行”时,才生成 loop;不要仅凭 merge_key 相同就强制抽象成循环。\n2. 如果相邻动作虽然相似,但点击目标、selector、action_index、预期结果或页面语义不同,应保持为展开步骤,不要合并成 loop。\n3. 如果用户目标中包含"刷N个""滑动N次""连续播放"等明确重复意图,用 loop.count=N 或 loop.max_count=N。\n4. 如果目标是"找到某个元素"类的搜索滑动(swipe + 找目标),使用 max_count + completed="success",在 actions 中放置: 滑动动作 → base/sleep → accessibility/node(查找目标, throw_if_empty=["nodes"])。\n5. 如果用户目标含"一直""持续""不断"等无限循环意图,使用 count=-1 或 max_count=-1。\n6. 对不涉及重复的普通步骤,不要添加 loop。\n\n### 循环步骤示例\n\n滑动查找目标(条件循环):\n\`\`\`json\n{\n "scroll_find_target": {\n "description": "向下滑动列表查找目标内容",\n "loop": { "max_count": 10, "interval": 500 },\n "completed": "success",\n "actions": [\n { "path": "input/scroll_bezier", "params": { "start_x": 540, "start_y": 1600, "end_x": 540, "end_y": 600, "duration": 300 } },\n { "path": "base/sleep", "params": { "duration": 1000 } },\n { "path": "accessibility/node", "params": { "selector": { "text": "目标文本" }, "wait_timeout": 5000 }, "throw_if_empty": ["nodes"] }\n ]\n }\n}\n\`\`\`\n\n固定次数滑动浏览:\n\`\`\`json\n{\n "swipe_browse": {\n "description": "连续刷5个视频",\n "loop": { "count": 5, "interval": 2000 },\n "actions": [\n { "path": "input/scroll_bezier", "params": { "start_x": 540, "start_y": 1600, "end_x": 540, "end_y": 400, "duration": 300 } },\n { "path": "base/sleep", "params": { "duration": 1500 } }\n ]\n }\n}\n\`\`\`\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- 带 action 的 accessibility/node wait_timeout 至少 3000ms;纯校验 accessibility/node 至少 5000ms\n- throw_if_empty 只能是 ["nodes"]\n- exception_handlers 只能放在 workflow 顶层\n- loop.count 和 loop.max_count 只能是正整数或 -1(无限)\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": 3000 } },\n { "path": "base/sleep", "params": { "duration": 2000 } },\n { "path": "accessibility/node", "params": { "selector": { "resource_id": "com.example:id/detail_title" }, "wait_timeout": 5000 }, "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- **循环决策**:仅当 goal 与录制动作共同表明“同一操作需要重复执行”时,才生成 loop。merge_key 只能作为弱提示,不能单独作为循环依据;若目标元素或语义发生变化,保持为展开步骤。\n\n输出 pure JSON only.`;function te(e){return ee}function ne(e,t,n){return function(e,t,n){const i=(o=X(e,t),{version:o.version,goal:o.goal,screen:o.screen??null,design_principle:o.design_principle,warnings:o.warnings??[],actions:o.actions.map(e=>({id:e.id,seq:e.seq,tool:e.tool,success:e.success,error:e.error??null,page:{before:{kind:e.page.before.kind,description:e.page.before.description??null,anchor:e.page.before.anchor??{}},after:e.page.after?{kind:e.page.after.kind,description:e.page.after.description??null,anchor:e.page.after.anchor??{}}:null,key_features:e.page.key_features??[]},intent:{summary:e.intent.summary,purpose:e.intent.purpose,expected_result:e.intent.expected_result,merge_key:e.intent.merge_key},target:{desc:e.target.desc??null,role:e.target.role,runtime_selector:e.target.runtime_selector??{},runtime_action_index:e.target.runtime_action_index??null,runtime_selected_node:e.target.runtime_selected_node??{},selector_profile:e.target.selector_profile??{},script_selector:e.target.script_selector??null,selector_candidates:e.target.selector_candidates??[]},success_criteria:{required:e.success_criteria.required,materialization:e.success_criteria.materialization,signal_type:e.success_criteria.signal_type,rationale:e.success_criteria.rationale,primary:e.success_criteria.primary??null,fallback:e.success_criteria.fallback??null},classification:{criticality:e.classification.criticality,include_mode:e.classification.include_mode,step_is_key:e.classification.step_is_key},replay_plan:{actions:e.replay_plan.actions??[],verification:e.replay_plan.verification??null},diagnostics:{matched_count:e.diagnostics.matched_count??null,selector_reusable:e.diagnostics.selector_reusable??null},warnings:e.warnings??[]}))}),r=[];var o;return i.screen&&r.push(`[屏幕]\n${i.screen.width}x${i.screen.height}`),r.push(`[目标]\n${e}`),r.push(`[录制产物字段说明]\n${Z}`),r.push(`[录制产物]\n${JSON.stringify(i,null,2)}`),n&&r.push(`[上一次失败]\n${n}`),r.push("请将 RecordingArtifact 翻译成最终 WorkflowScript。只使用 replay_plan、target.script_selector、success_criteria。不要自行推导 selector,不要补编未记录动作。遇到 warnings 时继续基于已批准信息生成,但不要发明缺失验证。WorkflowScript 中所有自然语言字段必须与用户目标语言一致,step id 仍保持稳定英文。只输出 WorkflowScript JSON。"),r.join("\n\n")}(e,t,n)}const ie=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()}),re=i.z.number().int().refine(e=>-1===e||e>0,{message:"loop count must be a positive integer or -1 (infinite)"}),oe=i.z.object({description:i.z.string().optional(),completed:i.z.literal("success").optional(),loop:i.z.union([i.z.object({count:re,interval:i.z.number().int().min(0).optional()}),i.z.object({max_count:re,interval:i.z.number().int().min(0).optional()})]).optional(),actions:i.z.array(ie).min(1)}),se=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(oe),flow:i.z.array(i.z.string()).min(1)}),ae=new Set(["activity/start","permission/set","activity/stop","accessibility/node","input/scroll_bezier","input/text","input/keyevent","input/click","base/sleep"]);function ce(e){return!e||"object"!=typeof e||Array.isArray(e)?null:e}function le(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function pe(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 _e(e){const t=ce(e.loop);if(!t)return void(le(e,"completed")&&delete e.completed);const n=(Array.isArray(e.actions)?e.actions:[]).map(e=>ce(e)).filter(e=>!!e),i=pe(t.interval),r=null!==i&&i>0?{interval:i}:{};if(le(t,"count")){return 1===pe(t.count)?(delete e.loop,void delete e.completed):void(le(e,"completed")&&delete e.completed)}if(!le(t,"max_count"))return;e.completed="success";if(n.some(e=>de(e.throw_if_empty)))return;const o=n.find(e=>{if("accessibility/node"!==e.path)return!1;const t=ce(e.params);return!t||!le(t,"action")});if(o)return void(o.throw_if_empty=["nodes"]);const s=n.find(e=>"accessibility/node"===e.path);if(s)return void(s.throw_if_empty=["nodes"]);const a=pe(t.max_count);if(null!==a){if(1===a)return delete e.loop,void delete e.completed;e.loop={count:a,...r},delete e.completed}}function ue(e){return"number"==typeof e&&Number.isInteger(e)&&e>0}function de(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]&&"nodes"===e[0]}function me(e,t,n,i){const r=`${e}.actions[${n}]`;if(ae.has(t.path)||i.push(`${r}: illegal path "${t.path}"`),t.throw_if_empty&&!de(t.throw_if_empty)&&i.push(`${r}: throw_if_empty must be exactly ["nodes"]`),"accessibility/node"===t.path){const e=t.params?.wait_timeout;ue(e)||i.push(`${r}: accessibility/node requires positive integer params.wait_timeout`)}if("base/sleep"===t.path){const e=t.params?.duration;ue(e)||i.push(`${r}: base/sleep requires positive integer params.duration`)}}function fe(e,t,n){const i=t.steps[e],r=i.loop,o=i.completed;if(!r)return void(void 0!==o&&n.push(`${e}: steps without loop must not define completed`));if("count"in r)return 0!==r.count&&1!==r.count||n.push(`${e}: loop.count=${r.count} is forbidden; remove loop or use count>=2 or -1`),r.count<-1&&n.push(`${e}: loop.count=${r.count} is invalid; use -1 for infinite`),void(void 0!==o&&n.push(`${e}: count loop must not define completed`));"success"!==o&&n.push(`${e}: max_count loop must define completed="success"`);i.actions.some(e=>de(e.throw_if_empty))||n.push(`${e}: max_count loop requires at least one action with throw_if_empty=["nodes"]`)}function ge(e){const t=[];for(const[n,i]of Object.entries(e.steps)){fe(n,e,t);for(let e=0;e<i.actions.length;e++)me(n,i.actions[e],e,t)}if(function(e,t){const n=new Set;for(const i of e.flow){const r=e.steps[i];for(let e=0;e<r.actions.length;e++){const o=r.actions[e],s=o.params||{},a=`${i}.actions[${e}]`;if("permission/set"===o.path){const e=s.package_name;if("string"!=typeof e||!e){t.push(`${a}: permission/set requires params.package_name`);continue}!0!==s.grant_all&&t.push(`${a}: permission/set requires params.grant_all=true`),n.add(e)}if("activity/start"===o.path){const e=s.package_name;if("string"!=typeof e||!e){t.push(`${a}: activity/start requires params.package_name`);continue}n.has(e)||t.push(`${a}: missing prior permission/set for package "${e}"`)}}}}(e,t),t.length>0)throw new Error(`workflow semantic validation failed:\n- ${t.join("\n- ")}`)}function ye(e){const t=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=ce(t.params)||{};if("base/sleep"===t.path){const e=pe(n.duration);t.params={...n,duration:null!==e&&e>0?Math.max(2e3,e):2e3}}if("accessibility/node"===t.path){const e="string"==typeof n.action?n.action:void 0,i=pe(n.wait_timeout);e&&(null===i||i<=0)?t.params={...n,wait_timeout:3e3}:e&&null!==i&&i>0?t.params={...n,wait_timeout:Math.max(3e3,i)}:!e&&(null===i||i<=0)?t.params={...n,wait_timeout:5e3}:!e&&null!==i&&i>0&&(t.params={...n,wait_timeout:Math.max(5e3,i)})}const i=t.throw_if_empty;Array.isArray(i)&&i.includes("nodes")&&(t.throw_if_empty=["nodes"])}_e(t)}}return e}(e),n=se.parse(t),i={...n,id:n.id||`workflow_${Date.now()}`,version:n.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}`)}(i),ge(i),i}function he(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}}async function xe(i,r,o,s,a){const c=h(o).getModel(),l=Math.max(1,s.scriptGeneration.maxAttempts),p=te(),_=e.ChatPromptTemplate.fromMessages([["system","{systemPrompt}"],["human","{userMessage}"]]).pipe(c).pipe(new t.JsonOutputParser).pipe(new n.RunnableLambda({func:e=>ye(e)})),u=[];let d;for(let e=1;e<=l;e++){const t=ne(i,r,d);try{return{workflow:await _.invoke({systemPrompt:p,userMessage:t},{signal:a}),attempts:e,errors:u}}catch(t){const n=he(t);if(u.push(`[${n.category}] ${n.message}`),d=n.message,e>=l)throw new Error(`script generation failed (maxAttempts=${l}): [${n.category}] ${n.message}`)}}throw new Error(`script generation failed (maxAttempts=${l}): [UNKNOWN] unreachable`)}const be={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=M,exports.DEFAULT_RELIABILITY_CONFIG=be,exports.POST_VERIFY_HINT_TOOLS=R,exports.PRE_CONTEXT_REQUIRED_TOOLS=E,exports.SELECTOR_ACTION_TOOLS=C,exports.SOFT_VERIFY_ACTIONS=L,exports.TOOL_TIMEOUT_OVERRIDES_MS={observe_screen:"observeScreenMs",start_app:"startAppMs"},exports.VERIFY_COMPLETION_TOOLS=T,exports.VERIFY_REQUIRED_ACTIONS=I,exports.buildAgentSystemPrompt=function(e,t){return function(e,t){const n=k(e),i=N[n]||N.single_flow;return["你是 Android 自动化执行 Agent。你的职责是在真实设备上高成功率完成目标。","以下规则统一用中文描述,但你回复用户时必须沿用用户输入语言。","系统提示词固定使用中文,但你面向用户输出的自然语言文本必须与用户输入语言一致。",A("强制层",S),A("建议层",z),A("核心执行规则",$),A(`任务模式规则(${n})`,i),"## 输出约束","- 优先一次只推进一个可验证子目标;若发生状态变化,必须先完成 wait -> observe_screen -> verify_ui_state 再继续。","- 非必要不输出长文本,优先通过 tool_calls 执行动作。","- 当缺少关键参数或验证证据时,先尝试基于当前界面补齐;确实无法判断时再暂停并明确提问。",O(t)].filter(Boolean).join("\n\n")}(e,t)},exports.buildOptimizeIntentPrompt=function(e){return"你是 Android 自动化需求优化助手。\n\n要求:\n1. 系统提示词固定使用中文,但最终输出语言必须与用户输入一致。\n2. 只做表达优化,不新增用户未提及的目标。\n3. 保留次数、顺序、停止条件、应用名和关键文本。\n4. 输出仅保留优化后的最终描述,不要解释。"},exports.buildTaskPlanPrompt=function(e,t=!0){return function(e,t=!0){const n=k(e),i=q[n]||q.single_flow;return["你是 Android 自动化任务规划助手。","根据用户目标和当前屏幕,生成可执行、可验证、可收敛的任务计划。","规划规则统一用中文描述,但输出 JSON 中的自然语言内容必须跟随用户输入语言。","输出 JSON 中所有自然语言字段都必须与用户输入语言一致;系统提示词本身固定为中文。",...t?["若缺少规划所需的关键信息,必须暂停并明确提出一个最小问题,禁止编造计划。"]:[],A("全局规则",j),A(`模式规则(${n})`,i),"## 输出 JSON 结构","{",...t?[' "status": "completed | paused(默认 completed)",']:[],' "goal": "string",',' "subtasks": [{"id":1,"description":"string","successCriteria":"string","estimatedActions":["tool"]}],',' "risks": ["string"],',' "assumptions": ["string"],',...t?[' "estimatedSteps": 1,',' "pauseMessage": "string,可选,仅 status=paused 时填写",',' "pauseReason": "string,可选,仅 status=paused 时填写"']:[' "estimatedSteps": 1'],"}"].join("\n\n")}(e,t)},exports.createProvider=h,exports.detectInputLanguage=function(e){return x.test(e||"")?"zh":"en"},exports.generateScript=async function(e,t,n,i){return(await xe(e,t,n,be,i)).workflow},exports.generateScriptWithReliability=async function(e,t,n,i,r){return xe(e,t,n,i,r)},exports.zodSchemaToJsonSchema=u;
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./chunks/scriptGenerator-
|
|
1
|
+
"use strict";var e=require("./chunks/scriptGenerator-uuk6ZSss.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("zod-to-json-schema"),exports.generateScript=e.generateScript,exports.generateScriptWithReliability=e.generateScriptWithReliability;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{g as generateScript,a as generateScriptWithReliability}from"./chunks/scriptGenerator-
|
|
1
|
+
export{g as generateScript,a as generateScriptWithReliability}from"./chunks/scriptGenerator-Dwf4MEHV.js";import"@langchain/core/prompts";import"@langchain/core/output_parsers";import"@langchain/core/runnables";import"zod";import"@langchain/anthropic";import"@langchain/core/messages";import"@langchain/core/tools";import"@langchain/google-genai";import"@langchain/openai";import"zod-to-json-schema";
|