shennian 0.2.108 → 0.2.109

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.
Files changed (38) hide show
  1. package/dist/publish-build-manifest.json +47 -37
  2. package/dist/src/agents/model-registry/discovery.js +2 -2
  3. package/dist/src/agents/pi-context.d.ts +1 -1
  4. package/dist/src/agents/pi-context.js +3 -3
  5. package/dist/src/channels/runtime.d.ts +2 -0
  6. package/dist/src/channels/runtime.js +1 -1
  7. package/dist/src/channels/secret-registry.d.ts +2 -0
  8. package/dist/src/channels/wechat-channel/client.d.ts +9 -2
  9. package/dist/src/channels/wechat-channel/client.js +1 -1
  10. package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -1
  11. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  12. package/dist/src/channels/wechat-channel/helper-client.d.ts +2 -0
  13. package/dist/src/channels/wechat-channel/helper-client.js +3 -3
  14. package/dist/src/channels/wechat-channel/observer.js +6 -6
  15. package/dist/src/channels/wechat-channel/runner.js +1 -1
  16. package/dist/src/channels/wechat-channel/runtime.d.ts +1 -0
  17. package/dist/src/channels/wechat-channel/runtime.js +1 -1
  18. package/dist/src/channels/wechat-channel/scheduler.d.ts +1 -1
  19. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  20. package/dist/src/channels/wechat-rpa/product-channel.d.ts +1 -0
  21. package/dist/src/channels/wechat-rpa/product-channel.js +4 -4
  22. package/dist/src/channels/wechat-rpa.d.ts +2 -0
  23. package/dist/src/channels/wechat-rpa.js +2 -2
  24. package/dist/src/commands/helper-runtime-official-download.d.ts +80 -0
  25. package/dist/src/commands/helper-runtime-official-download.js +1 -0
  26. package/dist/src/commands/runtime.d.ts +23 -0
  27. package/dist/src/commands/runtime.js +1 -1
  28. package/dist/src/commands/wechat/command.js +1 -1
  29. package/dist/src/commands/wechat/ipc.js +1 -1
  30. package/dist/src/commands/wechat/output.js +3 -3
  31. package/dist/src/manager/runtime.js +10 -10
  32. package/dist/src/session/handlers/wechat-helper-runtime.d.ts +31 -0
  33. package/dist/src/session/handlers/wechat-helper-runtime.js +1 -0
  34. package/dist/src/session/manager.d.ts +3 -0
  35. package/dist/src/session/manager.js +1 -1
  36. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  37. package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
  38. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- import{defaultWeChatChannelAttachmentDir as _e,resolveVisibleWeChatChannelMedia as Me}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Se}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Ne,loadWeChatChannelVectorStore as Te,saveWeChatChannelVectorStore as Be,upsertWeChatChannelVectorReferences as Ee}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as H,normalizeWeChatChannelMessageKind as We}from"./core/schema.js";import{waitForWeChatChannelPacing as U,weChatChannelPacingDelayMs as Re}from"./pacing.js";import{matchWeChatConversationFingerprints as Fe}from"./fingerprint.js";import{normalizeWeChatAnchorText as x,weChatTextSimilarity as Y}from"./anchor.js";import{decideWeChatChannelActivityGate as Ae,normalizeWeChatChannelActivitySnapshot as Oe}from"./human-coordination.js";const Le=3,Pe=5,De="wechat_login_required",Ke=5500;class S 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 sn(e){return await je(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>$e({...e,helper:n}))}async function $e(e){const t=e.foregroundMode??"required";let n=await ze(e,{foregroundMode:t});if(t!=="background"&&(n=await ue(e.helper,n,e.traceId)),t==="background"){const{capture:a,ocr:s}=await I({helper:e.helper,window:n,traceId:e.traceId}),c=T({capture:a,ocr:s,window:n});if(c)throw new Error(c);if(!A(s,a,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(V({capture:a,ocr:s,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:a,ocr:s}),Z(e)?Q({...e,window:n,capture:a,ocr:s}):L({...e,window:n,capture:a,ocr:s})}const r=await D({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!r.opened){const a=await X({...e,fallback:n,requireTargetConversation:!1,force:!0});if(a.windowId!==n.windowId){n=await ue(e.helper,a,e.traceId);const s=await D({helper:e.helper,window:n,binding:e.binding,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 I({helper:e.helper,window:n,traceId:e.traceId});if(V({capture:i,ocr:o,binding:e.binding})){n=await It(e.helper,e.traceId);const a=await D({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await I({helper:e.helper,window:n,traceId:e.traceId}),V({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),Z(e)?Q({...e,window:n,capture:i,ocr:o}):L({...e,window:n,capture:i,ocr:o})}async function ze(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Ct(e.helper,e.traceId,{foreground:n});return n==="background"?r:X({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}async function X(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);b(t,"windows.list");const n=(t.result?.windows??[]).filter(z).filter(a=>a.visible!==!1&&a.minimized!==!0&&!!a.windowId),r=qe(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 K(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,score:Ve(c,e.requireTargetConversation===!0)})}catch{i.push({window:a,classification:null,score:-1})}const o=i.filter(a=>a.score>0).sort((a,s)=>s.score-a.score)[0];if(!o)throw new Error("wechat_chat_main_window_not_found");return o.window}function qe(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 Ve(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 je(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??{},f=u.ok&&d.interrupted?J(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,...f?{reasonCode:f}:{}}),!u.ok)throw new S({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new S({reasonCode:J(d.interruptReason),stage:s,action:c})}const w=Date.now()+He(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 S({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});const d=Ae({snapshot:Oe(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 f=new S({reasonCode:d.reasonCode,stage:s,action:c});if(!l.settle)throw f;const G=w-Date.now();if(G<=0)throw f;await q(Math.max(50,Math.min(d.waitMs,G)))}},a={request:async(s,c,l)=>{const w=Ge(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 yt(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 S({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(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:s,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function Ge(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 J(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 He(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Ke}function Z(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function L(e){const t=await fe(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??[]}),r=await re({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,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Q(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await fe(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??[]})}catch(c){if(Ue(c))return L(e);throw c}const r=rt(n.structuredMessages??n.observedMessages??[]),i=await Ye({...e,messages:r,screenshot:t}),o=await re({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,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Se({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function Ue(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function Ye(e){const t=Xe(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Ze({...e,seedBlocks:t}):t.filter(a=>h(a.vectorBase64)||h(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Ne(e.workDir||process.cwd(),e.runtime.runtimeId),i=Te(r,e.runtime.runtimeId),o=Ee({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Be(r,i),{messages:tt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Xe(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}`,a=h(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 Je(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 et(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Ze(e){try{return await Je(e)}catch(t){if(Qe(t))throw t;return[]}}function Qe(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function et(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 tt(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:nt(r,i)}:r})}function nt(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=h(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(l=>l.blockId===s);return c?ee(c,o):te(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(ee(o));return r}function ee(e,t){return{...t?te(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 te(e){const{vectorBase64:t,...n}=e;return n}function rt(e){return e.map((t,n)=>it(t,n)).filter(t=>t!=null)}function it(e,t){if(!m(e))return null;const n=We(e.kind),r=st(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=h(e.stableMessageKey)||ot({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}:{},...C(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...C(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...C(e,"bbox")?{bbox:e.bbox}:{},...C(e,"mediaMetadata")?{mediaMetadata:at(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 ot(e){const t=m(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 at(e){if(!m(e))return e;const t={...e};return C(t,"availability")&&(t.availability=H(t.availability)),m(t.attachment)&&(t.attachment=ne(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>m(n)?ne(n):n)),t}function ne(e){const t={...e};return C(t,"availability")&&(t.availability=H(t.availability)),t}function st(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function re(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>ft(o,e.screenshot,e.window,ht(e.messages,a))).filter((o,a)=>ct(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??_e(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Me({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:bt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function ct(e,t,n){const r=oe(n);if(!r)return!0;const i=dt(r),o=e.map((a,s)=>oe(wt(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===lt(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(ie))return!0;return!1}function dt(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 lt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(ie(e[n]))return ut(e[n],t)?n:-1;return-1}function ie(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=m(e.mediaMetadata)?e.mediaMetadata:{},r=m(n.attachment)?n.attachment:{};return ae(t,n,r)}function ut(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=m(e.mediaMetadata)?e.mediaMetadata:{},i=m(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 wt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
2
- `)}function oe(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function ht(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(`
3
- `)||null}function ft(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=m(e.mediaMetadata)?e.mediaMetadata:{},a=m(o.attachment)?o.attachment:{},s=h(a.availability)||h(o.availability),c=h(a.localPath)||h(o.localPath);if(s==="edge-local"&&c||!ae(i,o,a))return null;const l=W(e.bbox)??W(o.bbox)??W(o.downloadActionBbox),w=W(o.downloadActionBbox),u=se(l,t,n),d=gt(l,t,n),f=se(w,t,n);return{messageKey:e.stableMessageKey,kind:h(a.type)||h(a.kind)||h(o.messageType)||i,fileName:h(a.name)||h(a.fileName)||h(o.fileName)||null,mimeType:h(a.mimeType)||h(o.mimeType)||null,size:k(a.size)??k(o.size),mediaStatus:mt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function bt(e,t,n,r,i){const o=m(e)?{...e}:{};return{...o,availability:m(t)?t.availability:o.availability,mediaStatus:m(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function mt(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";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 ae(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 W(e){if(!m(e))return null;const t=k(e.x),n=k(e.y),r=k(e.width),i=k(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 se(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=B(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 gt(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 m(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function C(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 k(e){const t=Number(e);return Number.isFinite(t)?t:null}async function cn(e,t){await de(e,t)}async function yt(e,t){await ce(e,t)}async function ce(e,t){const n=await e.request("permissions.check",{},t);b(n,"permissions.check");const r=n.result??{},i=await xt(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 de(e,t,n={}){const r=await ce(e,t);if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(kt(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&&String(r.platform??"").toLowerCase()!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function xt(e,t,n){const r=vt(t);if(!r||process.platform!=="darwin")return r;const i=pt(r);try{await e.request(i,{},n)}catch{}return r}function vt(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function pt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Ct(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await de(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);b(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return l}const a=await e.request("windows.ensureReady",{restore:i,focus:i},t);b(a,"windows.ensureReady");const s=a.result;if(!s?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return s}function kt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function le(e){const t=e.filter(z),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function dn(e,t){let n=await e.request("windows.list",{},t);b(n,"windows.list");let r=n.result?.windows??[],i=le(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return b(o,"windows.focus"),await we(e,i,t)}async function ue(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 b(r,"windows.focus"),t}async function It(e,t){const n=await e.request("windows.list",{},t);b(n,"windows.list");const r=n.result?.windows??[],i=le(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return b(o,"windows.focus"),await we(e,i,t)}async function we(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(z);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function P(e){const{capture:t,ocr:n}=e.observation??await I(e),r=T({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=Fe({visibleItems:Ft(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=Xt(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 b(a,"mouse.click"),{opened:!0,reason:i.reason}}async function D(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Le;r+=1){if(t=await _t(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await _(e,"open-retry",`retry:${r}`)}const n=await he(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function _t(e){const t=await I(e),n=T({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(A(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await P({...e,observation:t});if(u.opened){if(await _(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await he(e);if(f.opened)return f}}const i=Et(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??R(e,"search-input-settle","search-input"),...i?{searchPoint:i}:{}},e.traceId);b(o,"wechat.searchConversation");const a=await Mt(e);if(a.opened){await _(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await N({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 Rt(e.helper,e.traceId),await _(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await N({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??R(e,"search-input-settle","search-input-retry-after-return"),...i?{searchPoint:i}:{}},e.traceId);b(d,"wechat.searchConversation"),s=void 0}const l=await P({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await _(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function he(e){const t=await P(e);if(!t.opened)return t;await _(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function ln(e){const t=e.bounds,n=Number(t?.x),r=Number(t?.y),i=Number(t?.width),o=Number(t?.height);if(![n,r,i,o].every(Number.isFinite)||i<=0||o<=0)return null;const a=Math.max(520,Math.min(i-160,i*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(r+o-s),coordinateSpace:"screen"}}async function Mt(e){let t;const n=e.searchDelayMs===0?0:R(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Pe:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await q(n);const o=await St(e);t=o;const{capture:a,ocr:s}=o,c=T({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=Lt(s,a,e.binding.conversationDisplayName);if(!l){if(Pt(s)||Dt(s,e.binding.conversationDisplayName))break;continue}const w=B(l.bbox,a,Nt(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 b(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function St(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 be(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&b(t,"screen.capture")}return I(e)}function Nt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function I(e){let{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Tt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await U("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Tt(e){const t=Bt(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 Bt(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 B(s?.bbox,t,n)}function Et(e,t){const{capture:n,ocr:r}=t,i=(r.blocks??[]).find(o=>{const a=String(o.text||"").trim().toLowerCase(),s=o.bbox;if(!s||!/^(搜索|search)$/.test(a))return!1;const c=Number(s.x),l=Number(s.y);return Number.isFinite(c)&&Number.isFinite(l)&&c<n.width*.22&&l<n.height*.12});return B(i?.bbox,n,e.window)}async function K(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);b(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 fe(e){return e.capture.dataBase64?e.capture:K(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function $(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"&&b(i,"windows.captureAndOcr");const o=await K(e,t,n,r),a=await be(e,o,n);return{capture:o,ocr:a}}async function be(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return b(r,"ocr.recognize"),r.result??{}}function b(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function z(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"}function q(e){return new Promise(t=>setTimeout(t,e))}function R(e,t,n){return e.searchDelayMs===0?0:Re(t,Wt(e,n))}async function _(e,t,n){const r=R(e,t,n);r>0&&await q(r)}function Wt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function Rt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);b(n,"keyboard.shortcut")}async function N(e){if(!e.window.bounds)return;const t=e.observation,n=Jt(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);b(r,"mouse.click"),await U("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=T({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:A(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function T(e){const n=(e.ocr.blocks??[]).map(r=>$t(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?De:null}function Ft(e,t,n=!0){const r=n?At(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function At(e,t){const n=e.map(i=>me(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=>!ge(i.text)),r=new Set;return n.filter(i=>!Ot(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=E(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function me(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 Ot(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 ge(e){const t=v(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||F(t)||Kt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Lt(e,t,n){const r=(e.blocks??[]).map(s=>me(s)).filter(s=>!!s),i=r.filter(s=>F(v(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=>v(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=>!F(v(d.text))).filter(d=>!xe(d,r)).filter(d=>j(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:ye(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=>!ge(s.text)).filter(s=>!xe(s,r)).filter(s=>j(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:ye(a,t),reason:"search-result-title"}:null}function ye(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 F(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function Pt(e){return(e.blocks??[]).some(t=>F(v(t.text)))}function Dt(e,t){return(e.visibleConversationFingerprints??[]).some(n=>j(n.title,t))}function v(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function xe(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 Kt(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 $t(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function A(e,t,n){const r=y(n)?E(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?E(i.text):O(i.text);if(!o||!Ce(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 V(e){if(A(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(`
4
- `),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(`
5
- `);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)&&zt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?E(s.title):O(s.title),l=y(e.binding.conversationDisplayName)?E(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&l&&Ce(c,l))}),!1)}function zt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
6
- `);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 ve(e){const t=e.anchors.map(o=>({...o,normalized:x(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=qt(e.ocr,e.screenshot).sort((o,a)=>x(a.text).length-x(o.text).length);for(const o of i){const a=x(o.text);if(!a)continue;const s=t.find(l=>Vt(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 qt(e,t){const n=(e.blocks??[]).filter(i=>Yt(i,t)&&x(i.text)).sort((i,o)=>p(i.bbox,"y")-p(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(!jt(o[o.length-1],s,t))break;o.push(s),r.push({text:Gt(o.map(c=>String(c.text||"").trim())),bbox:Ut(o.map(c=>c.bbox))})}}return r}function Vt(e,t){if(!e||!t)return!1;if(Y(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function jt(e,t,n){const r=p(e.bbox,"x"),i=p(e.bbox,"y"),o=p(e.bbox,"height"),a=p(t.bbox,"x"),s=p(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 Gt(e){return e.reduce((t,n)=>t?n?`${t}${Ht(t,n)?" ":""}${n}`:t:n,"")}function Ht(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Ut(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 p(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=x(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=x(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&Y(s,i)>=.9})||n.push(r)}return n}function Yt(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 Xt(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),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 B(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),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 Jt(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 B(r?.bbox,t,n)}function Ce(e,t){return y(t)?Ie(e,t):e===t||e.includes(t)||t.includes(e)}function j(e,t){if(y(t))return Ie(e,t);const n=ke(v(e)),r=ke(v(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function ke(e){return e.toLowerCase().replace(/[l1]/g,"i")}function O(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function E(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 Ie(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{S as WeChatChannelUserActivityAbort,$ as captureAndRecognizeWeChatWindow,K as captureWeChatWindow,yt as ensureHelperPermissionPreflight,cn as ensureHelperPreflight,Ct as ensureWeChatWindowReady,ln as fallbackWeChatMessageInputPointForWindow,It as focusExistingWeChatWindow,ue as focusKnownWeChatWindow,dn as focusWeChatWindow,sn as observeWeChatChannelBindingViaHelper,D as openConversationBySearch,P as openConversationInVisibleList,be as recognizeWeChatScreenshot,ze as selectWeChatWindowForBinding};
1
+ import{WeChatChannelApiError as Ie}from"./client.js";import{defaultWeChatChannelAttachmentDir as Me,resolveVisibleWeChatChannelMedia as Se}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Ne}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Te,loadWeChatChannelVectorStore as Be,saveWeChatChannelVectorStore as Ee,upsertWeChatChannelVectorReferences as We}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as H,normalizeWeChatChannelMessageKind as Re}from"./core/schema.js";import{waitForWeChatChannelPacing as U,weChatChannelPacingDelayMs as Ae}from"./pacing.js";import{matchWeChatConversationFingerprints as Fe}from"./fingerprint.js";import{normalizeWeChatAnchorText as x,weChatTextSimilarity as Y}from"./anchor.js";import{decideWeChatChannelActivityGate as Oe,normalizeWeChatChannelActivitySnapshot as Le}from"./human-coordination.js";const Pe=3,De=5,Ke="wechat_login_required",$e=5500;class S 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 un(e){return await Ue(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>ze({...e,helper:n}))}async function ze(e){const t=e.foregroundMode??"required";let n=await qe(e,{foregroundMode:t});if(t!=="background"&&(n=await ue(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=T({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!F(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(V({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),Z(e)?Q({...e,window:n,capture:s,ocr:a}):L({...e,window:n,capture:s,ocr:a})}const r=await D({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!r.opened){const s=await X({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await ue(e.helper,s,e.traceId);const a=await D({helper:e.helper,window:n,binding:e.binding,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(V({capture:i,ocr:o,binding:e.binding})){n=await St(e.helper,e.traceId);const s=await D({helper:e.helper,window:n,binding:e.binding,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}),V({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),Z(e)?Q({...e,window:n,capture:i,ocr:o}):L({...e,window:n,capture:i,ocr:o})}async function qe(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await It(e.helper,e.traceId,{foreground:n});return n==="background"?r:X({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}async function X(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);b(t,"windows.list");const n=(t.result?.windows??[]).filter(z).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=Ge(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 K(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,score:He(c,e.requireTargetConversation===!0)})}catch(a){if(Ve(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.window}function Ve(e){const t=je(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function je(e){if(e instanceof Ie||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 Ge(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 He(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 Ue(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??{},h=u.ok&&d.interrupted?J(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,...h?{reasonCode:h}:{}}),!u.ok)throw new S({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new S({reasonCode:J(d.interruptReason),stage:a,action:c})}const w=Date.now()+Xe(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 S({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});const d=Oe({snapshot:Le(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 h=new S({reasonCode:d.reasonCode,stage:a,action:c});if(!l.settle)throw h;const G=w-Date.now();if(G<=0)throw h;await q(Math.max(50,Math.min(d.waitMs,G)))}},s={request:async(a,c,l)=>{const w=Ye(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 pt(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 S({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(h=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:h instanceof Error?h.message:String(h)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function Ye(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 J(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 Xe(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:$e}function Z(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function L(e){const t=await he(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 re({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,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Q(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await he(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(Je(c))return L(e);throw c}const r=at(n.structuredMessages??n.observedMessages??[]),i=await Ze({...e,messages:r,screenshot:t}),o=await re({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=pe(o,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Ne({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function Je(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function Ze(e){const t=Qe(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await tt({...e,seedBlocks:t}):t.filter(s=>f(s.vectorBase64)||f(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Te(e.workDir||process.cwd(),e.runtime.runtimeId),i=Be(r,e.runtime.runtimeId),o=We({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ee(r,i),{messages:it(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Qe(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}`,s=f(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 et(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 rt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function tt(e){try{return await et(e)}catch(t){if(nt(t))throw t;return[]}}function nt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function rt(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:ot(r,i)}:r})}function ot(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=f(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(l=>l.blockId===a);return c?ee(c,o):te(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(ee(o));return r}function ee(e,t){return{...t?te(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 te(e){const{vectorBase64:t,...n}=e;return n}function at(e){return e.map((t,n)=>st(t,n)).filter(t=>t!=null)}function st(e,t){if(!m(e))return null;const n=Re(e.kind),r=lt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=f(e.stableMessageKey)||ct({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}:{},...C(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...C(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...C(e,"bbox")?{bbox:e.bbox}:{},...C(e,"mediaMetadata")?{mediaMetadata:dt(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 ct(e){const t=m(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 dt(e){if(!m(e))return e;const t={...e};return C(t,"availability")&&(t.availability=H(t.availability)),m(t.attachment)&&(t.attachment=ne(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>m(n)?ne(n):n)),t}function ne(e){const t={...e};return C(t,"availability")&&(t.availability=H(t.availability)),t}function lt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function re(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>gt(o,e.screenshot,e.window,mt(e.messages,s))).filter((o,s)=>ut(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Me(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Se({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:yt(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function ut(e,t,n){const r=oe(n);if(!r)return!0;const i=wt(r),o=e.map((s,a)=>oe(bt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===ft(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(ie))return!0;return!1}function wt(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 ft(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(ie(e[n]))return ht(e[n],t)?n:-1;return-1}function ie(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=m(e.mediaMetadata)?e.mediaMetadata:{},r=m(n.attachment)?n.attachment:{};return ae(t,n,r)}function ht(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=m(e.mediaMetadata)?e.mediaMetadata:{},i=m(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 bt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
2
+ `)}function oe(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function mt(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(`
3
+ `)||null}function gt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=m(e.mediaMetadata)?e.mediaMetadata:{},s=m(o.attachment)?o.attachment:{},a=f(s.availability)||f(o.availability),c=f(s.localPath)||f(o.localPath);if(a==="edge-local"&&c||!ae(i,o,s))return null;const l=W(e.bbox)??W(o.bbox)??W(o.downloadActionBbox),w=W(o.downloadActionBbox),u=se(l,t,n),d=vt(l,t,n),h=se(w,t,n);return{messageKey:e.stableMessageKey,kind:f(s.type)||f(s.kind)||f(o.messageType)||i,fileName:f(s.name)||f(s.fileName)||f(o.fileName)||null,mimeType:f(s.mimeType)||f(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:xt(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...h?{downloadActionBbox:h}:{}}}function yt(e,t,n,r,i){const o=m(e)?{...e}:{};return{...o,availability:m(t)?t.availability:o.availability,mediaStatus:m(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function xt(e,t){const n=f(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";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 ae(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 W(e){if(!m(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 se(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=B(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 vt(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 m(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function C(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 wn(e,t){await de(e,t)}async function pt(e,t){await ce(e,t)}async function ce(e,t){const n=await e.request("permissions.check",{},t);b(n,"permissions.check");const r=n.result??{},i=await Ct(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 de(e,t,n={}){const r=await ce(e,t);if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(Mt(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&&String(r.platform??"").toLowerCase()!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function Ct(e,t,n){const r=_t(t);if(!r||process.platform!=="darwin")return r;const i=kt(r);try{await e.request(i,{},n)}catch{}return r}function _t(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function kt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function It(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await de(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);b(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return l}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);b(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}function Mt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function le(e){const t=e.filter(z),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function fn(e,t){let n=await e.request("windows.list",{},t);b(n,"windows.list");let r=n.result?.windows??[],i=le(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return b(o,"windows.focus"),await we(e,i,t)}async function ue(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 b(r,"windows.focus"),t}async function St(e,t){const n=await e.request("windows.list",{},t);b(n,"windows.list");const r=n.result?.windows??[],i=le(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return b(o,"windows.focus"),await we(e,i,t)}async function we(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(z);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function P(e){const{capture:t,ocr:n}=e.observation??await k(e),r=T({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=Fe({visibleItems:Lt(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=Qt(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 b(s,"mouse.click"),{opened:!0,reason:i.reason}}async function D(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Pe;r+=1){if(t=await Nt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await I(e,"open-retry",`retry:${r}`)}const n=await fe(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Nt(e){const t=await k(e),n=T({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(F(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await P({...e,observation:t});if(u.opened){if(await I(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const h=await fe(e);if(h.opened)return h}}const i=At(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??R(e,"search-input-settle","search-input"),...i?{searchPoint:i}:{}},e.traceId);b(o,"wechat.searchConversation");const s=await Tt(e);if(s.opened){await I(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await N({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 Ot(e.helper,e.traceId),await I(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await N({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??R(e,"search-input-settle","search-input-retry-after-return"),...i?{searchPoint:i}:{}},e.traceId);b(d,"wechat.searchConversation"),a=void 0}const l=await P({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await I(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function fe(e){const t=await P(e);if(!t.opened)return t;await I(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await N({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function hn(e){const t=e.bounds,n=Number(t?.x),r=Number(t?.y),i=Number(t?.width),o=Number(t?.height);if(![n,r,i,o].every(Number.isFinite)||i<=0||o<=0)return null;const s=Math.max(520,Math.min(i-160,i*.68)),a=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+s),y:Math.round(r+o-a),coordinateSpace:"screen"}}async function Tt(e){let t;const n=e.searchDelayMs===0?0:R(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?De:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await q(n);const o=await Bt(e);t=o;const{capture:s,ocr:a}=o,c=T({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=Kt(a,s,e.binding.conversationDisplayName);if(!l){if($t(a)||zt(a,e.binding.conversationDisplayName))break;continue}const w=B(l.bbox,s,Et(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 b(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Bt(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 be(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&b(t,"screen.capture")}return k(e)}function Et(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Wt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await U("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Wt(e){const t=Rt(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 Rt(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 B(a?.bbox,t,n)}function At(e,t){const{capture:n,ocr:r}=t,i=(r.blocks??[]).find(o=>{const s=String(o.text||"").trim().toLowerCase(),a=o.bbox;if(!a||!/^(搜索|search)$/.test(s))return!1;const c=Number(a.x),l=Number(a.y);return Number.isFinite(c)&&Number.isFinite(l)&&c<n.width*.22&&l<n.height*.12});return B(i?.bbox,n,e.window)}async function K(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);b(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 he(e){return e.capture.dataBase64?e.capture:K(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function $(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"&&b(i,"windows.captureAndOcr");const o=await K(e,t,n,r),s=await be(e,o,n);return{capture:o,ocr:s}}async function be(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return b(r,"ocr.recognize"),r.result??{}}function b(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function z(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function q(e){return new Promise(t=>setTimeout(t,e))}function R(e,t,n){return e.searchDelayMs===0?0:Ae(t,Ft(e,n))}async function I(e,t,n){const r=R(e,t,n);r>0&&await q(r)}function Ft(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function Ot(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);b(n,"keyboard.shortcut")}async function N(e){if(!e.window.bounds)return;const t=e.observation,n=en(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);b(r,"mouse.click"),await U("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=T({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:F(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function T(e){const n=(e.ocr.blocks??[]).map(r=>Vt(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ke:null}function Lt(e,t,n=!0){const r=n?Pt(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function Pt(e,t){const n=e.map(i=>me(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=>!ge(i.text)),r=new Set;return n.filter(i=>!Dt(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=E(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function me(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 Dt(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 ge(e){const t=v(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||A(t)||qt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Kt(e,t,n){const r=(e.blocks??[]).map(a=>me(a)).filter(a=>!!a),i=r.filter(a=>A(v(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=>v(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=>!A(v(d.text))).filter(d=>!xe(d,r)).filter(d=>j(d.text,n)).sort((d,h)=>d.y-h.y||d.x-h.x)[0];if(u)return{bbox:ye(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=>!ge(a.text)).filter(a=>!xe(a,r)).filter(a=>j(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:ye(s,t),reason:"search-result-title"}:null}function ye(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 A(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function $t(e){return(e.blocks??[]).some(t=>A(v(t.text)))}function zt(e,t){return(e.visibleConversationFingerprints??[]).some(n=>j(n.title,t))}function v(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function xe(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 qt(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 Vt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function F(e,t,n){const r=y(n)?E(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?E(i.text):O(i.text);if(!o||!Ce(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 V(e){if(F(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(`
4
+ `),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(`
5
+ `);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)&&jt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?E(a.title):O(a.title),l=y(e.binding.conversationDisplayName)?E(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&l&&Ce(c,l))}),!1)}function jt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
6
+ `);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 ve(e){const t=e.anchors.map(o=>({...o,normalized:x(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=Gt(e.ocr,e.screenshot).sort((o,s)=>x(s.text).length-x(o.text).length);for(const o of i){const s=x(o.text);if(!s)continue;const a=t.find(l=>Ht(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 Gt(e,t){const n=(e.blocks??[]).filter(i=>Zt(i,t)&&x(i.text)).sort((i,o)=>p(i.bbox,"y")-p(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(!Ut(o[o.length-1],a,t))break;o.push(a),r.push({text:Yt(o.map(c=>String(c.text||"").trim())),bbox:Jt(o.map(c=>c.bbox))})}}return r}function Ht(e,t){if(!e||!t)return!1;if(Y(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function Ut(e,t,n){const r=p(e.bbox,"x"),i=p(e.bbox,"y"),o=p(e.bbox,"height"),s=p(t.bbox,"x"),a=p(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 Yt(e){return e.reduce((t,n)=>t?n?`${t}${Xt(t,n)?" ":""}${n}`:t:n,"")}function Xt(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Jt(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 p(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=x(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=x(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&Y(a,i)>=.9})||n.push(r)}return n}function Zt(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 Qt(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),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 B(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),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 en(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 B(r?.bbox,t,n)}function Ce(e,t){return y(t)?ke(e,t):e===t||e.includes(t)||t.includes(e)}function j(e,t){if(y(t))return ke(e,t);const n=_e(v(e)),r=_e(v(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function _e(e){return e.toLowerCase().replace(/[l1]/g,"i")}function O(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function E(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 ke(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{S as WeChatChannelUserActivityAbort,$ as captureAndRecognizeWeChatWindow,K as captureWeChatWindow,pt as ensureHelperPermissionPreflight,wn as ensureHelperPreflight,It as ensureWeChatWindowReady,hn as fallbackWeChatMessageInputPointForWindow,St as focusExistingWeChatWindow,ue as focusKnownWeChatWindow,fn as focusWeChatWindow,un as observeWeChatChannelBindingViaHelper,D as openConversationBySearch,P as openConversationInVisibleList,be as recognizeWeChatScreenshot,qe as selectWeChatWindowForBinding};
@@ -1 +1 @@
1
- import d from"node:path";import u from"node:fs";import{createWeChatChannelApiClient as p}from"./client.js";import{WeChatChannelHelperClient as m}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as k,WECHAT_CHANNEL_HELPER_VERSION as W}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as h}from"./helper-protocol.js";import{captureWeChatWindow as S,fallbackWeChatMessageInputPointForWindow as I,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as P,openConversationBySearch as O,recognizeWeChatScreenshot as A,selectWeChatWindowForBinding as g}from"./observer.js";import{WeChatChannelScheduler as F}from"./scheduler.js";import{loadWeChatChannelLedger as D}from"./ledger.js";import{buildWeChatConversationFingerprint as M,matchWeChatConversationFingerprints as T}from"./fingerprint.js";import{WeChatChannelOutboundSender as x}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as L,nextWeChatChannelActivityRetryAt as _,normalizeWeChatChannelActivitySnapshot as q}from"./human-coordination.js";class H{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??p();this.helper=e.helper??E(e),this.scheduler=new F({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(i,a)=>{if(e.runtime.foregroundPolicy==="work")return{ok:!0};const o=await this.helper.request("activity.snapshot",{});if(!o.ok)return{ok:!1,reasonCode:"user_activity_unknown",nextAttemptAt:new Date(a.getTime()+5e3)};const t=L({snapshot:q(o.result),stage:"observe"});return t.ok?{ok:!0}:{ok:!1,reasonCode:t.reasonCode,nextAttemptAt:_(a,t.waitMs)}}},shouldSkipObserve:(i,a)=>this.shouldSkipObserveForUnchangedFingerprint(i,a.foregroundMode),outboundSender:new x({helper:this.helper,platform:e.runtime.policy.platform,activityGatePolicy:e.runtime.foregroundPolicy==="work"?{mouseMovedThresholdMs:0,mouseClickThresholdMs:0,scrollWheelThresholdMs:0,keyDownThresholdMs:0}:void 0,takeoverCheck:e.runtime.foregroundPolicy!=="work",openConversation:async i=>{const a={bindingId:`outbound:${i}`,sessionId:"outbound",conversationDisplayName:i,enabled:!0,allowReply:!0,downloadMedia:!0},o=await g({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir}),t=await f(this.helper,o);return{...await O({helper:this.helper,window:t,settleToBottom:!1,binding:a}),windowId:t.windowId,inputPoint:I(t)}}}),observeBinding:(i,a)=>P({runtime:e.runtime,binding:i,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:R(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,i.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{N({statePath:C(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:i,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(e){return await this.ensureHelperStarted(),this.scheduler.tick(e)}async stop(){this.scheduler.stop(),this.helperStarted&&this.helper.stop&&await this.helper.stop(),this.helperStarted=!1}async ensureHelperStarted(){this.helperStarted||(this.helper.start&&await this.helper.start(),this.helperStarted=!0)}async shouldSkipObserveForUnchangedFingerprint(e,n="required"){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const i=C(this.options.workDir,this.options.runtime.runtimeId),a=c(i,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=this.options.api??p(),t=await g({runtime:this.options.runtime,binding:e,helper:this.helper,api:o,workDir:this.options.workDir},{foregroundMode:n}),s=n==="background"?t:await f(this.helper,t),v=await S(this.helper,s.windowId,void 0,s.bounds),y=await A(this.helper,v),l=b({binding:e,ocr:y,prior:a});return!l?.fingerprint||l.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:l.fingerprint}}catch{return{skip:!1}}}}function ne(r){return new H(r)}function R(r,e,n,i=3){return(D(r,e).bindings[n]?.recent??[]).slice(-i).map(t=>({stableMessageKey:t.stableMessageKey,senderRole:t.senderRole,kind:t.kind,anchorText:t.anchorText??t.normalizedText??t.textExcerpt??null,anchorMetadata:t.anchorMetadata??null}))}function E(r){if(r.helperClientOptions)return new m({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??w(r.runtime),guardUnsafeWindowsCommands:r.helperClientOptions.guardUnsafeWindowsCommands??r.runtime.policy.platform==="win32",skipUserActivityGuard:r.helperClientOptions.skipUserActivityGuard??r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.helperClientOptions.cleanupWindowsOverlays??r.runtime.policy.platform==="win32",requestLogger:r.helperClientOptions.requestLogger??r.helperRequestLogger});const e=k();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new m({helperPath:e.helperPath,expectedHelperVersion:e.version||W,requiredCapabilities:w(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32",requestLogger:r.helperRequestLogger})}function w(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?h("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?h("download"):h("observe")}function U(r,e){return d.join(r,"wechat-channel",`${e}.ledger.json`)}function C(r,e){return d.join(r,"wechat-channel",`${G(e)}.conversation-fingerprints.json`)}function c(r,e){try{const n=JSON.parse(u.readFileSync(r,"utf8"));if(n?.version===1&&n.runtimeId===e&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:e,bindings:{}}}function B(r,e){u.mkdirSync(d.dirname(r),{recursive:!0}),u.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=b({binding:r.binding,ocr:r.ocr,prior:c(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=c(r.statePath,r.runtimeId);n.bindings[r.binding.bindingId]={bindingId:r.binding.bindingId,fingerprint:e.fingerprint,title:e.item.title??null,preview:e.item.preview??null,timeText:e.item.timeText??null,unreadText:e.item.unreadText??null,observedAt:new Date().toISOString()},B(r.statePath,n)}function b(r){const e=r.ocr.visibleConversationFingerprints??[],n=T({visibleItems:e,targets:[{bindingId:r.binding.bindingId,conversationDisplayName:r.binding.conversationDisplayName,lastConversationFingerprint:r.prior?.fingerprint,lastPreview:r.prior?.preview??void 0}]})[0];if(!n)return null;const i=n.item.fingerprint||M(n.item);return i?{item:n.item,fingerprint:i}:null}function G(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{H as WeChatChannelProductRunner,R as buildLocalLedgerTailAnchors,ne as createWeChatChannelProductRunner};
1
+ import d from"node:path";import u from"node:fs";import{createWeChatChannelApiClient as p}from"./client.js";import{WeChatChannelHelperClient as m}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as k,WECHAT_CHANNEL_HELPER_VERSION as W}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as h}from"./helper-protocol.js";import{captureWeChatWindow as S,fallbackWeChatMessageInputPointForWindow as I,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as P,openConversationBySearch as O,recognizeWeChatScreenshot as A,selectWeChatWindowForBinding as g}from"./observer.js";import{WeChatChannelScheduler as F}from"./scheduler.js";import{loadWeChatChannelLedger as D}from"./ledger.js";import{buildWeChatConversationFingerprint as M,matchWeChatConversationFingerprints as T}from"./fingerprint.js";import{WeChatChannelOutboundSender as x}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as L,nextWeChatChannelActivityRetryAt as _,normalizeWeChatChannelActivitySnapshot as q}from"./human-coordination.js";class H{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??p();this.helper=e.helper??E(e),this.scheduler=new F({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(i,a)=>{if(e.runtime.foregroundPolicy==="work")return{ok:!0};const o=await this.helper.request("activity.snapshot",{});if(!o.ok)return{ok:!1,reasonCode:"user_activity_unknown",nextAttemptAt:new Date(a.getTime()+5e3)};const t=L({snapshot:q(o.result),stage:"observe"});return t.ok?{ok:!0}:{ok:!1,reasonCode:t.reasonCode,nextAttemptAt:_(a,t.waitMs)}}},shouldSkipObserve:(i,a)=>this.shouldSkipObserveForUnchangedFingerprint(i,a.foregroundMode),outboundSender:new x({helper:this.helper,platform:e.runtime.policy.platform,activityGatePolicy:e.runtime.foregroundPolicy==="work"?{mouseMovedThresholdMs:0,mouseClickThresholdMs:0,scrollWheelThresholdMs:0,keyDownThresholdMs:0}:void 0,takeoverCheck:e.runtime.foregroundPolicy!=="work",openConversation:async i=>{const a={bindingId:`outbound:${i}`,sessionId:"outbound",conversationDisplayName:i,enabled:!0,allowReply:!0,downloadMedia:!0},o=await g({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir}),t=await f(this.helper,o);return{...await O({helper:this.helper,window:t,settleToBottom:!1,binding:a}),windowId:t.windowId,inputPoint:I(t)}}}),observeBinding:(i,a)=>P({runtime:e.runtime,binding:i,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:R(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,i.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{N({statePath:C(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:i,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(e){return await this.ensureHelperStarted(),this.scheduler.tick(e)}async stop(){await this.scheduler.stop(),this.helperStarted&&this.helper.stop&&await this.helper.stop(),this.helperStarted=!1}async ensureHelperStarted(){this.helperStarted||(this.helper.start&&await this.helper.start(),this.helperStarted=!0)}async shouldSkipObserveForUnchangedFingerprint(e,n="required"){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const i=C(this.options.workDir,this.options.runtime.runtimeId),a=c(i,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=this.options.api??p(),t=await g({runtime:this.options.runtime,binding:e,helper:this.helper,api:o,workDir:this.options.workDir},{foregroundMode:n}),s=n==="background"?t:await f(this.helper,t),v=await S(this.helper,s.windowId,void 0,s.bounds),y=await A(this.helper,v),l=b({binding:e,ocr:y,prior:a});return!l?.fingerprint||l.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:l.fingerprint}}catch{return{skip:!1}}}}function ne(r){return new H(r)}function R(r,e,n,i=3){return(D(r,e).bindings[n]?.recent??[]).slice(-i).map(t=>({stableMessageKey:t.stableMessageKey,senderRole:t.senderRole,kind:t.kind,anchorText:t.anchorText??t.normalizedText??t.textExcerpt??null,anchorMetadata:t.anchorMetadata??null}))}function E(r){if(r.helperClientOptions)return new m({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??w(r.runtime),guardUnsafeWindowsCommands:r.helperClientOptions.guardUnsafeWindowsCommands??r.runtime.policy.platform==="win32",skipUserActivityGuard:r.helperClientOptions.skipUserActivityGuard??r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.helperClientOptions.cleanupWindowsOverlays??r.runtime.policy.platform==="win32",requestLogger:r.helperClientOptions.requestLogger??r.helperRequestLogger});const e=k();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new m({helperPath:e.helperPath,expectedHelperVersion:e.version||W,requiredCapabilities:w(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32",requestLogger:r.helperRequestLogger})}function w(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?h("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?h("download"):h("observe")}function U(r,e){return d.join(r,"wechat-channel",`${e}.ledger.json`)}function C(r,e){return d.join(r,"wechat-channel",`${G(e)}.conversation-fingerprints.json`)}function c(r,e){try{const n=JSON.parse(u.readFileSync(r,"utf8"));if(n?.version===1&&n.runtimeId===e&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:e,bindings:{}}}function B(r,e){u.mkdirSync(d.dirname(r),{recursive:!0}),u.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=b({binding:r.binding,ocr:r.ocr,prior:c(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=c(r.statePath,r.runtimeId);n.bindings[r.binding.bindingId]={bindingId:r.binding.bindingId,fingerprint:e.fingerprint,title:e.item.title??null,preview:e.item.preview??null,timeText:e.item.timeText??null,unreadText:e.item.unreadText??null,observedAt:new Date().toISOString()},B(r.statePath,n)}function b(r){const e=r.ocr.visibleConversationFingerprints??[],n=T({visibleItems:e,targets:[{bindingId:r.binding.bindingId,conversationDisplayName:r.binding.conversationDisplayName,lastConversationFingerprint:r.prior?.fingerprint,lastPreview:r.prior?.preview??void 0}]})[0];if(!n)return null;const i=n.item.fingerprint||M(n.item);return i?{item:n.item,fingerprint:i}:null}function G(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{H as WeChatChannelProductRunner,R as buildLocalLedgerTailAnchors,ne as createWeChatChannelProductRunner};
@@ -26,6 +26,7 @@ export type WeChatChannelBindingConfig = {
26
26
  enabled: boolean;
27
27
  allowReply: boolean;
28
28
  downloadMedia: boolean;
29
+ selfTriggerMarker?: string | null;
29
30
  };
30
31
  export type WeChatChannelRuntimeConfig = {
31
32
  runtimeId: string;
@@ -1 +1 @@
1
- const a="wechat_channel",A=1,L=20,C=6e4,u=3e4,c=3e5,s="work";function N(n,e){return{kind:a,runtimeVersion:1,platform:_(e),pollIntervalMs:l(n),minPollIntervalMs:3e4,maxPollIntervalMs:3e5,recentMessageWindow:20,requiresServerDecision:!0,requiresPlatformHelper:!0,productRuntimeUsesLabFacade:!1}}function l(n){return Number.isFinite(n)?Math.min(3e5,Math.max(3e4,Number(n))):6e4}function I(n){const e=i(n.runtimeId,"runtimeId"),t=i(n.machineId,"machineId"),r=l(n.pollIntervalMs),o=_(n.platform);return{kind:a,runtimeId:e,machineId:t,foregroundPolicy:E(n.foregroundPolicy),policy:N(r,o),bindings:d(n.bindings??[])}}function E(n){return n==="polite"?"polite":s}function _(n){return n==="win32"?"win32":n==="darwin"?"darwin":process.platform==="win32"?"win32":"darwin"}function d(n){const e=new Set,t=[];for(const r of n){const o=i(r.bindingId,"bindingId");e.has(o)||(e.add(o),t.push({bindingId:o,sessionId:i(r.sessionId,"sessionId"),conversationDisplayName:i(r.conversationDisplayName,"conversationDisplayName"),enabled:r.enabled!==!1,allowReply:r.allowReply!==!1,downloadMedia:r.downloadMedia!==!1}))}return t}function i(n,e){const t=String(n||"").trim();if(!t)throw new Error(`WeChat channel ${e} is required`);return t}export{s as WECHAT_CHANNEL_DEFAULT_FOREGROUND_POLICY,C as WECHAT_CHANNEL_DEFAULT_POLL_INTERVAL_MS,c as WECHAT_CHANNEL_MAX_POLL_INTERVAL_MS,u as WECHAT_CHANNEL_MIN_POLL_INTERVAL_MS,L as WECHAT_CHANNEL_RECENT_MESSAGE_WINDOW,a as WECHAT_CHANNEL_RUNTIME_KIND,A as WECHAT_CHANNEL_RUNTIME_VERSION,I as createWeChatChannelRuntime,N as defaultWeChatChannelRuntimePolicy,E as normalizeWeChatChannelForegroundPolicy,l as normalizeWeChatChannelPollIntervalMs};
1
+ const a="wechat_channel",u=1,L=20,C=6e4,c=3e4,I=3e5,_="work";function N(n,e){return{kind:a,runtimeVersion:1,platform:s(e),pollIntervalMs:l(n),minPollIntervalMs:3e4,maxPollIntervalMs:3e5,recentMessageWindow:20,requiresServerDecision:!0,requiresPlatformHelper:!0,productRuntimeUsesLabFacade:!1}}function l(n){return Number.isFinite(n)?Math.min(3e5,Math.max(3e4,Number(n))):6e4}function m(n){const e=o(n.runtimeId,"runtimeId"),t=o(n.machineId,"machineId"),r=l(n.pollIntervalMs),i=s(n.platform);return{kind:a,runtimeId:e,machineId:t,foregroundPolicy:E(n.foregroundPolicy),policy:N(r,i),bindings:d(n.bindings??[])}}function E(n){return n==="polite"?"polite":_}function s(n){return n==="win32"?"win32":n==="darwin"?"darwin":process.platform==="win32"?"win32":"darwin"}function d(n){const e=new Set,t=[];for(const r of n){const i=o(r.bindingId,"bindingId");e.has(i)||(e.add(i),t.push({bindingId:i,sessionId:o(r.sessionId,"sessionId"),conversationDisplayName:o(r.conversationDisplayName,"conversationDisplayName"),enabled:r.enabled!==!1,allowReply:r.allowReply!==!1,downloadMedia:r.downloadMedia!==!1,selfTriggerMarker:A(r.selfTriggerMarker)}))}return t}function o(n,e){const t=String(n||"").trim();if(!t)throw new Error(`WeChat channel ${e} is required`);return t}function A(n){return String(n||"").trim()||null}export{_ as WECHAT_CHANNEL_DEFAULT_FOREGROUND_POLICY,C as WECHAT_CHANNEL_DEFAULT_POLL_INTERVAL_MS,I as WECHAT_CHANNEL_MAX_POLL_INTERVAL_MS,c as WECHAT_CHANNEL_MIN_POLL_INTERVAL_MS,L as WECHAT_CHANNEL_RECENT_MESSAGE_WINDOW,a as WECHAT_CHANNEL_RUNTIME_KIND,u as WECHAT_CHANNEL_RUNTIME_VERSION,m as createWeChatChannelRuntime,N as defaultWeChatChannelRuntimePolicy,E as normalizeWeChatChannelForegroundPolicy,l as normalizeWeChatChannelPollIntervalMs};
@@ -67,7 +67,7 @@ export declare class WeChatChannelScheduler {
67
67
  private runningTick;
68
68
  constructor(options: WeChatChannelSchedulerOptions);
69
69
  start(): Promise<void>;
70
- stop(): void;
70
+ stop(): Promise<void>;
71
71
  tick(options?: WeChatChannelSchedulerTickOptions): Promise<WeChatChannelSchedulerTickResult | void>;
72
72
  noteInterruption(bindingId: string, reason?: string, now?: Date): void;
73
73
  private reportOutboundProjections;
@@ -1 +1 @@
1
- import v from"node:path";import{loadWeChatChannelLedger as _,markWeChatChannelBindingDisabled as y,saveWeChatChannelLedger as R,updateWeChatChannelBindingLedger as O}from"./ledger.js";import{classifyWeChatOutboundEchoes as k,isWeChatOutboundTerminalDeliveryStatus as E,loadWeChatChannelOutboundLedger as W,markWeChatOutboundUserActiveTimeout as A,markWeChatOutboundWaitingUserIdle as x,saveWeChatChannelOutboundLedger as M,shouldFailWeChatOutboundForUserActiveTimeout as T}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as P,isWeChatChannelCooldownActive as U,noteWeChatChannelBillingPause as L,noteWeChatChannelInterruption as B,noteWeChatChannelStableRun as D}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as j}from"./outbound-sender.js";const K=600*1e3;class sn{options;timer=null;running=!1;runningTick=null;constructor(t){this.options=t}async start(){this.timer||(await this.tick(),this.timer=setInterval(()=>{this.tick().catch(()=>{})},this.options.runtime.policy.pollIntervalMs),this.timer.unref())}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}async tick(t={}){for(;this.running;){if(!t.forceObserve)return;const i=this.runningTick;if(!i)return;await i}this.running=!0;let o=()=>{};this.runningTick=new Promise(i=>{o=i});try{const i=this.options.ledgerPath||S(this.options.workDir,this.options.runtime.runtimeId),r=this.options.outboundLedgerPath||V(this.options.workDir,this.options.runtime.runtimeId),d=_(i,this.options.runtime.runtimeId),c=W(r,this.options.runtime.runtimeId),h=[],g=new Date;for(const e of this.options.runtime.bindings.filter(u=>!u.enabled))d.bindings[e.bindingId]?.disabledSince||y({ledger:d,bindingId:e.bindingId});for(const e of this.options.runtime.bindings.filter(u=>u.enabled)){await this.options.api.upsertRuntime(this.options.runtime,e);const u=d.bindings[e.bindingId],p=c.records.filter(s=>s.bindingId===e.bindingId),C=q(this.options.runtime,p);if(u?.cooldown&&(u.cooldown=P(u.cooldown),U(u.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"cooldown",reasonCode:u.cooldown.manualReviewReason||"user_interruption_cooldown"}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:u.revision});continue}const f=this.options.activityGate?await this.options.activityGate.canObserve(e,g):{ok:!0};if(f.ok===!1){const s=X({records:c.records,bindingId:e.bindingId,reasonCode:f.reasonCode,nextAttemptAt:f.nextAttemptAt,now:g,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??K});await this.reportOutboundProjections(e,[...s.waitingRecords,...s.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_user_active",reasonCode:s.failedRecords[0]?.failureCode||f.reasonCode}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:s.failedRecords.length,waitingOutboundCount:s.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:u?.revision??0});continue}if(!t.forceObserve&&this.options.shouldSkipObserve&&N(u,p)){const s=await this.options.shouldSkipObserve(e,{ledgerBinding:u,outboundRecords:p,now:g,foregroundMode:C});if(s.skip){await this.options.api.reportRunStatus(this.options.runtime,e,{status:$(s.reasonCode),reasonCode:s.reasonCode}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:u?.revision??0});continue}}let w;try{w=await this.options.observeBinding(e,{localOutboundEchoAnchors:J(c.records,e.bindingId),foregroundMode:C})}catch(s){if(G(s)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_window_unavailable",reasonCode:I(s,"wechat_window_unavailable"),lastErrorSummary:s instanceof Error?s.message:"wechat_window_unavailable"}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:u?.revision??0});continue}if(z(s)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"wechat_window_unresponsive",reasonCode:"wechat_window_unresponsive",lastErrorSummary:s instanceof Error?s.message:"wechat_window_unresponsive"}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:u?.revision??0});continue}if(F(s)){const m=u??{bindingId:e.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};m.cooldown=L({state:m.cooldown,reason:"insufficient_credits",now:g}),d.bindings[e.bindingId]=m,await this.options.api.reportRunStatus(this.options.runtime,e,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:s instanceof Error?s.message:"insufficient_credits"}),h.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:m.revision});continue}throw s}const b=k({ledger:c,bindingId:e.bindingId,messages:w}),l=O({ledger:d,bindingId:e.bindingId,observedMessages:b.remainingMessages}),a=this.options.outboundSender?await j({ledger:c,bindingId:e.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:g,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=D(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,e,{idempotencyKey:`${e.bindingId}:${l.binding.revision}:${H(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(e,l.newMessages)),await this.reportOutboundProjections(e,[...b.confirmedRecords,...b.manualReviewRecords,...a.sentRecords,...a.staleRecords,...a.failedRecords,...a.waitingRecords,...a.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:Y(l.newMessages.length,b.confirmedRecords,b.manualReviewRecords,a.sentRecords,a.staleRecords,a.failedRecords,a.waitingRecords,a.manualReviewRecords),reasonCode:a.waitingRecords[0]?.deferReason}),h.push({bindingId:e.bindingId,observedCount:w.length,newInboundCount:l.newMessages.length,outboundSentCount:a.sentRecords.length,staleOutboundCount:a.staleRecords.length,failedOutboundCount:a.failedRecords.length,waitingOutboundCount:a.waitingRecords.length,confirmedEchoCount:b.confirmedRecords.length,manualReviewCount:b.manualReviewRecords.length+a.manualReviewRecords.length,revision:l.binding.revision})}return R(i,d),M(r,c),h}finally{o(),this.runningTick=null,this.running=!1}}noteInterruption(t,o,i=new Date){const r=this.options.ledgerPath||S(this.options.workDir,this.options.runtime.runtimeId),d=_(r,this.options.runtime.runtimeId),c=d.bindings[t]??{bindingId:t,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=B({state:c.cooldown,reason:o,now:i}),d.bindings[t]=c,R(r,d)}async reportOutboundProjections(t,o){if(this.options.api.reportOutboundStatus)for(const i of o)await this.options.api.reportOutboundStatus(this.options.runtime,t,{replyId:i.replyId,idempotencyKey:i.idempotencyKey,status:i.sendStatus,replyBaseRevision:i.replyBaseRevision,sentAt:i.sentAt,confirmedAt:i.confirmedAt,failureCode:i.failureCode,lastErrorSummary:i.lastErrorSummary,nextAttemptAt:i.nextAttemptAt,attemptCount:i.attemptCount,commitStage:i.commitStage,copyableContentRef:i.copyableContentRef,copyableText:Q(i)?i.text:void 0,agentVisible:E(i)?!1:void 0})}}function F(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function q(n,t){return n.foregroundPolicy==="work"||t.some(o=>o.sendStatus==="queued"||o.sendStatus==="sending"||o.sendStatus==="sent_unconfirmed")?"required":"background"}function $(n){return n==="conversation_not_visible_background"?"observe_deferred_window_unavailable":"baseline_or_no_change"}function G(n){const t=I(n);return t==="wechat_window_unavailable"||t==="wechat_window_not_found"||t==="conversation_not_visible_background"||t==="wechat_window_obstructed_background"}function I(n,t="unknown_error"){if(n&&typeof n=="object"){const r=n.reasonCode??n.errorCode;if(typeof r=="string"&&r.trim())return r.trim()}return(n instanceof Error?n.message:String(n||"")).match(/^([a-z0-9_./-]+)(?::|\b)/i)?.[1]||t}function z(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function N(n,t){return!n?.baselineEstablished||n.disabledSince||!n.recent.length?!1:!t.some(o=>o.sendStatus==="queued"||o.sendStatus==="sending"||o.sendStatus==="sent_unconfirmed")}function Q(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function S(n,t){return v.join(n,"wechat-channel",`${t}.ledger.json`)}function V(n,t){return v.join(n,"wechat-channel",`${t}.outbound-ledger.json`)}function H(n){return n.at(-1)?.stableMessageKey||"empty"}function Y(n,t,o,i=[],r=[],d=[],c=[],h=[]){return o.length>0||h.length>0?"manual_review":d.length>0?"outbound_send_failed":r.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":i.length>0?"outbound_sent_unconfirmed":t.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function J(n,t){return n.filter(o=>o.bindingId===t&&o.sendStatus==="sent_unconfirmed").map(o=>({replyId:o.replyId,idempotencyKey:o.idempotencyKey,expectedEchoAnchor:o.expectedEchoAnchor||o.textNormalized||o.text||""}))}function X(n){const t=n.records.filter(r=>r.bindingId===n.bindingId&&r.sendStatus==="queued"),o=[],i=[];for(const r of t)x({record:r,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),T({record:r,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(A(r,n.now),o.push(r)):i.push(r);return{waitingRecords:i,failedRecords:o}}export{sn as WeChatChannelScheduler};
1
+ import v from"node:path";import{loadWeChatChannelLedger as _,markWeChatChannelBindingDisabled as k,saveWeChatChannelLedger as R,updateWeChatChannelBindingLedger as A}from"./ledger.js";import{classifyWeChatOutboundEchoes as E,isWeChatOutboundTerminalDeliveryStatus as W,loadWeChatChannelOutboundLedger as T,markWeChatOutboundUserActiveTimeout as M,markWeChatOutboundWaitingUserIdle as x,saveWeChatChannelOutboundLedger as P,shouldFailWeChatOutboundForUserActiveTimeout as U}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as L,isWeChatChannelCooldownActive as B,noteWeChatChannelBillingPause as D,noteWeChatChannelInterruption as K,noteWeChatChannelStableRun as j}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as F}from"./outbound-sender.js";const $=600*1e3,q=8e3;class ln{options;timer=null;running=!1;runningTick=null;constructor(t){this.options=t}async start(){this.timer||(await this.tick(),this.timer=setInterval(()=>{this.tick().catch(()=>{})},this.options.runtime.policy.pollIntervalMs),this.timer.unref())}async stop(){this.timer&&(clearInterval(this.timer),this.timer=null);const t=this.runningTick;t&&await en(t,q)}async tick(t={}){for(;this.running;){if(!t.forceObserve)return;const e=this.runningTick;if(!e)return;await e}this.running=!0;let i=()=>{};this.runningTick=new Promise(e=>{i=e});try{const e=this.options.ledgerPath||y(this.options.workDir,this.options.runtime.runtimeId),r=this.options.outboundLedgerPath||X(this.options.workDir,this.options.runtime.runtimeId),d=_(e,this.options.runtime.runtimeId),c=T(r,this.options.runtime.runtimeId),h=[],b=new Date;for(const o of this.options.runtime.bindings.filter(a=>!a.enabled))d.bindings[o.bindingId]?.disabledSince||k({ledger:d,bindingId:o.bindingId});for(const o of this.options.runtime.bindings.filter(a=>a.enabled)){await this.options.api.upsertRuntime(this.options.runtime,o);const a=d.bindings[o.bindingId],p=c.records.filter(s=>s.bindingId===o.bindingId),C=z(this.options.runtime,p);if(a?.cooldown&&(a.cooldown=L(a.cooldown),B(a.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,o,{status:"cooldown",reasonCode:a.cooldown.manualReviewReason||"user_interruption_cooldown"}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:a.revision});continue}const f=this.options.activityGate?await this.options.activityGate.canObserve(o,b):{ok:!0};if(f.ok===!1){const s=sn({records:c.records,bindingId:o.bindingId,reasonCode:f.reasonCode,nextAttemptAt:f.nextAttemptAt,now:b,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??$});await this.reportOutboundProjections(o,[...s.waitingRecords,...s.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,o,{status:"observe_deferred_user_active",reasonCode:s.failedRecords[0]?.failureCode||f.reasonCode}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:s.failedRecords.length,waitingOutboundCount:s.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:a?.revision??0});continue}if(!t.forceObserve&&this.options.shouldSkipObserve&&V(a,p)){const s=await this.options.shouldSkipObserve(o,{ledgerBinding:a,outboundRecords:p,now:b,foregroundMode:C});if(s.skip){await this.options.api.reportRunStatus(this.options.runtime,o,{status:G(s.reasonCode),reasonCode:s.reasonCode}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:a?.revision??0});continue}}let w;try{w=await this.options.observeBinding(o,{localOutboundEchoAnchors:on(c.records,o.bindingId),foregroundMode:C})}catch(s){if(H(s)){await this.options.api.reportRunStatus(this.options.runtime,o,{status:"observe_deferred_window_unavailable",reasonCode:I(s,"wechat_window_unavailable"),lastErrorSummary:s instanceof Error?s.message:"wechat_window_unavailable"}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:a?.revision??0});continue}if(Q(s)){await this.options.api.reportRunStatus(this.options.runtime,o,{status:"wechat_window_unresponsive",reasonCode:"wechat_window_unresponsive",lastErrorSummary:s instanceof Error?s.message:"wechat_window_unresponsive"}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:a?.revision??0});continue}if(N(s)){const m=a??{bindingId:o.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};m.cooldown=D({state:m.cooldown,reason:"insufficient_credits",now:b}),d.bindings[o.bindingId]=m,await this.options.api.reportRunStatus(this.options.runtime,o,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:s instanceof Error?s.message:"insufficient_credits"}),h.push({bindingId:o.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:m.revision});continue}throw s}const g=E({ledger:c,bindingId:o.bindingId,messages:w}),O=nn(o,g.remainingMessages),l=A({ledger:d,bindingId:o.bindingId,observedMessages:O}),u=this.options.outboundSender?await F({ledger:c,bindingId:o.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:b,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=j(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,o,{idempotencyKey:`${o.bindingId}:${l.binding.revision}:${Z(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(o,l.newMessages)),await this.reportOutboundProjections(o,[...g.confirmedRecords,...g.manualReviewRecords,...u.sentRecords,...u.staleRecords,...u.failedRecords,...u.waitingRecords,...u.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,o,{status:tn(l.newMessages.length,g.confirmedRecords,g.manualReviewRecords,u.sentRecords,u.staleRecords,u.failedRecords,u.waitingRecords,u.manualReviewRecords),reasonCode:u.waitingRecords[0]?.deferReason}),h.push({bindingId:o.bindingId,observedCount:w.length,newInboundCount:l.newMessages.length,outboundSentCount:u.sentRecords.length,staleOutboundCount:u.staleRecords.length,failedOutboundCount:u.failedRecords.length,waitingOutboundCount:u.waitingRecords.length,confirmedEchoCount:g.confirmedRecords.length,manualReviewCount:g.manualReviewRecords.length+u.manualReviewRecords.length,revision:l.binding.revision})}return R(e,d),P(r,c),h}finally{i(),this.runningTick=null,this.running=!1}}noteInterruption(t,i,e=new Date){const r=this.options.ledgerPath||y(this.options.workDir,this.options.runtime.runtimeId),d=_(r,this.options.runtime.runtimeId),c=d.bindings[t]??{bindingId:t,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=K({state:c.cooldown,reason:i,now:e}),d.bindings[t]=c,R(r,d)}async reportOutboundProjections(t,i){if(this.options.api.reportOutboundStatus)for(const e of i)await this.options.api.reportOutboundStatus(this.options.runtime,t,{replyId:e.replyId,idempotencyKey:e.idempotencyKey,status:e.sendStatus,replyBaseRevision:e.replyBaseRevision,sentAt:e.sentAt,confirmedAt:e.confirmedAt,failureCode:e.failureCode,lastErrorSummary:e.lastErrorSummary,nextAttemptAt:e.nextAttemptAt,attemptCount:e.attemptCount,commitStage:e.commitStage,copyableContentRef:e.copyableContentRef,copyableText:Y(e)?e.text:void 0,agentVisible:W(e)?!1:void 0})}}function N(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function z(n,t){return n.foregroundPolicy==="work"||t.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")?"required":"background"}function G(n){return n==="conversation_not_visible_background"?"observe_deferred_window_unavailable":"baseline_or_no_change"}function H(n){const t=I(n);return t==="wechat_window_unavailable"||t==="wechat_window_not_found"||t==="conversation_not_visible_background"||t==="wechat_window_obstructed_background"}function I(n,t="unknown_error"){if(n&&typeof n=="object"){const r=n.reasonCode??n.errorCode;if(typeof r=="string"&&r.trim())return r.trim()}return(n instanceof Error?n.message:String(n||"")).match(/^([a-z0-9_./-]+)(?::|\b)/i)?.[1]||t}function Q(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function V(n,t){return!n?.baselineEstablished||n.disabledSince||!n.recent.length?!1:!t.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")}function Y(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function J(n){return!!n&&typeof n=="object"&&!Array.isArray(n)}function y(n,t){return v.join(n,"wechat-channel",`${t}.ledger.json`)}function X(n,t){return v.join(n,"wechat-channel",`${t}.outbound-ledger.json`)}function Z(n){return n.at(-1)?.stableMessageKey||"empty"}function nn(n,t){const i=S(n.selfTriggerMarker);return i?t.map(e=>e.senderRole!=="self"||!S(e.anchorText||e.normalizedText||e.textExcerpt||"").includes(i)?e:{...e,stableMessageKey:`self-trigger:${e.stableMessageKey}`,senderRole:"contact",anchorMetadata:{...J(e.anchorMetadata)?e.anchorMetadata:{},selfTrigger:!0,originalSenderRole:"self"}}):t}function S(n){return typeof n=="string"?n.replace(/\s+/g," ").trim():""}async function en(n,t){let i=null;await Promise.race([n.then(()=>{},()=>{}),new Promise(e=>{i=setTimeout(e,t),i.unref?.()})]),i&&clearTimeout(i)}function tn(n,t,i,e=[],r=[],d=[],c=[],h=[]){return i.length>0||h.length>0?"manual_review":d.length>0?"outbound_send_failed":r.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":e.length>0?"outbound_sent_unconfirmed":t.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function on(n,t){return n.filter(i=>i.bindingId===t&&i.sendStatus==="sent_unconfirmed").map(i=>({replyId:i.replyId,idempotencyKey:i.idempotencyKey,expectedEchoAnchor:i.expectedEchoAnchor||i.textNormalized||i.text||""}))}function sn(n){const t=n.records.filter(r=>r.bindingId===n.bindingId&&r.sendStatus==="queued"),i=[],e=[];for(const r of t)x({record:r,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),U({record:r,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(M(r,n.now),i.push(r)):e.push(r);return{waitingRecords:e,failedRecords:i}}export{ln as WeChatChannelScheduler};
@@ -14,6 +14,7 @@ export type WeChatRpaProductSourceSecret = {
14
14
  forceForeground?: boolean;
15
15
  downloadAttachments?: boolean;
16
16
  canReply?: boolean;
17
+ selfTriggerMarker?: string;
17
18
  };
18
19
  export type WeChatChannelProductRunnerLike = {
19
20
  start(): Promise<void>;
@@ -1,9 +1,9 @@
1
- import w from"node:crypto";import m from"node:fs";import f from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as O}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as k}from"../wechat-channel/client.js";import{createWeChatChannelRuntime as $}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as D,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as h,saveWeChatChannelOutboundLedger as I}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as S}from"../wechat-channel/ledger.js";const T="wechat-channel";function se(e){const t=$({runtimeId:e.config.id,machineId:J(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:j(e.secret.forceForeground),bindings:H(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),a=u(e.config.workDir,t.runtimeId),r=L(e.config.workDir,t.runtimeId),o=[];m.mkdirSync(f.dirname(r),{recursive:!0}),m.writeFileSync(r,"");const s=e.createRunner??O,c=z(),d=s({runtime:t,workDir:e.config.workDir,api:c,ledgerPath:n,outboundLedgerPath:a,helperRequestLogger:_(r),onInboundMessages:(C,A)=>{const M=t.bindings.find(p=>p.bindingId===C.bindingId)??C;for(const p of A){const y=R(e.config,M,p);if(!y)continue;const N=e.onMessage?.({...y,managerSessionId:e.config.managerSessionId})??y;o.push(N)}}});return{runtime:t,runner:d,emitted:o,ledgerPath:n,outboundLedgerPath:a,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=k(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function j(e){return e===!1?"polite":"work"}function ce(e){const t=e.config.id,n=P(e.config.id,e.conversationName),a=S(x(e.config.workDir,t),t),r=u(e.config.workDir,t),o=h(r,t),s=e.reply.attachment?[U(e.reply.attachment)]:[],c=K({ledger:o,replyId:e.reply.messageId||e.reply.idempotencyKey||l("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||l("wechat-channel-send",`${e.config.id}
1
+ import w from"node:crypto";import m from"node:fs";import f from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as O}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as k}from"../wechat-channel/client.js";import{createWeChatChannelRuntime as $}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as D,enqueueWeChatOutboundReply as T,loadWeChatChannelOutboundLedger as h,saveWeChatChannelOutboundLedger as I}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as S}from"../wechat-channel/ledger.js";const K="wechat-channel";function se(e){const t=$({runtimeId:e.config.id,machineId:J(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:j(e.secret.forceForeground),bindings:H(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),a=u(e.config.workDir,t.runtimeId),r=L(e.config.workDir,t.runtimeId),o=[];m.mkdirSync(f.dirname(r),{recursive:!0}),m.writeFileSync(r,"");const s=e.createRunner??O,c=z(),d=s({runtime:t,workDir:e.config.workDir,api:c,ledgerPath:n,outboundLedgerPath:a,helperRequestLogger:_(r),onInboundMessages:(C,A)=>{const M=t.bindings.find(p=>p.bindingId===C.bindingId)??C;for(const p of A){const y=R(e.config,M,p);if(!y)continue;const N=e.onMessage?.({...y,managerSessionId:e.config.managerSessionId})??y;o.push(N)}}});return{runtime:t,runner:d,emitted:o,ledgerPath:n,outboundLedgerPath:a,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=k(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function j(e){return e===!1?"polite":"work"}function ce(e){const t=e.config.id,n=P(e.config.id,e.conversationName),a=S(x(e.config.workDir,t),t),r=u(e.config.workDir,t),o=h(r,t),s=e.reply.attachment?[U(e.reply.attachment)]:[],c=T({ledger:o,replyId:e.reply.messageId||e.reply.idempotencyKey||l("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||l("wechat-channel-send",`${e.config.id}
2
2
  ${e.reply.conversationId}
3
3
  ${e.reply.text}
4
4
  ${s.join(`
5
- `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:a.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return I(r,o),c}function le(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function de(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ue(e){const t=Q(e.limit,20,1,100),n=S(e.product.ledgerPath,e.product.runtime.runtimeId),a=[];for(const r of e.product.runtime.bindings){const o=n.bindings[r.bindingId]?.recent??[];for(const s of o.slice(-t)){const c=R(e.config,r,s);c&&a.push(c)}}return a}function me(e){const t=u(e.config.workDir,e.config.id),n=h(t,e.config.id),a=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!a&&!r)return{cancelled:!1};const o=n.records.find(c=>a&&c.idempotencyKey===a||r&&c.replyId===r);if(!o)return{cancelled:!1};const s=o.sendStatus;return D(o,e.reason||"user_cancelled"),o.sendStatus!==s?(I(t,n),{cancelled:!0,status:o.sendStatus,record:o}):{cancelled:!1,status:o.sendStatus,record:o}}function fe(e){return e===T}function E(e){return l("wechat-conversation",i(e))}function P(e,t){return l("wechat-channel-binding",`${e}
5
+ `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:a.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return I(r,o),c}function le(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function de(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ue(e){const t=Q(e.limit,20,1,100),n=S(e.product.ledgerPath,e.product.runtime.runtimeId),a=[];for(const r of e.product.runtime.bindings){const o=n.bindings[r.bindingId]?.recent??[];for(const s of o.slice(-t)){const c=R(e.config,r,s);c&&a.push(c)}}return a}function me(e){const t=u(e.config.workDir,e.config.id),n=h(t,e.config.id),a=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!a&&!r)return{cancelled:!1};const o=n.records.find(c=>a&&c.idempotencyKey===a||r&&c.replyId===r);if(!o)return{cancelled:!1};const s=o.sendStatus;return D(o,e.reason||"user_cancelled"),o.sendStatus!==s?(I(t,n),{cancelled:!0,status:o.sendStatus,record:o}):{cancelled:!1,status:o.sendStatus,record:o}}function fe(e){return e===K}function E(e){return l("wechat-conversation",i(e))}function P(e,t){return l("wechat-channel-binding",`${e}
6
6
  ${i(t)}`)}function x(e,t){return f.join(e,"wechat-channel",`${b(t)}.ledger.json`)}function u(e,t){return f.join(e,"wechat-channel",`${b(t)}.outbound-ledger.json`)}function L(e,t){return f.join(e,"wechat-channel",`${b(t)}.helper-trace.jsonl`)}function b(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function _(e){return t=>{m.appendFileSync(e,`${JSON.stringify(F(t))}
7
- `)}}function F(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=g(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=g(e.result)),t}function g(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:w.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const o=e.slice(0,10).map(s=>g(s,t,n+1));return e.length>o.length?{type:"array",length:e.length,items:o}:o}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const a=e,r={};for(const[o,s]of Object.entries(a))r[o]=g(s,o,n+1);return r}function H(e,t){return G(t).map(n=>({bindingId:P(e.id,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1}))}function R(e,t,n){const a=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=q(n);if(!a&&r.length===0)return null;const o=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:E(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:l("wechat-message",`${t.bindingId}
7
+ `)}}function F(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=g(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=g(e.result)),t}function g(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:w.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const o=e.slice(0,10).map(s=>g(s,t,n+1));return e.length>o.length?{type:"array",length:e.length,items:o}:o}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const a=e,r={};for(const[o,s]of Object.entries(a))r[o]=g(s,o,n+1);return r}function H(e,t){return G(t).map(n=>({bindingId:P(e.id,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function R(e,t,n){const a=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=q(n);if(!a&&r.length===0)return null;const o=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:E(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:l("wechat-message",`${t.bindingId}
8
8
  ${n.stableMessageKey}`),sender:{id:l("wechat-sender",`${t.bindingId}
9
- ${o||n.senderRole}`),name:o||null},text:a,attachments:r,receivedAt:Z(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function q(e){const t=e.mediaMetadata;return B(t).map(a=>{const r=a,o=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",c=typeof r.url=="string"?i(r.url):"",d=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...c?{url:c}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(d)&&d>0?{size:d}:{},...o?{localPath:o}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:V(r.availability,{localPath:o,url:c}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...v(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function B(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function V(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function v(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function U(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!m.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function G(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function J(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function Z(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function Q(e,t,n,a){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(a,Math.floor(r))):t}function l(e,t){return`${e}:${w.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{T as WECHAT_RPA_PRODUCT_SOURCE,me as cancelWeChatRpaProductOutboundReply,le as countPendingWeChatRpaProductOutbound,se as createWeChatRpaProductSourceConnection,ce as enqueueWeChatRpaProductOutboundReply,fe as isWeChatRpaProductSource,de as listPendingWeChatRpaProductOutbound,ue as listWeChatRpaProductRecentMessages,R as observedMessageToExternalEvent,b as safeWeChatChannelRuntimePathSegment,P as weChatChannelBindingId,E as weChatChannelConversationId,L as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,u as weChatChannelOutboundLedgerPath};
9
+ ${o||n.senderRole}`),name:o||null},text:a,attachments:r,receivedAt:Z(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function q(e){const t=e.mediaMetadata;return B(t).map(a=>{const r=a,o=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",c=typeof r.url=="string"?i(r.url):"",d=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...c?{url:c}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(d)&&d>0?{size:d}:{},...o?{localPath:o}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:V(r.availability,{localPath:o,url:c}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...v(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function B(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function V(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function v(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function U(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!m.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function G(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function J(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function Z(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function Q(e,t,n,a){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(a,Math.floor(r))):t}function l(e,t){return`${e}:${w.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{K as WECHAT_RPA_PRODUCT_SOURCE,me as cancelWeChatRpaProductOutboundReply,le as countPendingWeChatRpaProductOutbound,se as createWeChatRpaProductSourceConnection,ce as enqueueWeChatRpaProductOutboundReply,fe as isWeChatRpaProductSource,de as listPendingWeChatRpaProductOutbound,ue as listWeChatRpaProductRecentMessages,R as observedMessageToExternalEvent,b as safeWeChatChannelRuntimePathSegment,P as weChatChannelBindingId,E as weChatChannelConversationId,L as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,u as weChatChannelOutboundLedgerPath};
@@ -17,6 +17,8 @@ type WeChatRpaSecret = {
17
17
  idleSeconds?: number;
18
18
  recentLimit?: number;
19
19
  selfNickname?: string;
20
+ selfTriggerMarker?: string;
21
+ deferInitialPoll?: boolean;
20
22
  flowScriptPath?: string;
21
23
  canReply?: boolean;
22
24
  systemPrompt?: string;
@@ -1,8 +1,8 @@
1
- import I from"node:crypto";import f from"node:fs";import m from"node:path";import{fileURLToPath as Z,pathToFileURL as ee}from"node:url";import{ChannelSecretRegistry as te}from"./secret-registry.js";import{resolveShennianPath as re}from"../config/index.js";import{probeMacWeChat as D,observedMessageFromProbe as ne}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as ae,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as se,countPendingWeChatRpaProductOutbound as ie,createWeChatRpaProductSourceConnection as oe,enqueueWeChatRpaProductOutboundReply as ue,isWeChatRpaProductSource as R,listWeChatRpaProductRecentMessages as ce,listPendingWeChatRpaProductOutbound as de,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as le}from"./wechat-channel/client.js";const pe=5e3,A=5,me=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),M=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 fe(t){return P(t,A,1,50)>=me?"scroll-read":"read-latest"}class at{onMessage;options;type="wechat-rpa";secrets=new te;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,T(n,e),O(n,e),this.seedConfiguredConversations(n,r),R(r.source)){if(await this.ensureProductConnection(n,r),n.timer)return;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(()=>{})},n.product?.runtime.policy.pollIntervalMs??B(r.pollIntervalMs)),n.timer.unref();return}n.timer||(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(()=>{})},B(r.pollIntervalMs)),n.timer.unref())}async disconnect(e){const r=this.connections.get(e.id);r&&(r.stopped=!0,r.timer&&clearInterval(r.timer),r.product&&await r.product.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,T(n,e),O(n,e),this.seedConfiguredConversations(n,r),R(r.source)?(await this.ensureProductConnection(n,r),this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config),o=await this.pollProductOnce(n,a,{forceObserve:!0}),i=await this.ensureProductConnection(n,a),u=ce({config:n.config,product:i,limit:a.recentLimit});return ze(u,o)})):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return R(n.source)?se({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,T(a,e),O(a,e),this.seedConfiguredConversations(a,n),R(n.source)){const c=this.resolveConversationName(e,n,r.conversationId);if(!c)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");return await this.ensureProductConnection(a,n),await this.enqueueOperation(a,async()=>{const y=this.readSecret(a.config);ue({config:a.config,secret:y,reply:{...r,text:h},conversationName:c}),a.runtimeState="retrying",g(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${c}`})}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(y=>{a.lastError=y instanceof Error?y.message:String(y)}),{status:"queued"}}if(!Ne(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const o=this.resolveConversationName(e,n,r.conversationId);if(!o)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const i=Me(e,r);if(a.completedPendingReplyKeys.has(i))return{status:"sent"};const u=a.manualReviewReplies.get(i);if(u)return{status:"manual-review",reason:u.reason};const s=r.attachment?await Ke(e.workDir,r.attachment):void 0,l=r.text.trim();if(!l&&!s)throw new Error("Reply text or attachment is required");if(Q(a,n)){const c="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",W(a,{key:i,conversationId:r.conversationId,conversationName:o,text:l,attachmentPath:s,reason:c}),{status:"queued",reason:c}}let d;try{a.runtimeState="syncing",d=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),G(e,n,o,l,s,i)))}catch(c){throw a.lastError=c instanceof Error?c.message:String(c),g(a,{status:k(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),c}L(a,d);const p=V(d,{text:l,attachmentPath:s});if(p.status==="queued"){const c=p.reason;return W(a,{key:i,conversationId:r.conversationId,conversationName:o,text:p.text,attachmentPath:p.attachmentPath,reason:c,skipText:p.skipText}),a.lastError=c,{status:"queued",reason:c}}if(d.interrupted){const c=d.error||"WeChat RPA send was interrupted by user activity";return $(a,d,c),W(a,{key:i,conversationId:r.conversationId,conversationName:o,text:p.text,attachmentPath:s,reason:c,skipText:p.skipText}),{status:"queued",reason:c}}if(!d.ok){const c=d.error||"WeChat RPA send validator failed; manual review required before retry";return Y(a,i,c),{status:"manual-review",reason:c}}return g(a,{status:"sent",runId:a.lastRunId??null,summary:d.rpaTraceSummary||`sent ${o}`}),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&&f.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(!w(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(R(r.source))return Re(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(!w(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{z(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 ye(r,process.platform);const n=await D();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=w(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return R(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+ie({config:e}),wechatRpaPendingReplies:Ee(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(Q(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let o=!1;e.runtimeState="syncing";const i=await this.readObservedMessages(e.config,r,u=>{L(e,u),u.interrupted&&(o=!0,$(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of i){const s=ae(u,{selfNicknames:U(r)});if(!s||!e.deduper.accept(s.messageId))continue;Le(e),e.conversations.set(s.conversationId,s.conversationName),e.lastMessageAt=s.receivedAt;const l={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:s.conversationId,conversationName:s.conversationName,messageId:s.messageId,sender:s.sender,text:s.text,attachments:s.attachments,receivedAt:s.receivedAt,isMentioned:s.isMentioned,replyTarget:"",rawRef:s.rawRef};n.push(this.onMessage?.(l)??l),g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName}${s.sender.name?` from ${s.sender.name}`:""}: ${_(s.text||s.attachments[0]?.name||"attachment")}`})}return o||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!R(r.source))return[];const a=await this.ensureProductConnection(e,r);if((await H(a).catch(()=>null))?.enabled===!1)return e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,[];const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const u=a.emitted.slice(i);e.lastError=null;for(const s of u)e.conversations.set(s.conversationId,s.conversationName||""),e.lastMessageAt=s.receivedAt,g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName||s.conversationId}${s.sender.name?` from ${s.sender.name}`:""}: ${_(s.text||s.attachments[0]?.name||"attachment")}`});return C(e),u}catch(u){throw e.lastError=u instanceof Error?u.message:String(u),e.runtimeState=k(e.lastError)==="blocked"?"interrupted":"idle_waiting",g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),u}}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=oe({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await H(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}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(),v(e);const a=await G(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);L(e,a);const o=V(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(o.status==="queued")return n.text=o.text,n.attachmentPath=o.attachmentPath,n.skipText=o.skipText,n.lastInterruptedReason=o.reason,e.lastError=o.reason,v(e),!0;if(a.interrupted){const i=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=o.text,n.attachmentPath=o.attachmentPath,n.skipText=o.skipText,n.lastInterruptedReason=i,$(e,a,i),v(e),!0}if(!a.ok){const i=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Y(e,n.key,i),v(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),v(e),e.lastError=null,g(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 Fe(r.fixturePath);if(r.source==="macos-flow")return be(e,r,n);if(r.source==="wechat-rpa-lab")return ve(e,r,n);if(r.source==="windows-visual-flow")return xe(e,r,n);const a=await D(),o=ne(a);return o?[o]:[]}seedConfiguredConversations(e,r){for(const n of w(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const o=this.connections.get(e.id)?.conversations.get(n);return o||(w(r).find(i=>x(i)===n||N(i)===n)??null)}}function ge(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:b(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,groups:w(e)})}async function H(t){const e=await le().getRuntimePolicy(),r=e.runtime?.current,n=e.runtime?.scanPolicy??e.usagePolicy?.scanPolicy,a=Number.isFinite(r?.pollIntervalSeconds)?Number(r?.pollIntervalSeconds):n?.defaultPollIntervalSeconds??e.runtime?.defaultPollIntervalSeconds;r?.foregroundPolicy==="polite"&&(t.runtime.foregroundPolicy="polite"),r?.foregroundPolicy==="work"&&(t.runtime.foregroundPolicy="work");const o=r?.status!=="disabled";if(!Number.isFinite(a))return{enabled:o};const i=Number.isFinite(n?.minPollIntervalSeconds)?Number(n?.minPollIntervalSeconds):e.runtime?.minPollIntervalSeconds,u=Number.isFinite(n?.maxPollIntervalSeconds)?Number(n?.maxPollIntervalSeconds):e.runtime?.maxPollIntervalSeconds,s=Number.isFinite(i)?Number(i)*1e3:t.runtime.policy.minPollIntervalMs,l=Number.isFinite(u)?Number(u)*1e3:t.runtime.policy.maxPollIntervalMs,d=Math.min(l,Math.max(s,Number(a)*1e3));return t.runtime.policy.pollIntervalMs=d,t.runtime.policy.minPollIntervalMs=s,t.runtime.policy.maxPollIntervalMs=l,{enabled:o}}function B(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):pe}function w(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 we(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 ye(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 Re(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 ve(t,e,r){const n=await j(t.workDir),a=[];for(const o of w(e)){const i=await Se(n,t,e,o);if(r?.(i),!(!i.ok||i.interrupted))for(const u of i.newMessages??[]){const s=X(o,u,e);s&&a.push(s)}}return a}async function Se(t,e,r,n){const a=fe(r.recentLimit),o={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:q(e,r)},i=await t.runWechatRpaReadLatest(o),u=Array.isArray(i.data?.structuredMessages)?i.data.structuredMessages:[],s=i.validation?.deterministic?.latestMessages??[],l=u.length?u.map((d,p)=>Pe(d,i.runId||"lab-read",p)):s.map((d,p)=>({id:`${i.runId||"lab-read"}:${p}`,text:d,confidence:.8}));return{ok:!!i.ok,groupName:n,interrupted:i.status==="interrupted",reason:i.status,rpaRunId:i.runId,rpaTraceSummary:`${a} ${i.status||(i.ok?"success":"failed")} ${n}`,recentMessages:l,newMessages:l,screenshotPath:i.tracePath,error:J(i)}}function Pe(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:Ie(t)}}function Ie(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 Ae(t,e,r,n,a,o){const i=await j(t.workDir),u=Ce({groupName:r,text:n,attachmentPath:a,dedupeKey:o,requestId:o,policy:"work"}),s=[];for(const h of u){const y=await i.runWechatRpaTask(h);if(s.push(y),!y.ok)break}const l=s.find(h=>h.kind==="send-text"),d=s.find(h=>h.kind==="send-image"||h.kind==="send-video"||h.kind==="send-file"),p=u.length>0&&s.length===u.length&&s.every(h=>h.ok),c=s.some(h=>h.status==="interrupted");return{ok:p,groupName:r,interrupted:c,reason:s.at(-1)?.status,rpaRunId:s.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":s.at(-1)?.status||"failed"} ${r} (${s.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!l?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!d?.ok:!1,postSendScreenshotPath:s.at(-1)?.tracePath,error:p?void 0:J(s.at(-1))}}function Ce(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=ke(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 ke(t){const e=m.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 j(t){const e=z(t);return await import(ee(e).href)}function z(t){const e=m.dirname(Z(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,m.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?m.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",m.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>f.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 m.resolve(n)}function J(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 be(t,e,r){const n=[];for(const a of w(e)){const o=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:b(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:q(t,e)});if(r?.(o),!o.interrupted)for(const i of o.newMessages??[]){const u=X(a,i,e);u&&n.push(u)}}return n}async function xe(t,e,r){const n=w(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 X(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const o=typeof e.senderName=="string"?e.senderName.trim():"",i=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:o||null,text:n,attachments:je(a),observedAt:i,isMentioned:e.isMentioned===!0||we(n,U(r)),rawId:String(e.id||`${t}:${o}:${n}:${i}`)}}async function G(t,e,r,n,a,o){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"?Ae(t,e,r,n,a,o):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:b(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:q(t,e)})}function Ne(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function V(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 Me(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
1
+ import I from"node:crypto";import f from"node:fs";import m from"node:path";import{fileURLToPath as Z,pathToFileURL as ee}from"node:url";import{ChannelSecretRegistry as te}from"./secret-registry.js";import{resolveShennianPath as re}from"../config/index.js";import{probeMacWeChat as D,observedMessageFromProbe as ne}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as ae,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as se,countPendingWeChatRpaProductOutbound as ie,createWeChatRpaProductSourceConnection as oe,enqueueWeChatRpaProductOutboundReply as ue,isWeChatRpaProductSource as R,listWeChatRpaProductRecentMessages as ce,listPendingWeChatRpaProductOutbound as de,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as le}from"./wechat-channel/client.js";const pe=5e3,A=5,me=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),M=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),T=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function fe(t){return P(t,A,1,50)>=me?"scroll-read":"read-latest"}class at{onMessage;options;type="wechat-rpa";secrets=new te;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,W(n,e),O(n,e),this.seedConfiguredConversations(n,r),R(r.source)){if(await this.ensureProductConnection(n,r),n.timer)return;r.deferInitialPoll||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(()=>{})},n.product?.runtime.policy.pollIntervalMs??B(r.pollIntervalMs)),n.timer.unref();return}n.timer||(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(()=>{})},B(r.pollIntervalMs)),n.timer.unref())}async disconnect(e){const r=this.connections.get(e.id);r&&(r.stopped=!0,r.timer&&clearInterval(r.timer),r.product&&await r.product.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,W(n,e),O(n,e),this.seedConfiguredConversations(n,r),R(r.source)?(await this.ensureProductConnection(n,r),this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config),o=await this.pollProductOnce(n,a,{forceObserve:!0}),i=await this.ensureProductConnection(n,a),u=ce({config:n.config,product:i,limit:a.recentLimit});return ze(u,o)})):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return R(n.source)?se({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,W(a,e),O(a,e),this.seedConfiguredConversations(a,n),R(n.source)){const c=this.resolveConversationName(e,n,r.conversationId);if(!c)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");return await this.ensureProductConnection(a,n),await this.enqueueOperation(a,async()=>{const y=this.readSecret(a.config);ue({config:a.config,secret:y,reply:{...r,text:h},conversationName:c}),a.runtimeState="retrying",g(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${c}`})}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(y=>{a.lastError=y instanceof Error?y.message:String(y)}),{status:"queued"}}if(!Ne(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const o=this.resolveConversationName(e,n,r.conversationId);if(!o)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const i=Me(e,r);if(a.completedPendingReplyKeys.has(i))return{status:"sent"};const u=a.manualReviewReplies.get(i);if(u)return{status:"manual-review",reason:u.reason};const s=r.attachment?await Ke(e.workDir,r.attachment):void 0,l=r.text.trim();if(!l&&!s)throw new Error("Reply text or attachment is required");if(Q(a,n)){const c="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",E(a,{key:i,conversationId:r.conversationId,conversationName:o,text:l,attachmentPath:s,reason:c}),{status:"queued",reason:c}}let d;try{a.runtimeState="syncing",d=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),G(e,n,o,l,s,i)))}catch(c){throw a.lastError=c instanceof Error?c.message:String(c),g(a,{status:k(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),c}L(a,d);const p=V(d,{text:l,attachmentPath:s});if(p.status==="queued"){const c=p.reason;return E(a,{key:i,conversationId:r.conversationId,conversationName:o,text:p.text,attachmentPath:p.attachmentPath,reason:c,skipText:p.skipText}),a.lastError=c,{status:"queued",reason:c}}if(d.interrupted){const c=d.error||"WeChat RPA send was interrupted by user activity";return $(a,d,c),E(a,{key:i,conversationId:r.conversationId,conversationName:o,text:p.text,attachmentPath:s,reason:c,skipText:p.skipText}),{status:"queued",reason:c}}if(!d.ok){const c=d.error||"WeChat RPA send validator failed; manual review required before retry";return Y(a,i,c),{status:"manual-review",reason:c}}return g(a,{status:"sent",runId:a.lastRunId??null,summary:d.rpaTraceSummary||`sent ${o}`}),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&&f.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(!w(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(R(r.source))return Re(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(!w(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{z(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 ye(r,process.platform);const n=await D();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=w(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return R(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+ie({config:e}),wechatRpaPendingReplies:Te(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(Q(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let o=!1;e.runtimeState="syncing";const i=await this.readObservedMessages(e.config,r,u=>{L(e,u),u.interrupted&&(o=!0,$(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of i){const s=ae(u,{selfNicknames:U(r)});if(!s||!e.deduper.accept(s.messageId))continue;Le(e),e.conversations.set(s.conversationId,s.conversationName),e.lastMessageAt=s.receivedAt;const l={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:s.conversationId,conversationName:s.conversationName,messageId:s.messageId,sender:s.sender,text:s.text,attachments:s.attachments,receivedAt:s.receivedAt,isMentioned:s.isMentioned,replyTarget:"",rawRef:s.rawRef};n.push(this.onMessage?.(l)??l),g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName}${s.sender.name?` from ${s.sender.name}`:""}: ${_(s.text||s.attachments[0]?.name||"attachment")}`})}return o||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!R(r.source))return[];const a=await this.ensureProductConnection(e,r);if((await H(a).catch(()=>null))?.enabled===!1)return e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,[];const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const u=a.emitted.slice(i);e.lastError=null;for(const s of u)e.conversations.set(s.conversationId,s.conversationName||""),e.lastMessageAt=s.receivedAt,g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName||s.conversationId}${s.sender.name?` from ${s.sender.name}`:""}: ${_(s.text||s.attachments[0]?.name||"attachment")}`});return C(e),u}catch(u){throw e.lastError=u instanceof Error?u.message:String(u),e.runtimeState=k(e.lastError)==="blocked"?"interrupted":"idle_waiting",g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),u}}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=oe({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await H(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}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(),v(e);const a=await G(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);L(e,a);const o=V(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(o.status==="queued")return n.text=o.text,n.attachmentPath=o.attachmentPath,n.skipText=o.skipText,n.lastInterruptedReason=o.reason,e.lastError=o.reason,v(e),!0;if(a.interrupted){const i=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=o.text,n.attachmentPath=o.attachmentPath,n.skipText=o.skipText,n.lastInterruptedReason=i,$(e,a,i),v(e),!0}if(!a.ok){const i=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Y(e,n.key,i),v(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),v(e),e.lastError=null,g(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 Fe(r.fixturePath);if(r.source==="macos-flow")return be(e,r,n);if(r.source==="wechat-rpa-lab")return ve(e,r,n);if(r.source==="windows-visual-flow")return xe(e,r,n);const a=await D(),o=ne(a);return o?[o]:[]}seedConfiguredConversations(e,r){for(const n of w(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const o=this.connections.get(e.id)?.conversations.get(n);return o||(w(r).find(i=>x(i)===n||N(i)===n)??null)}}function ge(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:b(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:w(e)})}async function H(t){const e=await le().getRuntimePolicy(),r=e.runtime?.current,n=e.runtime?.scanPolicy??e.usagePolicy?.scanPolicy,a=Number.isFinite(r?.pollIntervalSeconds)?Number(r?.pollIntervalSeconds):n?.defaultPollIntervalSeconds??e.runtime?.defaultPollIntervalSeconds;r?.foregroundPolicy==="polite"&&(t.runtime.foregroundPolicy="polite"),r?.foregroundPolicy==="work"&&(t.runtime.foregroundPolicy="work");const o=r?.status!=="disabled";if(!Number.isFinite(a))return{enabled:o};const i=Number.isFinite(n?.minPollIntervalSeconds)?Number(n?.minPollIntervalSeconds):e.runtime?.minPollIntervalSeconds,u=Number.isFinite(n?.maxPollIntervalSeconds)?Number(n?.maxPollIntervalSeconds):e.runtime?.maxPollIntervalSeconds,s=Number.isFinite(i)?Number(i)*1e3:t.runtime.policy.minPollIntervalMs,l=Number.isFinite(u)?Number(u)*1e3:t.runtime.policy.maxPollIntervalMs,d=Math.min(l,Math.max(s,Number(a)*1e3));return t.runtime.policy.pollIntervalMs=d,t.runtime.policy.minPollIntervalMs=s,t.runtime.policy.maxPollIntervalMs=l,{enabled:o}}function B(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):pe}function w(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 we(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 ye(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 Re(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 ve(t,e,r){const n=await j(t.workDir),a=[];for(const o of w(e)){const i=await Se(n,t,e,o);if(r?.(i),!(!i.ok||i.interrupted))for(const u of i.newMessages??[]){const s=X(o,u,e);s&&a.push(s)}}return a}async function Se(t,e,r,n){const a=fe(r.recentLimit),o={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:q(e,r)},i=await t.runWechatRpaReadLatest(o),u=Array.isArray(i.data?.structuredMessages)?i.data.structuredMessages:[],s=i.validation?.deterministic?.latestMessages??[],l=u.length?u.map((d,p)=>Pe(d,i.runId||"lab-read",p)):s.map((d,p)=>({id:`${i.runId||"lab-read"}:${p}`,text:d,confidence:.8}));return{ok:!!i.ok,groupName:n,interrupted:i.status==="interrupted",reason:i.status,rpaRunId:i.runId,rpaTraceSummary:`${a} ${i.status||(i.ok?"success":"failed")} ${n}`,recentMessages:l,newMessages:l,screenshotPath:i.tracePath,error:J(i)}}function Pe(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:Ie(t)}}function Ie(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 Ae(t,e,r,n,a,o){const i=await j(t.workDir),u=Ce({groupName:r,text:n,attachmentPath:a,dedupeKey:o,requestId:o,policy:"work"}),s=[];for(const h of u){const y=await i.runWechatRpaTask(h);if(s.push(y),!y.ok)break}const l=s.find(h=>h.kind==="send-text"),d=s.find(h=>h.kind==="send-image"||h.kind==="send-video"||h.kind==="send-file"),p=u.length>0&&s.length===u.length&&s.every(h=>h.ok),c=s.some(h=>h.status==="interrupted");return{ok:p,groupName:r,interrupted:c,reason:s.at(-1)?.status,rpaRunId:s.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":s.at(-1)?.status||"failed"} ${r} (${s.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!l?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!d?.ok:!1,postSendScreenshotPath:s.at(-1)?.tracePath,error:p?void 0:J(s.at(-1))}}function Ce(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=ke(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 ke(t){const e=m.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 j(t){const e=z(t);return await import(ee(e).href)}function z(t){const e=m.dirname(Z(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,m.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?m.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",m.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>f.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 m.resolve(n)}function J(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 be(t,e,r){const n=[];for(const a of w(e)){const o=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:b(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:q(t,e)});if(r?.(o),!o.interrupted)for(const i of o.newMessages??[]){const u=X(a,i,e);u&&n.push(u)}}return n}async function xe(t,e,r){const n=w(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 X(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const o=typeof e.senderName=="string"?e.senderName.trim():"",i=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:o||null,text:n,attachments:je(a),observedAt:i,isMentioned:e.isMentioned===!0||we(n,U(r)),rawId:String(e.id||`${t}:${o}:${n}:${i}`)}}async function G(t,e,r,n,a,o){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"?Ae(t,e,r,n,a,o):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:b(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:q(t,e)})}function Ne(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function V(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 Me(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||""}
5
- ${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Ee(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...de({config:e})].slice(0,100)}function W(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),v(t)}function Y(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,g(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),v(t)}function T(t,e){const r=$e(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=We(r);for(const a of n.pending??[])Te(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])Oe(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function v(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{f.mkdirSync(m.dirname(t.pendingStatePath),{recursive:!0}),f.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function We(t){try{const e=JSON.parse(f.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function Te(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function Oe(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function $e(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return m.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function O(t,e){const r=qe(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=_e(r);t.deduper=new K((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function Le(t){if(t.messageStatePath)try{f.mkdirSync(m.dirname(t.messageStatePath),{recursive:!0}),f.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function _e(t){try{const e=JSON.parse(f.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function qe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return m.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function $(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",De(t)&&(t.interruptionCooldownUntil=Date.now()+E,t.runtimeState="cooldown"),g(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function L(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function g(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:_(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,he)}function k(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function _(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function Q(t,e){if(b(e))return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function b(t){return t.forceForeground!==!1}function De(t){return!Number.isFinite(M)||M<=0||!Number.isFinite(E)||E<=0?!1:t.consecutiveInterruptions>=M}function Fe(t){return!t||!f.existsSync(t)?[]:f.readFileSync(t,"utf-8").split(`
5
+ ${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Te(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...de({config:e})].slice(0,100)}function E(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),v(t)}function Y(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,g(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),v(t)}function W(t,e){const r=$e(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Ee(r);for(const a of n.pending??[])We(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])Oe(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function v(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{f.mkdirSync(m.dirname(t.pendingStatePath),{recursive:!0}),f.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function Ee(t){try{const e=JSON.parse(f.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function We(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function Oe(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function $e(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return m.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function O(t,e){const r=qe(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=_e(r);t.deduper=new K((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function Le(t){if(t.messageStatePath)try{f.mkdirSync(m.dirname(t.messageStatePath),{recursive:!0}),f.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function _e(t){try{const e=JSON.parse(f.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function qe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return m.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function $(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",De(t)&&(t.interruptionCooldownUntil=Date.now()+T,t.runtimeState="cooldown"),g(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function L(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function g(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:_(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,he)}function k(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function _(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function Q(t,e){if(b(e))return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function b(t){return t.forceForeground!==!1}function De(t){return!Number.isFinite(M)||M<=0||!Number.isFinite(T)||T<=0?!1:t.consecutiveInterruptions>=M}function Fe(t){return!t||!f.existsSync(t)?[]:f.readFileSync(t,"utf-8").split(`
6
6
  `).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function Ke(t,e){if(e.localPath){if(!f.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return He(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function He(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return Be(t,e,a)}function Be(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=m.join(t,".uploads","wechat-rpa","outbound");f.mkdirSync(n,{recursive:!0});const a=Ue(e.name||"attachment"),o=I.createHash("sha256").update(r).digest("hex").slice(0,12),i=m.extname(a),u=i?a.slice(0,-i.length):a,s=m.join(n,`${u}-${o}${i}`);return f.existsSync(s)||f.writeFileSync(s,r),s}function Ue(t){return m.basename(t||"attachment").normalize("NFKC").replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g," ").replace(/^[ ._]+|[ ._]+$/g,"")||"attachment"}function q(t,e){if(e.downloadAttachments===!1)return;const r=e.downloadAttachmentsDir?.trim();if(r)return m.resolve(r);const n=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return re("wechat-rpa","attachments","inbound",n)}function je(t){if(!Array.isArray(t))return[];const e=process.env.SHENNIAN_MACHINE_ID?.trim();return t.map(r=>r?.localPath?{...r,availability:r.availability||"edge-local",...r.machineId||!e?{}:{machineId:e}}:r)}function P(t,e,r,n){const a=Number(t);return Number.isFinite(a)?Math.min(n,Math.max(r,a)):e}function ze(...t){const e=new Set,r=[];for(const n of t.flat()){const a=`${n.channelId}
7
7
  ${n.conversationId}
8
8
  ${n.messageId}`;e.has(a)||(e.add(a),r.push(n))}return r}export{at as WeChatRpaChannelAdapter,je as annotateWeChatRpaInboundAttachments,Ke as materializeWeChatRpaOutboundAttachment,Ce as planWeChatRpaLabSendTasks,q as resolveWeChatRpaInboundAttachmentDir,fe as selectWeChatRpaLabReadKind,ke as weChatRpaLabTaskKindForAttachment,Re as weChatRpaProductSourceHealth,ye as windowsVisualFlowHealth};