shennian 0.2.130 → 0.2.132

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.
@@ -261,8 +261,8 @@
261
261
  },
262
262
  {
263
263
  "file": "src/channels/wechat-channel/observer.js",
264
- "beforeBytes": 111237,
265
- "afterBytes": 50157
264
+ "beforeBytes": 112602,
265
+ "afterBytes": 50766
266
266
  },
267
267
  {
268
268
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -331,8 +331,8 @@
331
331
  },
332
332
  {
333
333
  "file": "src/channels/wechat-rpa.js",
334
- "beforeBytes": 58638,
335
- "afterBytes": 29885
334
+ "beforeBytes": 58837,
335
+ "afterBytes": 30003
336
336
  },
337
337
  {
338
338
  "file": "src/channels/wecom.js",
@@ -1,7 +1,7 @@
1
- import W from"node:fs";import J from"node:path";import{WeChatChannelApiError as De}from"./client.js";import{defaultWeChatChannelAttachmentDir as Le,resolveVisibleWeChatChannelMedia as Ke}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as $e,loadWeChatChannelVectorStore as ze,saveWeChatChannelVectorStore as Ve,upsertWeChatChannelVectorReferences as qe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as Z,normalizeWeChatChannelMessageKind as je}from"./core/schema.js";import{waitForWeChatChannelPacing as Q,weChatChannelPacingDelayMs as Ge}from"./pacing.js";import{matchWeChatConversationFingerprints as He}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as ee}from"./anchor.js";import{decideWeChatChannelActivityGate as Ue,normalizeWeChatChannelActivitySnapshot as Ye}from"./human-coordination.js";const Xe=3,Je=5,Ze="wechat_login_required",Qe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function Rn(e){return await ut(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>et({...e,helper:n}))}async function et(e){const t=e.foregroundMode??"required";let n=await tt(e,{foregroundMode:t});if(t!=="background"&&(n=await Ie(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=E({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!O(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(Y({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),de(e)?le({...e,window:n,capture:s,ocr:a}):q({...e,window:n,capture:s,ocr:a})}const r=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const s=await se({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await Ie(e.helper,s,e.traceId);const a=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(Y({capture:i,ocr:o,binding:e.binding})){n=await Gt(e.helper,e.traceId);const s=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),Y({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),de(e)?le({...e,window:n,capture:i,ocr:o}):q({...e,window:n,capture:i,ocr:o})}async function tt(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Vt(e.helper,e.traceId,{foreground:n});return n==="background"?r:se({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const x=new Map;function nt(){return process.env.SHENNIAN_WECHAT_VISION_LAYOUT_REUSE!=="0"}function te(e){if(!nt()||!e.classification||e.classification.windowKind!=="chat_main")return;const t=e.window.windowId;if(!t)return;const n=ae(e.window),r=F(e.classification.layout?.messageInputRect,e.screenshot,e.window,"message-input"),i=F(e.classification.layout?.searchInputRect,e.screenshot,e.window,"search-input");if(!r&&!i)return;const o={windowId:t,boundsKey:n,messageInputPoint:r,searchInputPoint:i,updatedAt:new Date().toISOString()},s=`${e.runtime.runtimeId}:${e.binding.bindingId}:${t}`,a=`${e.runtime.runtimeId}:window:${t}:${n}`;x.set(s,o),x.set(a,o),R(e,s,o),R(e,a,o)}async function Fn(e){const t=await ne(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function rt(e){const t=await ne(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function ne(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=ae(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,s=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,a=x.get(o)??x.get(s)??ie(e,o)??ie(e,s);if(a&&a.windowId===r&&a.boundsKey===i&&(!t||a[t]))return x.set(o,a),x.set(s,a),a;const c=e.screenshot??await T(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=F(l.layout?.messageInputRect,c,e.window,"message-input"),u=F(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return x.set(o,d),x.set(s,d),R(e,o,d),R(e,s,d),d}function re(e){const t=e.workDir;return t?J.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function ie(e,t){const n=re(e);if(!n)return null;try{const r=JSON.parse(W.readFileSync(n,"utf8"));return it(r[t])}catch{return null}}function R(e,t,n){const r=re(e);if(r)try{let i={};try{i=JSON.parse(W.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,W.mkdirSync(J.dirname(r),{recursive:!0}),W.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
- `,"utf8")}catch{}}function it(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=oe(t.messageInputPoint??t.point),o=oe(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function oe(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function ae(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function F(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),s=Number(e?.width),a=Number(e?.height);if(![i,o,s,a].every(Number.isFinite)||s<=0||a<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:s,height:a,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:s/999*t.width,height:a/999*t.height,coordinateSpace:"screenshotPixel"},l=ot(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function ot(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=at({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function at(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),s=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-s)/i}}async function se(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(D).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=dt(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const s of r)try{const a=await T(e.helper,s.windowId,e.traceId,s.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:a.mimeType,dataBase64:a.dataBase64,width:a.width,height:a.height,windowId:s.windowId},traceId:e.traceId});i.push({window:s,classification:c,screenshot:a,score:lt(c,e.requireTargetConversation===!0)})}catch(a){if(st(a))throw a;i.push({window:s,classification:null,errorSummary:a instanceof Error?a.message:String(a||"window classification failed"),score:-1})}const o=i.filter(s=>s.score>0).sort((s,a)=>a.score-s.score)[0];if(!o){const s=i.map(a=>({windowId:a.window.windowId,appName:a.window.appName,title:a.window.title,score:a.score,errorSummary:a.errorSummary,classification:a.classification?{windowKind:a.classification.windowKind,reasonCode:a.classification.reasonCode,confidence:a.classification.confidence,conversationTitle:a.classification.conversationTitle,isTargetConversation:a.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.screenshot&&te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:o.window,classification:o.classification,screenshot:o.screenshot}),o.window}async function An(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function st(e){const t=ct(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ct(e){if(e instanceof De||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function dt(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function lt(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ut(e,t,n){if(!t)return{helper:e,withLease:async(a,c,l)=>await l(e)};let r=null;const i=a=>{t.onEvent?.({...a,at:new Date().toISOString()})},o=async(a,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},f=u.ok&&d.interrupted?ce(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:a,action:c,ok:u.ok===!0&&!d.interrupted,...f?{reasonCode:f}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:ce(d.interruptReason),stage:a,action:c})}const w=Date.now()+ht(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:a,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});const d=Ue({snapshot:Ye(u.result),stage:a,policy:t.policy});if(i({phase:"snapshot",stage:a,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const f=new N({reasonCode:d.reasonCode,stage:a,action:c});if(!l.settle)throw f;const X=w-Date.now();if(X<=0)throw f;await U(Math.max(50,Math.min(d.waitMs,X)))}},s={request:async(a,c,l)=>{const w=wt(a);w&&await o(w.stage,`${a}:before`);const u=await e.request(a,c,l);return w&&u.ok&&await o(w.stage,`${a}:after`),u}};return{helper:s,async withLease(a,c,l){if(await Lt(e,n),await o(a,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(s);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:a,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:a,action:c});try{const u=await l(s);return await o(a,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function wt(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function ce(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function ht(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Qe}function de(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function q(e){const t=await Se(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return We(r,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function le(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Se(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(ft(c))return q(e);throw c}const r=It(n.structuredMessages??n.observedMessages??[]),i=await mt({...e,messages:r,screenshot:t}),o=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),s=We(o,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function ft(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function mt(e){const t=bt(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await yt({...e,seedBlocks:t}):t.filter(s=>h(s.vectorBase64)||h(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??$e(e.workDir||process.cwd(),e.runtime.runtimeId),i=ze(r,e.runtime.runtimeId),o=qe({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ve(r,i),{messages:pt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function bt(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,s=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:s,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function gt(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return vt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function yt(e){try{return await gt(e)}catch(t){if(xt(t))throw t;return[]}}function xt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function vt(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function pt(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:Ct(r,i)}:r})}function Ct(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=h(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(l=>l.blockId===a);return c?ue(c,o):we(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(ue(o));return r}function ue(e,t){return{...t?we(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function we(e){const{vectorBase64:t,...n}=e;return n}function It(e){return e.map((t,n)=>_t(t,n)).filter(t=>t!=null)}function _t(e,t){if(!b(e))return null;const n=je(e.kind),r=Mt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=h(e.stableMessageKey)||kt({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:s,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:St(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:s}}}function kt(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function St(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),b(t.attachment)&&(t.attachment=he(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?he(n):n)),t}function he(e){const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),t}function Mt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function fe(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>Rt(o,e.screenshot,e.window,Wt(e.messages,s))).filter((o,s)=>Nt(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Le(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ke({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const s=i.get(o.stableMessageKey);return s?{...o,mediaMetadata:Ft(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function Nt(e,t,n){const r=be(n);if(!r)return!0;const i=Tt(r),o=e.map((s,a)=>be(Pt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===Bt(e,i);for(const s of o)if(t>=s&&t<=s+8||t===s-1&&!e.slice(s+1,Math.min(e.length,s+9)).some(me))return!0;return!1}function Tt(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Bt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(me(e[n]))return Et(e[n],t)?n:-1;return-1}function me(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return ge(t,n,r)}function Et(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),s=String(i.mimeType||"").toLowerCase(),a=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||s.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(a):t==="image"?/image|photo|picture/.test(n)||o==="image"||s.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(a):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!s.startsWith("image/")&&!s.startsWith("video/")}function Pt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
3
- `)}function be(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Wt(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
4
- `)||null}function Rt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!ge(i,o,s))return null;const l=A(e.bbox)??A(o.bbox)??A(o.downloadActionBbox),w=A(o.downloadActionBbox),u=ye(l,t,n),d=Dt(l,t,n),f=ye(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:At(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Ft(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function At(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ge(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function A(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function ye(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Dt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Dn(e,t){await ve(e,t)}async function Lt(e,t){await xe(e,t)}async function xe(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if($t(r.platform)){const i=await Kt(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function ve(e,t,n={}){const r=await xe(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(jt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function Kt(e,t,n){const r=Ot(t);if(!r||process.platform!=="darwin")return r;const i=zt(r);try{await e.request(i,{},n)}catch{}return r}function Ot(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function $t(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function zt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Vt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ve(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await qt(e,l,t)}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}async function qt(e,t,n){if(pe(t.title))return t;try{const r=await e.request("windows.list",{},n);m(r,"windows.list");const o=(r.result?.windows??[]).find(s=>s.windowId&&s.visible!==!1&&s.minimized!==!0&&D(s)&&pe(s.title));if(!o)return t;if(t.windowId&&t.windowId!==o.windowId)try{await e.request("windows.closeWindow",{windowId:t.windowId},n)}catch{}return o}catch{return t}}function pe(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function jt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(D),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function Ln(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Gt(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(D);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function j(e){const{capture:t,ocr:n}=e.observation??await k(e),r=E({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:on(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=pn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function G(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Ht(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Ht(e){const t=await k(e),n=E({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await j({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await ke(e);if(f.opened)return f}}const i=await Qt(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await Ut(e);if(s.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:u.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await tn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const l=await j({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await j(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Ut(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await U(n);const o=await Yt(e);t=o;const{capture:s,ocr:a}=o,c=E({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=cn(a,s,e.binding.conversationDisplayName);if(!l){if(dn(a)||ln(a,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,s,Xt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Yt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Xt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Jt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Q("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Jt(e){const t=Zt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Zt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(a?.bbox,t,n)}async function Qt(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function H(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),s=await Me(e,o,n);return{capture:o,ocr:s}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function D(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function U(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Ge(t,en(e,n))}async function S(e,t,n){const r=L(e,t,n);r>0&&await U(r)}function en(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function tn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Cn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await Q("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=E({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!nn(n)&&await rn({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function nn(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function rn(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function E(e){const n=(e.ocr.blocks??[]).map(r=>wn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function on(e,t,n=!0){const r=n?an(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function an(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!sn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function sn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||un(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function cn(e,t,n){const r=(e.blocks??[]).map(a=>Ne(a)).filter(a=>!!a),i=r.filter(a=>K(p(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>p(d.text)===a);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Te(a.text)).filter(a=>!Ee(a,r)).filter(a=>z(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Be(s,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function dn(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function ln(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function un(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function wn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const l=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return l||w}):!1}function Y(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
- `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((a,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return a+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(a=>Number(a.bbox?.y)<e.capture.height*.14).map(a=>String(a.text||"")).join(`
6
- `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&hn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?P(a.title):V(a.title),l=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):V(e.binding.conversationDisplayName);return!!(c&&l&&Re(c,l))}),!1)}function hn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
- `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function Pe(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=fn(e.ocr,e.screenshot).sort((o,s)=>v(s.text).length-v(o.text).length);for(const o of i){const s=v(o.text);if(!s)continue;const a=t.find(l=>mn(l.normalized,s));if(!a)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${a.idempotencyKey||a.replyId||a.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:s,anchorText:s,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function fn(e,t){const n=(e.blocks??[]).filter(i=>vn(i,t)&&v(i.text)).sort((i,o)=>C(i.bbox,"y")-C(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let s=i+1;s<n.length&&o.length<5;s+=1){const a=n[s];if(!bn(o[o.length-1],a,t))break;o.push(a),r.push({text:gn(o.map(c=>String(c.text||"").trim())),bbox:xn(o.map(c=>c.bbox))})}}return r}function mn(e,t){if(!e||!t)return!1;if(ee(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function bn(e,t,n){const r=C(e.bbox,"x"),i=C(e.bbox,"y"),o=C(e.bbox,"height"),s=C(t.bbox,"x"),a=C(t.bbox,"y");if(![r,i,o,s,a].every(Number.isFinite)||r<n.width*.5||s<n.width*.5)return!1;const c=a-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(s-r)<=120}function gn(e){return e.reduce((t,n)=>t?n?`${t}${yn(t,n)?" ":""}${n}`:t:n,"")}function yn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function xn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const s=Math.min(...n),a=Math.min(...r);return{x:s,y:a,width:Math.max(...i)-s,height:Math.max(...o)-a,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function We(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=v(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&ee(a,i)>=.9})||n.push(r)}return n}function vn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function pn(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(s*.35,110*l),y:o+Math.min(a-8*w,Math.max(18*w,a/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),f=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-f)/w}}function $(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+s/2,y:o+a/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),f=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-f)/u}}function Cn(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),s=i.bbox;if(!s||!o)return!1;const a=Number(s.x),c=Number(s.y);return!Number.isFinite(a)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:a>t.width*.55&&c>t.height*.45&&c<t.height*.92});return $(r?.bbox,t,n)}function Re(e,t){return y(t)?Ae(e,t):e===t||e.includes(t)||t.includes(e)}function z(e,t){if(y(t))return Ae(e,t);const n=Fe(p(e)),r=Fe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Fe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function V(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ae(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,H as captureAndRecognizeWeChatWindow,T as captureWeChatWindow,Fn as detectWeChatMessageInputPointByServerVision,rt as detectWeChatSearchInputPointByServerVision,Lt as ensureHelperPermissionPreflight,Dn as ensureHelperPreflight,Vt as ensureWeChatWindowReady,Gt as focusExistingWeChatWindow,Ie as focusKnownWeChatWindow,Ln as focusWeChatWindow,Rn as observeWeChatChannelBindingViaHelper,G as openConversationBySearch,j as openConversationInVisibleList,Me as recognizeWeChatScreenshot,tt as selectWeChatWindowForBinding};
1
+ import R from"node:fs";import Z from"node:path";import{WeChatChannelApiError as Le}from"./client.js";import{defaultWeChatChannelAttachmentDir as De,resolveVisibleWeChatChannelMedia as Ke}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as $e,loadWeChatChannelVectorStore as ze,saveWeChatChannelVectorStore as Ve,upsertWeChatChannelVectorReferences as qe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as Q,normalizeWeChatChannelMessageKind as je}from"./core/schema.js";import{waitForWeChatChannelPacing as ee,weChatChannelPacingDelayMs as Ge}from"./pacing.js";import{matchWeChatConversationFingerprints as He}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as te}from"./anchor.js";import{decideWeChatChannelActivityGate as Ue,normalizeWeChatChannelActivitySnapshot as Ye}from"./human-coordination.js";const Xe=3,Je=5,Ze="wechat_login_required",Qe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function An(e){return await ft(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>et({...e,helper:n}))}async function et(e){const t=e.foregroundMode??"required";let n=await tt(e,{foregroundMode:t});if(t!=="background"&&(n=await Ie(e.helper,n,e.traceId)),t==="background"){const{capture:a,ocr:s}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=W({capture:a,ocr:s,window:n});if(c)throw new Error(c);if(!O(s,a,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(X({capture:a,ocr:s,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:a,ocr:s}),le(e)?ue({...e,window:n,capture:a,ocr:s}):q({...e,window:n,capture:a,ocr:s})}const r=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const a=await ce({...e,fallback:n,requireTargetConversation:!1,force:!0});if(a.windowId!==n.windowId){n=await Ie(e.helper,a,e.traceId);const s=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(X({capture:i,ocr:o,binding:e.binding})){n=await Ut(e.helper,e.traceId);const a=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),X({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),le(e)?ue({...e,window:n,capture:i,ocr:o}):q({...e,window:n,capture:i,ocr:o})}async function tt(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await jt(e.helper,e.traceId,{foreground:n});return n==="background"?r:ce({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const x=new Map;function nt(){return process.env.SHENNIAN_WECHAT_VISION_LAYOUT_REUSE!=="0"}function ne(e){if(!nt()||!e.classification||e.classification.windowKind!=="chat_main")return;const t=e.window.windowId;if(!t)return;const n=se(e.window),r=A(e.classification.layout?.messageInputRect,e.screenshot,e.window,"message-input"),i=A(e.classification.layout?.searchInputRect,e.screenshot,e.window,"search-input");if(!r&&!i)return;const o={windowId:t,boundsKey:n,messageInputPoint:r,searchInputPoint:i,updatedAt:new Date().toISOString()},a=`${e.runtime.runtimeId}:${e.binding.bindingId}:${t}`,s=`${e.runtime.runtimeId}:window:${t}:${n}`;x.set(a,o),x.set(s,o),F(e,a,o),F(e,s,o)}async function Ln(e){const t=await re(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function rt(e){const t=await re(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function re(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=se(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,a=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,s=x.get(o)??x.get(a)??oe(e,o)??oe(e,a);if(s&&s.windowId===r&&s.boundsKey===i&&(!t||s[t]))return x.set(o,s),x.set(a,s),s;const c=e.screenshot??await T(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=A(l.layout?.messageInputRect,c,e.window,"message-input"),u=A(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return x.set(o,d),x.set(a,d),F(e,o,d),F(e,a,d),d}function ie(e){const t=e.workDir;return t?Z.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function oe(e,t){const n=ie(e);if(!n)return null;try{const r=JSON.parse(R.readFileSync(n,"utf8"));return it(r[t])}catch{return null}}function F(e,t,n){const r=ie(e);if(r)try{let i={};try{i=JSON.parse(R.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,R.mkdirSync(Z.dirname(r),{recursive:!0}),R.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
+ `,"utf8")}catch{}}function it(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=ae(t.messageInputPoint??t.point),o=ae(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function ae(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function se(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function A(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),a=Number(e?.width),s=Number(e?.height);if(![i,o,a,s].every(Number.isFinite)||a<=0||s<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:a,height:s,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:a/999*t.width,height:s/999*t.height,coordinateSpace:"screenshotPixel"},l=ot(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function ot(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=at({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function at(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),a=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-a)/i}}async function ce(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(B).filter(a=>a.visible!==!1&&a.minimized!==!0&&!!a.windowId),r=dt(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const a of r)try{const s=await T(e.helper,a.windowId,e.traceId,a.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:s.mimeType,dataBase64:s.dataBase64,width:s.width,height:s.height,windowId:a.windowId},traceId:e.traceId});i.push({window:a,classification:c,screenshot:s,score:lt(c,e.requireTargetConversation===!0)})}catch(s){if(st(s))throw s;i.push({window:a,classification:null,errorSummary:s instanceof Error?s.message:String(s||"window classification failed"),score:-1})}const o=i.filter(a=>a.score>0).sort((a,s)=>s.score-a.score)[0];if(!o){const a=ut(i,e.fallback);if(a)return console.warn(`[wechat-channel] window classifier fallback to local Windows WeChat shell windowId=${a.window.windowId}`),a.window;const s=i.map(c=>({windowId:c.window.windowId,appName:c.window.appName,title:c.window.title,score:c.score,errorSummary:c.errorSummary,classification:c.classification?{windowKind:c.classification.windowKind,reasonCode:c.classification.reasonCode,confidence:c.classification.confidence,conversationTitle:c.classification.conversationTitle,isTargetConversation:c.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.screenshot&&ne({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:o.window,classification:o.classification,screenshot:o.screenshot}),o.window}async function Dn(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function st(e){const t=ct(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ct(e){if(e instanceof Le||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function dt(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function lt(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ut(e,t){const n=e.filter(i=>i.score<=0).filter(i=>i.classification?.reasonCode==="server_window_classified").filter(i=>wt(i.window));return n.length===1?n[0]:n.find(i=>i.window.windowId===t.windowId)??null}function wt(e){if(!B(e)||e.visible===!1||e.minimized===!0||!j(e.title))return!1;const t=String(e.className??"");if(!/^Qt\d+QWindowIcon$/i.test(t))return!1;const n=e.bounds;return n?Number(n.width)>=700&&Number(n.height)>=500:!1}function ft(e,t,n){if(!t)return{helper:e,withLease:async(s,c,l)=>await l(e)};let r=null;const i=s=>{t.onEvent?.({...s,at:new Date().toISOString()})},o=async(s,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},h=u.ok&&d.interrupted?de(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:s,action:c,ok:u.ok===!0&&!d.interrupted,...h?{reasonCode:h}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:de(d.interruptReason),stage:s,action:c})}const w=Date.now()+mt(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:s,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});const d=Ue({snapshot:Ye(u.result),stage:s,policy:t.policy});if(i({phase:"snapshot",stage:s,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const h=new N({reasonCode:d.reasonCode,stage:s,action:c});if(!l.settle)throw h;const J=w-Date.now();if(J<=0)throw h;await Y(Math.max(50,Math.min(d.waitMs,J)))}},a={request:async(s,c,l)=>{const w=ht(s);w&&await o(w.stage,`${s}:before`);const u=await e.request(s,c,l);return w&&u.ok&&await o(w.stage,`${s}:after`),u}};return{helper:a,async withLease(s,c,l){if(await Ot(e,n),await o(s,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(a);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:s,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:s,action:c});try{const u=await l(a);return await o(s,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(h=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:h instanceof Error?h.message:String(h)}));i({phase:"lease-release",stage:s,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function ht(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function de(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function mt(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Qe}function le(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function q(e){const t=await Se(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await me({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return Pe(r,We({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function ue(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Se(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(bt(c))return q(e);throw c}const r=kt(n.structuredMessages??n.observedMessages??[]),i=await gt({...e,messages:r,screenshot:t}),o=await me({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=Pe(o,We({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function bt(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function gt(e){const t=yt(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await vt({...e,seedBlocks:t}):t.filter(a=>f(a.vectorBase64)||f(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??$e(e.workDir||process.cwd(),e.runtime.runtimeId),i=ze(r,e.runtime.runtimeId),o=qe({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ve(r,i),{messages:It(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function yt(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=f(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=f(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function xt(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return Ct(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function vt(e){try{return await xt(e)}catch(t){if(pt(t))throw t;return[]}}function pt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function Ct(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:f(n.stableMessageKey)||f(i?.stableMessageKey),blockId:f(n.blockId)||f(i?.blockId),blockKind:f(n.blockKind)||f(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>f(n.stableMessageKey)&&f(n.blockId))}function It(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:_t(r,i)}:r})}function _t(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=f(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(l=>l.blockId===s);return c?we(c,o):fe(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(we(o));return r}function we(e,t){return{...t?fe(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function fe(e){const{vectorBase64:t,...n}=e;return n}function kt(e){return e.map((t,n)=>St(t,n)).filter(t=>t!=null)}function St(e,t){if(!b(e))return null;const n=je(e.kind),r=Tt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=f(e.stableMessageKey)||Mt({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:Nt(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function Mt(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||f(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function Nt(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=Q(t.availability)),b(t.attachment)&&(t.attachment=he(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?he(n):n)),t}function he(e){const t={...e};return I(t,"availability")&&(t.availability=Q(t.availability)),t}function Tt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function me(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>At(o,e.screenshot,e.window,Ft(e.messages,a))).filter((o,a)=>Bt(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??De(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ke({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=i.get(o.stableMessageKey);return a?{...o,mediaMetadata:Lt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Bt(e,t,n){const r=ge(n);if(!r)return!0;const i=Et(r),o=e.map((a,s)=>ge(Rt(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Wt(e,i);for(const a of o)if(t>=a&&t<=a+8||t===a-1&&!e.slice(a+1,Math.min(e.length,a+9)).some(be))return!0;return!1}function Et(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Wt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(be(e[n]))return Pt(e[n],t)?n:-1;return-1}function be(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return ye(t,n,r)}function Pt(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),a=String(i.mimeType||"").toLowerCase(),s=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function Rt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
3
+ `)}function ge(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Ft(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
4
+ `)||null}function At(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=f(a.availability)||f(o.availability),c=f(a.localPath)||f(o.localPath);if(s==="edge-local"&&c||!ye(i,o,a))return null;const l=L(e.bbox)??L(o.bbox)??L(o.downloadActionBbox),w=L(o.downloadActionBbox),u=xe(l,t,n),d=Kt(l,t,n),h=xe(w,t,n);return{messageKey:e.stableMessageKey,kind:f(a.type)||f(a.kind)||f(o.messageType)||i,fileName:f(a.name)||f(a.fileName)||f(o.fileName)||null,mimeType:f(a.mimeType)||f(o.mimeType)||null,size:_(a.size)??_(o.size),mediaStatus:Dt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...h?{downloadActionBbox:h}:{}}}function Lt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function Dt(e,t){const n=f(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=f(t.availability)||f(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ye(e,t,n){const r=`${e} ${f(t.messageType)} ${f(n.type)} ${f(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function L(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function xe(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,a=i.height/t.height,s=r.x,c=r.y;return{x:s-e.width*o/2,y:c-e.height*a/2,width:e.width*o,height:e.height*a,coordinateSpace:"screen"}}function Kt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function f(e){return typeof e=="string"?e.trim():""}function g(e){return f(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Kn(e,t){await pe(e,t)}async function Ot(e,t){await ve(e,t)}async function ve(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if(Vt(r.platform)){const i=await $t(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function pe(e,t,n={}){const r=await ve(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(Ht(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function $t(e,t,n){const r=zt(t);if(!r||process.platform!=="darwin")return r;const i=qt(r);try{await e.request(i,{},n)}catch{}return r}function zt(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function Vt(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function qt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function jt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await pe(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await Gt(e,l,t)}const a=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(a,"windows.ensureReady");const s=a.result;if(!s?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return s}async function Gt(e,t,n){if(j(t.title))return t;try{const r=await e.request("windows.list",{},n);m(r,"windows.list");const o=(r.result?.windows??[]).find(a=>a.windowId&&a.visible!==!1&&a.minimized!==!0&&B(a)&&j(a.title));if(!o)return t;if(t.windowId&&t.windowId!==o.windowId)try{await e.request("windows.closeWindow",{windowId:t.windowId},n)}catch{}return o}catch{return t}}function j(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function Ht(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(B),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function On(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Ut(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(B);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function G(e){const{capture:t,ocr:n}=e.observation??await k(e),r=W({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:sn(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=In(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(a,"mouse.click"),{opened:!0,reason:i.reason}}async function H(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Yt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Yt(e){const t=await k(e),n=W({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await G({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const h=await ke(e);if(h.opened)return h}}const i=await tn(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??D(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const a=await Xt(e);if(a.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason}):{opened:!1,reason:u.reason}}let s=a.observation;const c=y(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await rn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??D(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),s=void 0}const l=await G({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await G(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Xt(e){let t;const n=e.searchDelayMs===0?0:D(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await Y(n);const o=await Jt(e);t=o;const{capture:a,ocr:s}=o,c=W({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=ln(s,a,e.binding.conversationDisplayName);if(!l){if(un(s)||wn(s,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,a,Zt(a,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Jt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Zt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Qt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await ee("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Qt(e){const t=en(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function en(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-a)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(s?.bbox,t,n)}async function tn(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function U(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),a=await Me(e,o,n);return{capture:o,ocr:a}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function B(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function Y(e){return new Promise(t=>setTimeout(t,e))}function D(e,t,n){return e.searchDelayMs===0?0:Ge(t,nn(e,n))}async function S(e,t,n){const r=D(e,t,n);r>0&&await Y(r)}function nn(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function rn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function E(e){if(!e.window.bounds)return;const t=e.observation,n=_n(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await ee("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=W({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!on(n)&&await an({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function on(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function an(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(ne({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function W(e){const n=(e.ocr.blocks??[]).map(r=>hn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function sn(e,t,n=!0){const r=n?cn(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function cn(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!dn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![r,i,o,a].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:a}}function dn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||fn(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function ln(e,t,n){const r=(e.blocks??[]).map(s=>Ne(s)).filter(s=>!!s),i=r.filter(s=>K(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=i.findIndex(d=>p(d.text)===s);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,h)=>d.y-h.y||d.x-h.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const a=r.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!Te(s.text)).filter(s=>!Ee(s,r)).filter(s=>z(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:Be(a,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function un(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function wn(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function fn(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function hn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const a=i.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const l=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,w=s<t.width*.18&&c<t.height*.12;return l||w}):!1}function X(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
+ `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((s,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return s+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(s=>Number(s.bbox?.y)<e.capture.height*.14).map(s=>String(s.text||"")).join(`
6
+ `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&mn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?P(s.title):V(s.title),l=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):V(e.binding.conversationDisplayName);return!!(c&&l&&Re(c,l))}),!1)}function mn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
+ `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function We(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=bn(e.ocr,e.screenshot).sort((o,a)=>v(a.text).length-v(o.text).length);for(const o of i){const a=v(o.text);if(!a)continue;const s=t.find(l=>gn(l.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:a,anchorText:a,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function bn(e,t){const n=(e.blocks??[]).filter(i=>Cn(i,t)&&v(i.text)).sort((i,o)=>C(i.bbox,"y")-C(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let a=i+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!yn(o[o.length-1],s,t))break;o.push(s),r.push({text:xn(o.map(c=>String(c.text||"").trim())),bbox:pn(o.map(c=>c.bbox))})}}return r}function gn(e,t){if(!e||!t)return!1;if(te(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function yn(e,t,n){const r=C(e.bbox,"x"),i=C(e.bbox,"y"),o=C(e.bbox,"height"),a=C(t.bbox,"x"),s=C(t.bbox,"y");if(![r,i,o,a,s].every(Number.isFinite)||r<n.width*.5||a<n.width*.5)return!1;const c=s-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-r)<=120}function xn(e){return e.reduce((t,n)=>t?n?`${t}${vn(t,n)?" ":""}${n}`:t:n,"")}function vn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function pn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...r);return{x:a,y:s,width:Math.max(...i)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Pe(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=v(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&te(s,i)>=.9})||n.push(r)}return n}function Cn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function In(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(a*.35,110*l),y:o+Math.min(s-8*w,Math.max(18*w,s/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),h=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-h)/w}}function $(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),h=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-h)/u}}function _n(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),a=i.bbox;if(!a||!o)return!1;const s=Number(a.x),c=Number(a.y);return!Number.isFinite(s)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:s>t.width*.55&&c>t.height*.45&&c<t.height*.92});return $(r?.bbox,t,n)}function Re(e,t){return y(t)?Ae(e,t):e===t||e.includes(t)||t.includes(e)}function z(e,t){if(y(t))return Ae(e,t);const n=Fe(p(e)),r=Fe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Fe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function V(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ae(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,U as captureAndRecognizeWeChatWindow,T as captureWeChatWindow,Ln as detectWeChatMessageInputPointByServerVision,rt as detectWeChatSearchInputPointByServerVision,Ot as ensureHelperPermissionPreflight,Kn as ensureHelperPreflight,jt as ensureWeChatWindowReady,Ut as focusExistingWeChatWindow,Ie as focusKnownWeChatWindow,On as focusWeChatWindow,An as observeWeChatChannelBindingViaHelper,H as openConversationBySearch,G as openConversationInVisibleList,Me as recognizeWeChatScreenshot,tt as selectWeChatWindowForBinding};
@@ -1,4 +1,4 @@
1
- import I from"node:crypto";import m from"node:fs";import f from"node:path";import{fileURLToPath as ee,pathToFileURL as te}from"node:url";import{ChannelSecretRegistry as re}from"./secret-registry.js";import{resolveShennianPath as ne}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as ae}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as se,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ie,countPendingWeChatRpaProductOutbound as oe,createWeChatRpaProductSourceConnection as ue,enqueueWeChatRpaProductOutboundReply as de,isWeChatRpaProductSource as y,listWeChatRpaProductRecentMessages as ce,listPendingWeChatRpaProductOutbound as le,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as H}from"./wechat-channel/client.js";const pe=5e3,A=5,fe=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),W=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),E=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function me(t){return P(t,A,1,50)>=fe?"scroll-read":"read-latest"}class it{onMessage;options;type="wechat-rpa";secrets=new re;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)){const a=await this.stopProductWhenControlPlaneDisabled(n,r);if(a||await this.ensureProductConnection(n,r),n.timer)return;!r.deferInitialPoll&&!a&&await this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] autonomous poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)})},n.product?.runtime.policy.pollIntervalMs??j(r.pollIntervalMs)),n.timer.unref();return}n.timer||(r.deferInitialPoll||await this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},j(r.pollIntervalMs)),n.timer.unref())}async disconnect(e){const r=this.connections.get(e.id);if(!r)return;r.stopped=!0,r.timer&&clearInterval(r.timer),r.timer=null;const n=r.product;await this.enqueueOperation(r,async()=>{n&&await n.runner.stop()}).catch(()=>{}),this.connections.delete(e.id)}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)?this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config);if(await this.stopProductWhenControlPlaneDisabled(n,a))return[];const i=await this.pollProductOnce(n,a,{forceObserve:!0}),s=n.product;if(!s)return i;const u=ce({config:n.config,product:s,limit:a.recentLimit});return Xe(u,i)}):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ie({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,M(a,e),O(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const d=this.resolveConversationName(e,n,r.conversationId);if(!d)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const h=r.text.trim();if(!h&&!r.attachment)throw new Error("Reply text or attachment is required");if(await this.stopProductWhenControlPlaneDisabled(a,n))throw new Error("WeChat RPA channel is disabled");return de({config:a.config,secret:n,reply:{...r,text:h},conversationName:d}),a.runtimeState="retrying",w(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${d}`}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!Ee(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Te(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await Be(e.workDir,r.attachment):void 0,c=r.text.trim();if(!c&&!o)throw new Error("Reply text or attachment is required");if(Z(a,n)){const d="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:c,attachmentPath:o,reason:d}),{status:"queued",reason:d}}let l;try{a.runtimeState="syncing",l=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),V(e,n,i,c,o,s)))}catch(d){throw a.lastError=d instanceof Error?d.message:String(d),w(a,{status:b(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),d}$(a,l);const p=Y(l,{text:c,attachmentPath:o});if(p.status==="queued"){const d=p.reason;return T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:p.attachmentPath,reason:d,skipText:p.skipText}),a.lastError=d,{status:"queued",reason:d}}if(l.interrupted){const d=l.error||"WeChat RPA send was interrupted by user activity";return _(a,l,d),T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:o,reason:d,skipText:p.skipText}),{status:"queued",reason:d}}if(!l.ok){const d=l.error||"WeChat RPA send validator failed; manual review required before retry";return Q(a,s,d),{status:"manual-review",reason:d}}return w(a,{status:"sent",runId:a.lastRunId??null,summary:l.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&m.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!g(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return Se(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!g(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{J(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return ve(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=g(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:N(n),conversationName:n}:{conversationId:x(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??r.product?.helperTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+oe({config:e}),wechatRpaPendingReplies:Me(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new K,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=this.options.automationLane?()=>this.options.automationLane?.run(`wechat-rpa:${e.config.id}`,r):r,a=e.operation.then(n,n);return e.operation=a.then(()=>{},()=>{}),a}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(Z(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,_(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=se(u,{selfNicknames:U(r)});if(!o||!e.deduper.accept(o.messageId))continue;De(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const c={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(c)??c),w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${L(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!y(r.source))return[];if(await this.stopProductWhenControlPlaneDisabled(e,r))return[];const a=await this.ensureProductConnection(e,r);await B(a).catch(()=>null);const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const s=a.emitted.slice(i);e.lastError=null;for(const u of s)e.conversations.set(u.conversationId,u.conversationName||""),e.lastMessageAt=u.receivedAt,w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${u.conversationName||u.conversationId}${u.sender.name?` from ${u.sender.name}`:""}: ${L(u.text||u.attachments[0]?.name||"attachment")}`});return C(e),s}catch(s){throw e.lastError=s instanceof Error?s.message:String(s),e.runtimeState=b(e.lastError)==="blocked"?"interrupted":"idle_waiting",w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),s}}async ensureProductConnection(e,r){const n=ge(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=ue({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await B(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}async stopProductWhenControlPlaneDisabled(e,r){if(!y(r.source))return!1;let n=null;try{n=await we(e.config,r)}catch(a){return e.productDisabled?(e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=a instanceof Error?a.message:String(a),!0):!1}if(n?.enabled!==!1)return e.productDisabled=!1,!1;if(e.productDisabled=!0,e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,e.product){const a=e.product;e.product=void 0,e.productConfigKey=void 0,await a.runner.stop().catch(()=>{})}return!0}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await V(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=Y(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,_(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Q(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,w(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return He(r.fixturePath);if(r.source==="macos-flow")return Ne(e,r,n);if(r.source==="wechat-rpa-lab")return Pe(e,r,n);if(r.source==="windows-visual-flow")return We(e,r,n);const a=await q(),i=ae(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of g(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(g(r).find(s=>x(s)===n||N(s)===n)??null)}}function ge(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:k(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:g(e)})}async function we(t,e,r){const n=r??H(),a=await n.getRuntimePolicy();if(a.feature?.enabled===!1)return{enabled:!1,reasonCode:a.feature.reasonCode||"feature_disabled"};if(a.runtime?.current?.status==="disabled")return{enabled:!1,reasonCode:"runtime_disabled"};const i=t.sessionId||t.managerSessionId;if(!i)return{enabled:!1,reasonCode:"session_missing"};let s;try{s=(await n.getBinding({sessionId:i})).binding}catch(o){const c=ye(o);if(c==="binding_not_found"||c==="session_not_found"||c==="machine_not_found")return{enabled:!1,reasonCode:c};throw o}if(!s||s.enabled===!1)return{enabled:!1,reasonCode:"no_enabled_binding"};const u=g(e);return u.length?u.includes(s.conversationName)?{enabled:!0}:{enabled:!1,reasonCode:"binding_conversation_changed"}:{enabled:!1,reasonCode:"no_configured_conversation"}}async function B(t){const r=(await H().getRuntimePolicy()).runtime?.current;return t.runtime.foregroundPolicy="work",{enabled:r?.status!=="disabled"}}function ye(t){if(t&&typeof t=="object"){const e=t.reasonCode;if(typeof e=="string")return e}return""}function j(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):pe}function g(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function U(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function Re(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function ve(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function Se(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Pe(t,e,r){const n=await z(t.workDir),a=[];for(const i of g(e)){const s=await Ie(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=G(i,u,e);o&&a.push(o)}}return a}async function Ie(t,e,r,n){const a=me(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:D(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],c=u.length?u.map((l,p)=>Ae(l,s.runId||"lab-read",p)):o.map((l,p)=>({id:`${s.runId||"lab-read"}:${p}`,text:l,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:c,newMessages:c,screenshotPath:s.tracePath,error:X(s)}}function Ae(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Ce(t)}}function Ce(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function be(t,e,r,n,a,i){const s=await z(t.workDir),u=ke({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const h of u){const v=await s.runWechatRpaTask(h);if(o.push(v),!v.ok)break}const c=o.find(h=>h.kind==="send-text"),l=o.find(h=>h.kind==="send-image"||h.kind==="send-video"||h.kind==="send-file"),p=u.length>0&&o.length===u.length&&o.every(h=>h.ok),d=o.some(h=>h.status==="interrupted");return{ok:p,groupName:r,interrupted:d,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!c?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!l?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:p?void 0:X(o.at(-1))}}function ke(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=xe(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function xe(t){const e=f.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function z(t){const e=J(t);return await import(te(e).href)}function J(t){const e=f.dirname(ee(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,f.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?f.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",f.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>m.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return f.resolve(n)}function X(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function Ne(t,e,r){const n=[];for(const a of g(e)){const i=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=G(a,s,e);u&&n.push(u)}}return n}async function We(t,e,r){const n=g(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function G(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:Je(a),observedAt:s,isMentioned:e.isMentioned===!0||Re(n,U(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function V(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?be(t,e,r,n,a,i):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)})}function Ee(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function Y(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Te(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
1
+ import I from"node:crypto";import m from"node:fs";import f from"node:path";import{fileURLToPath as ee,pathToFileURL as te}from"node:url";import{ChannelSecretRegistry as re}from"./secret-registry.js";import{resolveShennianPath as ne}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as ae}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as se,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ie,countPendingWeChatRpaProductOutbound as oe,createWeChatRpaProductSourceConnection as ue,enqueueWeChatRpaProductOutboundReply as de,isWeChatRpaProductSource as y,listWeChatRpaProductRecentMessages as ce,listPendingWeChatRpaProductOutbound as le,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as H}from"./wechat-channel/client.js";const pe=5e3,A=5,fe=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),W=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),E=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function me(t){return P(t,A,1,50)>=fe?"scroll-read":"read-latest"}class it{onMessage;options;type="wechat-rpa";secrets=new re;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)){const a=await this.stopProductWhenControlPlaneDisabled(n,r);if(a||await this.ensureProductConnection(n,r),n.timer)return;n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] autonomous poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)})},n.product?.runtime.policy.pollIntervalMs??j(r.pollIntervalMs)),n.timer.unref(),!r.deferInitialPoll&&!a&&this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] initial poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)});return}n.timer||(n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},j(r.pollIntervalMs)),n.timer.unref(),r.deferInitialPoll||this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{}))}async disconnect(e){const r=this.connections.get(e.id);if(!r)return;r.stopped=!0,r.timer&&clearInterval(r.timer),r.timer=null;const n=r.product;await this.enqueueOperation(r,async()=>{n&&await n.runner.stop()}).catch(()=>{}),this.connections.delete(e.id)}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)?this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config);if(await this.stopProductWhenControlPlaneDisabled(n,a))return[];const i=await this.pollProductOnce(n,a,{forceObserve:!0}),s=n.product;if(!s)return i;const u=ce({config:n.config,product:s,limit:a.recentLimit});return Xe(u,i)}):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ie({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,M(a,e),O(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const d=this.resolveConversationName(e,n,r.conversationId);if(!d)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const h=r.text.trim();if(!h&&!r.attachment)throw new Error("Reply text or attachment is required");if(await this.stopProductWhenControlPlaneDisabled(a,n))throw new Error("WeChat RPA channel is disabled");return de({config:a.config,secret:n,reply:{...r,text:h},conversationName:d}),a.runtimeState="retrying",w(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${d}`}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!Ee(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Te(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await Be(e.workDir,r.attachment):void 0,c=r.text.trim();if(!c&&!o)throw new Error("Reply text or attachment is required");if(Z(a,n)){const d="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:c,attachmentPath:o,reason:d}),{status:"queued",reason:d}}let l;try{a.runtimeState="syncing",l=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),V(e,n,i,c,o,s)))}catch(d){throw a.lastError=d instanceof Error?d.message:String(d),w(a,{status:b(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),d}$(a,l);const p=Y(l,{text:c,attachmentPath:o});if(p.status==="queued"){const d=p.reason;return T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:p.attachmentPath,reason:d,skipText:p.skipText}),a.lastError=d,{status:"queued",reason:d}}if(l.interrupted){const d=l.error||"WeChat RPA send was interrupted by user activity";return _(a,l,d),T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:o,reason:d,skipText:p.skipText}),{status:"queued",reason:d}}if(!l.ok){const d=l.error||"WeChat RPA send validator failed; manual review required before retry";return Q(a,s,d),{status:"manual-review",reason:d}}return w(a,{status:"sent",runId:a.lastRunId??null,summary:l.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&m.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!g(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return Se(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!g(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{J(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return ve(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=g(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:N(n),conversationName:n}:{conversationId:x(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??r.product?.helperTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+oe({config:e}),wechatRpaPendingReplies:Me(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new K,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=this.options.automationLane?()=>this.options.automationLane?.run(`wechat-rpa:${e.config.id}`,r):r,a=e.operation.then(n,n);return e.operation=a.then(()=>{},()=>{}),a}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(Z(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,_(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=se(u,{selfNicknames:U(r)});if(!o||!e.deduper.accept(o.messageId))continue;De(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const c={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(c)??c),w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${L(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!y(r.source))return[];if(await this.stopProductWhenControlPlaneDisabled(e,r))return[];const a=await this.ensureProductConnection(e,r);await B(a).catch(()=>null);const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const s=a.emitted.slice(i);e.lastError=null;for(const u of s)e.conversations.set(u.conversationId,u.conversationName||""),e.lastMessageAt=u.receivedAt,w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${u.conversationName||u.conversationId}${u.sender.name?` from ${u.sender.name}`:""}: ${L(u.text||u.attachments[0]?.name||"attachment")}`});return C(e),s}catch(s){throw e.lastError=s instanceof Error?s.message:String(s),e.runtimeState=b(e.lastError)==="blocked"?"interrupted":"idle_waiting",w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),s}}async ensureProductConnection(e,r){const n=ge(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=ue({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await B(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}async stopProductWhenControlPlaneDisabled(e,r){if(!y(r.source))return!1;let n=null;try{n=await we(e.config,r)}catch(a){return e.productDisabled?(e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=a instanceof Error?a.message:String(a),!0):!1}if(n?.enabled!==!1)return e.productDisabled=!1,!1;if(e.productDisabled=!0,e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,e.product){const a=e.product;e.product=void 0,e.productConfigKey=void 0,await a.runner.stop().catch(()=>{})}return!0}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await V(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=Y(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,_(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Q(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,w(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return He(r.fixturePath);if(r.source==="macos-flow")return Ne(e,r,n);if(r.source==="wechat-rpa-lab")return Pe(e,r,n);if(r.source==="windows-visual-flow")return We(e,r,n);const a=await q(),i=ae(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of g(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(g(r).find(s=>x(s)===n||N(s)===n)??null)}}function ge(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:k(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:g(e)})}async function we(t,e,r){const n=r??H(),a=await n.getRuntimePolicy();if(a.feature?.enabled===!1)return{enabled:!1,reasonCode:a.feature.reasonCode||"feature_disabled"};if(a.runtime?.current?.status==="disabled")return{enabled:!1,reasonCode:"runtime_disabled"};const i=t.sessionId||t.managerSessionId;if(!i)return{enabled:!1,reasonCode:"session_missing"};let s;try{s=(await n.getBinding({sessionId:i})).binding}catch(o){const c=ye(o);if(c==="binding_not_found"||c==="session_not_found"||c==="machine_not_found")return{enabled:!1,reasonCode:c};throw o}if(!s||s.enabled===!1)return{enabled:!1,reasonCode:"no_enabled_binding"};const u=g(e);return u.length?u.includes(s.conversationName)?{enabled:!0}:{enabled:!1,reasonCode:"binding_conversation_changed"}:{enabled:!1,reasonCode:"no_configured_conversation"}}async function B(t){const r=(await H().getRuntimePolicy()).runtime?.current;return t.runtime.foregroundPolicy="work",{enabled:r?.status!=="disabled"}}function ye(t){if(t&&typeof t=="object"){const e=t.reasonCode;if(typeof e=="string")return e}return""}function j(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):pe}function g(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function U(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function Re(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function ve(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function Se(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Pe(t,e,r){const n=await z(t.workDir),a=[];for(const i of g(e)){const s=await Ie(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=G(i,u,e);o&&a.push(o)}}return a}async function Ie(t,e,r,n){const a=me(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:D(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],c=u.length?u.map((l,p)=>Ae(l,s.runId||"lab-read",p)):o.map((l,p)=>({id:`${s.runId||"lab-read"}:${p}`,text:l,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:c,newMessages:c,screenshotPath:s.tracePath,error:X(s)}}function Ae(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Ce(t)}}function Ce(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function be(t,e,r,n,a,i){const s=await z(t.workDir),u=ke({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const h of u){const v=await s.runWechatRpaTask(h);if(o.push(v),!v.ok)break}const c=o.find(h=>h.kind==="send-text"),l=o.find(h=>h.kind==="send-image"||h.kind==="send-video"||h.kind==="send-file"),p=u.length>0&&o.length===u.length&&o.every(h=>h.ok),d=o.some(h=>h.status==="interrupted");return{ok:p,groupName:r,interrupted:d,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!c?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!l?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:p?void 0:X(o.at(-1))}}function ke(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=xe(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function xe(t){const e=f.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function z(t){const e=J(t);return await import(te(e).href)}function J(t){const e=f.dirname(ee(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,f.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?f.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",f.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>m.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return f.resolve(n)}function X(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function Ne(t,e,r){const n=[];for(const a of g(e)){const i=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=G(a,s,e);u&&n.push(u)}}return n}async function We(t,e,r){const n=g(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function G(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:Je(a),observedAt:s,isMentioned:e.isMentioned===!0||Re(n,U(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function V(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?be(t,e,r,n,a,i):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)})}function Ee(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function Y(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Te(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
2
2
  ${e.conversationId}
3
3
  ${e.text}
4
4
  ${e.attachment?.name||""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.130",
3
+ "version": "0.2.132",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {