shennian 0.2.99 → 0.2.101

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 (47) hide show
  1. package/dist/publish-build-manifest.json +77 -27
  2. package/dist/src/channels/runtime.d.ts +5 -1
  3. package/dist/src/channels/runtime.js +3 -3
  4. package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
  5. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  6. package/dist/src/channels/wechat-channel/helper-client.js +3 -3
  7. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  8. package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
  9. package/dist/src/channels/wechat-channel/observer.js +6 -6
  10. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +2 -0
  11. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
  12. package/dist/src/channels/wechat-channel/pacing.d.ts +3 -0
  13. package/dist/src/channels/wechat-channel/pacing.js +1 -0
  14. package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
  15. package/dist/src/channels/wechat-channel/preflight.js +1 -1
  16. package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
  17. package/dist/src/channels/wechat-channel/runner.js +1 -1
  18. package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
  19. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  20. package/dist/src/channels/wechat-rpa.d.ts +5 -1
  21. package/dist/src/channels/wechat-rpa.js +4 -4
  22. package/dist/src/commands/runtime.d.ts +89 -0
  23. package/dist/src/commands/runtime.js +1 -0
  24. package/dist/src/commands/wechat/command.d.ts +2 -0
  25. package/dist/src/commands/wechat/command.js +1 -0
  26. package/dist/src/commands/wechat/direct.d.ts +14 -0
  27. package/dist/src/commands/wechat/direct.js +2 -0
  28. package/dist/src/commands/wechat/doctor.d.ts +2 -0
  29. package/dist/src/commands/wechat/doctor.js +1 -0
  30. package/dist/src/commands/wechat/ipc.d.ts +9 -0
  31. package/dist/src/commands/wechat/ipc.js +1 -0
  32. package/dist/src/commands/wechat/operations.d.ts +3 -0
  33. package/dist/src/commands/wechat/operations.js +4 -0
  34. package/dist/src/commands/wechat/output.d.ts +7 -0
  35. package/dist/src/commands/wechat/output.js +4 -0
  36. package/dist/src/commands/wechat/types.d.ts +141 -0
  37. package/dist/src/commands/wechat/types.js +1 -0
  38. package/dist/src/commands/wechat/utils.d.ts +14 -0
  39. package/dist/src/commands/wechat/utils.js +1 -0
  40. package/dist/src/commands/wechat.d.ts +6 -127
  41. package/dist/src/commands/wechat.js +1 -7
  42. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +5 -0
  43. package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
  44. package/dist/src/index.js +2 -2
  45. package/dist/src/manager/runtime.d.ts +3 -1
  46. package/dist/src/manager/runtime.js +5 -5
  47. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- import{defaultWeChatChannelAttachmentDir as pe,resolveVisibleWeChatChannelMedia as Ce}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Ne}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Ie,loadWeChatChannelVectorStore as Se,saveWeChatChannelVectorStore as Me,upsertWeChatChannelVectorReferences as ke}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as $,normalizeWeChatChannelMessageKind as Te}from"./core/schema.js";import{matchWeChatConversationFingerprints as _e}from"./fingerprint.js";import{normalizeWeChatAnchorText as p,weChatTextSimilarity as V}from"./anchor.js";const Be=3,We=5,Fe=420,q="wechat_login_required",F="wechat_duplicate_instance";async function jt(e){let t=await ot(e.helper,e.traceId);t=await lt(e.helper,t,e.traceId);const n=await ae({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!n.opened)throw new Error(n.reason||"conversation_not_opened");let{capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId});if(be({capture:r,ocr:i,binding:e.binding})){t=await ie(e.helper,e.traceId);const o=await ae({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!o.opened)throw new Error(o.reason||"wechat_window_obstructed");if({capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId}),be({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),Ee(e)?Re({...e,window:t,capture:r,ocr:i}):j({...e,window:t,capture:r,ocr:i})}function Ee(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function j(e){const t=await de(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 G({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 ge(r,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Re(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await de(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(Ae(c))return j(e);throw c}const r=qe(n.structuredMessages??n.observedMessages??[]),i=await Oe({...e,messages:r,screenshot:t}),o=await G({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=ge(o,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Ne({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function Ae(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function Oe(e){const t=Le(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Pe({...e,seedBlocks:t}):t.filter(a=>u(a.vectorBase64)||u(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Ie(e.workDir||process.cwd(),e.runtime.runtimeId),i=Se(r,e.runtime.runtimeId),o=ke({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Me(r,i),{messages:$e(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Le(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=u(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=u(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 ze(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 De(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Pe(e){try{return await ze(e)}catch(t){if(Ke(t))throw t;return[]}}function Ke(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function De(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:u(n.stableMessageKey)||u(i?.stableMessageKey),blockId:u(n.blockId)||u(i?.blockId),blockKind:u(n.blockKind)||u(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>u(n.stableMessageKey)&&u(n.blockId))}function $e(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:Ve(r,i)}:r})}function Ve(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=u(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?Y(c,o):H(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(Y(o));return r}function Y(e,t){return{...t?H(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 H(e){const{vectorBase64:t,...n}=e;return n}function qe(e){return e.map((t,n)=>je(t,n)).filter(t=>t!=null)}function je(e,t){if(!b(e))return null;const n=Te(e.kind),r=Ue(e.senderRole),i=x(e.normalizedText),o=x(e.anchorText)??i??x(e.textExcerpt),a=u(e.stableMessageKey)||Ye({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:r,...x(e.senderName)!==null?{senderName:x(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...x(e.textExcerpt)!==null?{textExcerpt:x(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:He(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...x(e.deliveryStatus)!==null?{deliveryStatus:x(e.deliveryStatus)??void 0}:{},...x(e.observedAt)!==null?{observedAt:x(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function Ye(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||u(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function He(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),b(t.attachment)&&(t.attachment=U(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?U(n):n)),t}function U(e){const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),t}function Ue(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function G(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>tt(o,e.screenshot,e.window,et(e.messages,a))).filter((o,a)=>Ge(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??pe(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ce({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 k({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:nt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Ge(e,t,n){const r=Z(n);if(!r)return!0;const i=Xe(r),o=e.map((a,s)=>Z(Qe(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Ze(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(X))return!0;return!1}function Xe(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 Ze(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(X(e[n]))return Je(e[n],t)?n:-1;return-1}function X(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return J(t,n,r)}function Je(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),a=String(i.mimeType||"").toLowerCase(),s=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function Qe(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
2
- `)}function Z(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function et(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 tt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=u(a.availability)||u(o.availability),c=u(a.localPath)||u(o.localPath);if(s==="edge-local"&&c||!J(i,o,a))return null;const d=E(e.bbox)??E(o.bbox)??E(o.downloadActionBbox),h=E(o.downloadActionBbox),f=Q(d,t,n),l=it(d,t,n),g=Q(h,t,n);return{messageKey:e.stableMessageKey,kind:u(a.type)||u(a.kind)||u(o.messageType)||i,fileName:u(a.name)||u(a.fileName)||u(o.fileName)||null,mimeType:u(a.mimeType)||u(o.mimeType)||null,size:S(a.size)??S(o.size),mediaStatus:rt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...f?{bbox:f}:{},...l?{screenshotBbox:l}:{},...g?{downloadActionBbox:g}:{}}}function nt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function rt(e,t){const n=u(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=u(t.availability)||u(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function J(e,t,n){const r=`${e} ${u(t.messageType)} ${u(n.type)} ${u(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function E(e){if(!b(e))return null;const t=S(e.x),n=S(e.y),r=S(e.width),i=S(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 Q(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 it(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function u(e){return typeof e=="string"?e.trim():""}function x(e){return u(e)||null}function S(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Yt(e,t){await ee(e,t)}async function ee(e,t){let n=await e.request("permissions.check",{},t);w(n,"permissions.check");const r=n.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(at(r))throw new Error(F);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(te([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(F);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32")throw new Error("wechat_window_unavailable");return r}async function ot(e,t){const n=await ee(e,t);if(String(n.platform??"").toLowerCase()==="win32"){const i=await e.request("windows.ensureReady",{activate:!0,allowRecovery:!1,allowLaunch:!1},t);w(i,"windows.ensureReady");const o=i.result;if(!o?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return o}const r=await ie(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.list missing WeChat window data");return r}function te(e,t){const n=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>M(o)-M(i))[0];return n.find(i=>st(i,r))??null}function at(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(v)||(e.unsafeAuxiliaryWindows??[]).some(v)}function L(e){const t=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter(re);if(n.length>1)throw new Error(F);const r=n.slice().sort((i,o)=>M(o)-M(i))[0];if(te(t,r))throw new Error(F)}function ne(e){const t=e.filter(v),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),r=n.filter(re);return(r.length?r:n.length?n:t).slice().sort((o,a)=>M(a)-M(o))[0]??null}function st(e,t){if(!v(e)||t&&dt(e,t)||e.visible===!1||e.minimized===!0)return!1;const n=Number(e.bounds?.width),r=Number(e.bounds?.height);if(!Number.isFinite(n)||!Number.isFinite(r))return!1;const i=n>=360&&n<=720&&r>=420&&r<=840&&r>n,o=n>=420&&n<=680&&r>=260&&r<=460;return!(!i&&!o||t&&ct(e,t))}function ct(e,t){const n=Number(e.rootProcessId),r=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(r)||n!==r)return!1;const i=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(i)||!Number.isFinite(o)?!1:i>o}function re(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function M(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function dt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Ht(e,t){let n=await e.request("windows.list",{},t);w(n,"windows.list");let r=n.result?.windows??[];L(r);let i=ne(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await oe(e,i,t)}async function lt(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 w(r,"windows.focus"),t}async function ie(e,t){const n=await e.request("windows.list",{},t);w(n,"windows.list");const r=n.result?.windows??[];L(r);const i=ne(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await oe(e,i,t)}async function oe(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(v);return L(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function z(e){const{capture:t,ocr:n}=e.observation??await T(e),r=R({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=_e({visibleItems:yt(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=Ot(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 w(a,"mouse.click"),{opened:!0,reason:i.reason}}async function ae(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Be;r+=1){if(t=await ut(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await m(350)}const n=await se(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function ut(e){const t=await T(e),n=R({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(K(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const f=await z({...e,observation:t});if(f.opened){if(await m(900),(await k(e)).ok)return{opened:!0,reason:f.reason};const g=await se(e);if(g.opened)return g}}const i=gt(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(o,"wechat.searchConversation");const a=await ht(e);if(a.opened){if(await m(900),(await k(e)).ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation")}let s=a.observation;const c=y(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await xt(e.helper,e.traceId),await m(900);const f=await k(e);if(f.ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:f.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation"),s=void 0}const d=await z({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!d.opened)return d;await m(900);const h=await k(e);return h.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:d.reason}):{opened:!1,reason:h.reason}}async function se(e){const t=await z(e);if(!t.opened)return t;await m(900);const n=await k(e);return n.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function Ut(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 ht(e){let t;const n=e.searchDelayMs===0?0:Fe,r=y(e.binding.conversationDisplayName)?We:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await m(n);const o=await ft(e);t=o;const{capture:a,ocr:s}=o,c=R({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Ct(s,a,e.binding.conversationDisplayName);if(!d){if(Nt(s)||It(s,e.binding.conversationDisplayName))break;continue}const h=B(d.bbox,a,wt(a,e.window));if(!h)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const f=await e.helper.request("mouse.click",{x:h.x,y:h.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(f,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function ft(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 le(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&w(t,"screen.capture")}return T(e)}function wt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function T(e){let{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await bt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await m(350),{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function bt(e){const t=mt(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 mt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const d=String(c.text||""),h=c.bbox;if(!h)return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&l>t.height*.06&&l<t.height*.22&&f>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const d=String(c.text||"").trim(),h=c.bbox;if(!h||d!=="\xD7")return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&f>t.width*.55&&f<t.width*.9&&Math.abs(l-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return B(s?.bbox,t,n)}function gt(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),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return B(i?.bbox,n,e.window)}async function ce(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);w(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 de(e){return e.capture.dataBase64?e.capture:ce(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function P(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"&&w(i,"windows.captureAndOcr");const o=await ce(e,t,n,r),a=await le(e,o,n);return{capture:o,ocr:a}}async function le(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return w(r,"ocr.recognize"),r.result??{}}function w(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function v(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function m(e){return new Promise(t=>setTimeout(t,e))}async function xt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);w(n,"keyboard.shortcut")}async function _(e){if(!e.window.bounds)return;const t=e.observation,n=Lt(t?.ocr,t?.capture,e.window);if(n){const i=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(i,"mouse.click"),await m(700)}const r=zt(e.window);if(r){const i=await e.helper.request("mouse.click",{x:r.x,y:r.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(i,"mouse.click"),await m(150)}for(let i=0;i<6;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"pagedown",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(260)}for(let i=0;i<2;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"end",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(180)}for(let i=0;i<3;i+=1){const o=await e.helper.request("mouse.scroll",{deltaY:-2400,windowId:e.window.windowId,...r?{point:{...r,coordinateSpace:"screen"}}:{}},e.traceId);w(o,"mouse.scroll"),await m(180)}await m(220)}async function k(e){const{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=R({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:K(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function R(e){const t=(e.ocr.blocks??[]).map(r=>kt(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Mt(e,t)?q:null}function yt(e,t,n=!0){const r=n?vt(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function vt(e,t){const n=e.map(i=>ue(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=>!he(i.text)),r=new Set;return n.filter(i=>!pt(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=W(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function ue(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 pt(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 he(e){const t=C(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||A(t)||St(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Ct(e,t,n){const r=(e.blocks??[]).map(s=>ue(s)).filter(s=>!!s),i=r.filter(s=>A(C(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(l=>C(l.text)===s);if(c<0)continue;const d=i[c],h=i[c+1]?.y??Number.POSITIVE_INFINITY,f=r.filter(l=>l.y>d.y&&l.y<h).filter(l=>l.x<t.width*.7).filter(l=>l.text.length>=2).filter(l=>!A(C(l.text))).filter(l=>!we(l,r)).filter(l=>D(l.text,n)).sort((l,g)=>l.y-g.y||l.x-g.x)[0];if(f)return{bbox:fe(f,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=>!he(s.text)).filter(s=>!we(s,r)).filter(s=>D(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:fe(a,t),reason:"search-result-title"}:null}function fe(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 Nt(e){return(e.blocks??[]).some(t=>A(C(t.text)))}function It(e,t){return(e.visibleConversationFingerprints??[]).some(n=>D(n.title,t))}function C(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function we(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 St(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 Mt(e,t){if(!v(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),r=Number(e.window.bounds?.height||e.capture.height);if(![n,r].every(Number.isFinite)||!(n>=360&&n<=720&&r>=420&&r<=840&&r>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function kt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function K(e,t,n){const r=y(n)?W(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?W(i.text):O(i.text);if(!o||!xe(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 d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,h=s<t.width*.18&&c<t.height*.12;return d||h}):!1}function be(e){if(K(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 d=Array.isArray(c.source)?c.source.some(h=>c.pattern.test(h)):c.pattern.test(c.source);return s+(d?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)&&Tt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?W(s.title):O(s.title),d=y(e.binding.conversationDisplayName)?W(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&d&&xe(c,d))}),!1)}function Tt(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 me(e){const t=e.anchors.map(o=>({...o,normalized:p(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=_t(e.ocr,e.screenshot).sort((o,a)=>p(a.text).length-p(o.text).length);for(const o of i){const a=p(o.text);if(!a)continue;const s=t.find(d=>Bt(d.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 _t(e,t){const n=(e.blocks??[]).filter(i=>At(i,t)&&p(i.text)).sort((i,o)=>N(i.bbox,"y")-N(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(!Wt(o[o.length-1],s,t))break;o.push(s),r.push({text:Ft(o.map(c=>String(c.text||"").trim())),bbox:Rt(o.map(c=>c.bbox))})}}return r}function Bt(e,t){if(!e||!t)return!1;if(V(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function Wt(e,t,n){const r=N(e.bbox,"x"),i=N(e.bbox,"y"),o=N(e.bbox,"height"),a=N(t.bbox,"x"),s=N(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 Ft(e){return e.reduce((t,n)=>t?n?`${t}${Et(t,n)?" ":""}${n}`:t:n,"")}function Et(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Rt(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 N(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function ge(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=p(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=p(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&V(s,i)>=.9})||n.push(r)}return n}function At(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 Ot(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,d=t.width&&n.bounds?.width?t.width/n.bounds.width:1,h=t.height&&n.bounds?.height?t.height/n.bounds.height:d,f={x:i+Math.min(a*.35,110*d),y:o+Math.min(s-8*h,Math.max(18*h,s/2))};if(c==="screen"||!n.bounds)return f;const l=Math.max(0,(t.width-n.bounds.width*d)/2),g=Math.max(0,(t.height-n.bounds.height*h)/2);return{x:n.bounds.x+(f.x-l)/d,y:n.bounds.y+(f.y-g)/h}}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,d={x:i+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return d;const h=t.width&&n.bounds.width?t.width/n.bounds.width:1,f=t.height&&n.bounds.height?t.height/n.bounds.height:h,l=Math.max(0,(t.width-n.bounds.width*h)/2),g=Math.max(0,(t.height-n.bounds.height*f)/2);return{x:n.bounds.x+(d.x-l)/h,y:n.bounds.y+(d.y-g)/f}}function Lt(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 zt(e){const t=e.bounds;return!t?.width||!t?.height?null:{x:t.x+t.width*.68,y:t.y+t.height*.58}}function xe(e,t){return y(t)?ve(e,t):e===t||e.includes(t)||t.includes(e)}function D(e,t){if(y(t))return ve(e,t);const n=ye(C(e)),r=ye(C(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function ye(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 W(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 ve(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{P as captureAndRecognizeWeChatWindow,ce as captureWeChatWindow,Yt as ensureHelperPreflight,ot as ensureWeChatWindowReady,Ut as fallbackWeChatMessageInputPointForWindow,ie as focusExistingWeChatWindow,lt as focusKnownWeChatWindow,Ht as focusWeChatWindow,jt as observeWeChatChannelBindingViaHelper,ae as openConversationBySearch,z as openConversationInVisibleList,le as recognizeWeChatScreenshot};
1
+ import{defaultWeChatChannelAttachmentDir as _e,resolveVisibleWeChatChannelMedia as Te}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as We}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Be,loadWeChatChannelVectorStore as Ae,saveWeChatChannelVectorStore as Ee,upsertWeChatChannelVectorReferences as Fe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as H,normalizeWeChatChannelMessageKind as Re}from"./core/schema.js";import{waitForWeChatChannelPacing as Y,weChatChannelPacingDelayMs as Le}from"./pacing.js";import{matchWeChatConversationFingerprints as Oe}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as X}from"./anchor.js";import{decideWeChatChannelActivityGate as $e,normalizeWeChatChannelActivitySnapshot as ze}from"./human-coordination.js";const De=3,Pe=5,D="wechat_login_required",F="wechat_duplicate_instance";class T 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 Ve(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>Ke({...e,helper:n}))}async function Ke(e){const t=e.foregroundMode??"required";let n=await bt(e.helper,e.traceId,{foreground:t});if(t!=="background"&&(n=await Ct(e.helper,n,e.traceId)),t==="background"){const{capture:a,ocr:s}=await M({helper:e.helper,window:n,traceId:e.traceId}),c=B({capture:a,ocr:s,window:n});if(c)throw new Error(c);if(!$(s,a,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(j({capture:a,ocr:s,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:a,ocr:s}),J(e)?Q({...e,window:n,capture:a,ocr:s}):P({...e,window:n,capture:a,ocr:s})}const i=await fe({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!i.opened)throw new Error(i.reason||"conversation_not_opened");let{capture:r,ocr:o}=await M({helper:e.helper,window:n,traceId:e.traceId});if(j({capture:r,ocr:o,binding:e.binding})){n=await kt(e.helper,e.traceId);const a=await fe({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||"wechat_window_obstructed");if({capture:r,ocr:o}=await M({helper:e.helper,window:n,traceId:e.traceId}),j({capture:r,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:r,ocr:o}),J(e)?Q({...e,window:n,capture:r,ocr:o}):P({...e,window:n,capture:r,ocr:o})}function Ve(e,t,n){if(!t)return{helper:e,withLease:async(s,c,d)=>await d(e)};let i=null;const r=s=>{t.onEvent?.({...s,at:new Date().toISOString()})},o=async(s,c)=>{if(i){const u=await e.request("automation.lease.status",{},n),h=u.result??{},w=u.ok&&h.interrupted?Z(h.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(r({phase:"lease-status",stage:s,action:c,ok:u.ok===!0&&!h.interrupted,...w?{reasonCode:w}:{}}),!u.ok)throw new T({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});if(h.active===!1||h.leaseId!==i||!h.interrupted)return;throw new T({reasonCode:Z(h.interruptReason),stage:s,action:c})}const d=await e.request("activity.snapshot",{},n);if(!d.ok)throw r({phase:"snapshot",stage:s,action:c,ok:!1,reasonCode:d.errorCode||"user_activity_unknown"}),new T({reasonCode:d.errorCode||"user_activity_unknown",stage:s,action:c});const l=$e({snapshot:ze(d.result),stage:s,policy:t.policy});if(r({phase:"snapshot",stage:s,action:c,ok:l.ok,...l.ok?{}:{reasonCode:l.reasonCode,waitMs:l.waitMs}}),!l.ok)throw new T({reasonCode:l.reasonCode,stage:s,action:c})},a={request:async(s,c,d)=>{const l=qe(s);l&&await o(l.stage,`${s}:before`);const u=await e.request(s,c,d);return l&&u.ok&&await o(l.stage,`${s}:after`),u}};return{helper:a,async withLease(s,c,d){if(await o(s,`${c}:preflight`),t.useAutomationLease===!1)return await d(a);const l=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(i=typeof l.result?.leaseId=="string"?l.result.leaseId:null,r({phase:"lease-acquire",stage:s,action:c,ok:l.ok===!0,...l.ok?{}:{reasonCode:l.errorCode||"user_activity_unknown"}}),!l.ok)throw i=null,new T({reasonCode:l.errorCode||"user_activity_unknown",stage:s,action:c});try{const u=await d(a);return await o(s,`${c}:complete`),u}finally{const u=i;if(i=null,u){const h=await e.request("automation.lease.release",{leaseId:u},n).catch(w=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:w instanceof Error?w.message:String(w)}));r({phase:"lease-release",stage:s,action:c,ok:h.ok===!0,...h.ok?{}:{reasonCode:h.errorCode||"automation_lease_release_failed"}})}}}}}function qe(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 Z(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 J(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function P(e){const t=await me(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??[]}),i=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 Ne(i,ke({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 me(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 P(e);throw c}const i=et(n.structuredMessages??n.observedMessages??[]),r=await je({...e,messages:i,screenshot:t}),o=await re({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:r.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=Ne(o,ke({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?We({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:r.vectorReferences}).newMessages:a}function Ue(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function je(e){const t=Ge(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Ye({...e,seedBlocks:t}):t.filter(a=>f(a.vectorBase64)||f(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const i=e.vectorStorePath??Be(e.workDir||process.cwd(),e.runtime.runtimeId),r=Ae(i,e.runtime.runtimeId),o=Fe({store:r,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ee(i,r),{messages:Je(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Ge(e){const t=[];for(const n of e)for(const[i,r]of(n.visualBlocks??[]).entries()){const o=f(r.blockId)||`${n.stableMessageKey}:visual-${i}`,a=f(r.blockKind)||n.kind||"visual";t.push({...r,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...r.bbox!==void 0?{bbox:r.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function He(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 Ze(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Ye(e){try{return await He(e)}catch(t){if(Xe(t))throw t;return[]}}function Xe(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function Ze(e,t){return e.map((n,i)=>{const r=t[i];return{...n,stableMessageKey:f(n.stableMessageKey)||f(r?.stableMessageKey),blockId:f(n.blockId)||f(r?.blockId),blockKind:f(n.blockKind)||f(r?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:r?.bbox!==void 0?{bbox:r.bbox}:{}}}).filter(n=>f(n.stableMessageKey)&&f(n.blockId))}function Je(e,t){const n=new Map;for(const i of t){const r=n.get(i.stableMessageKey)??[];r.push(i),n.set(i.stableMessageKey,r)}return e.map(i=>{const r=n.get(i.stableMessageKey);return r?.length?{...i,visualBlocks:Qe(i,r)}:i})}function Qe(e,t){const i=(e.visualBlocks??[]).map((o,a)=>{const s=f(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?ee(c,o):te(o)}),r=new Set(i.map(o=>o.blockId));for(const o of t)r.has(o.blockId)||i.push(ee(o));return i}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 et(e){return e.map((t,n)=>tt(t,n)).filter(t=>t!=null)}function tt(e,t){if(!m(e))return null;const n=Re(e.kind),i=it(e.senderRole),r=g(e.normalizedText),o=g(e.anchorText)??r??g(e.textExcerpt),a=f(e.stableMessageKey)||nt({message:e,index:t,senderRole:i,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:i,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...r!==null?{normalizedText:r}:{},...o!==null?{anchorText:o}:{},...k(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...k(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...k(e,"bbox")?{bbox:e.bbox}:{},...k(e,"mediaMetadata")?{mediaMetadata:rt(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 nt(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 rt(e){if(!m(e))return e;const t={...e};return k(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 k(t,"availability")&&(t.availability=H(t.availability)),t}function it(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)=>ut(o,e.screenshot,e.window,lt(e.messages,a))).filter((o,a)=>ot(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),i=await Te({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 _({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!i.length)return e.messages;const r=new Map(i.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=r.get(o.stableMessageKey);return a?{...o,mediaMetadata:ht(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function ot(e,t,n){const i=oe(n);if(!i)return!0;const r=at(i),o=e.map((a,s)=>oe(dt(a)).includes(i)?s:-1).filter(a=>a>=0);if(!o.length)return t===st(e,r);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 at(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 st(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(ie(e[n]))return ct(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:{},i=m(n.attachment)?n.attachment:{};return ae(t,n,i)}function ct(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),i=m(e.mediaMetadata)?e.mediaMetadata:{},r=m(i.attachment)?i.attachment:{},o=String(r.type||"").toLowerCase(),a=String(r.mimeType||"").toLowerCase(),s=String(r.name||r.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 dt(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 lt(e,t){return[e[t-1],e[t],e[t+1]].map(i=>i&&(i.normalizedText||i.anchorText||i.textExcerpt)||"").map(i=>String(i||"").trim()).filter(Boolean).join(`
3
+ `)||null}function ut(e,t,n,i){const r=String(e.kind||"").toLowerCase(),o=m(e.mediaMetadata)?e.mediaMetadata:{},a=m(o.attachment)?o.attachment:{},s=f(a.availability)||f(o.availability),c=f(a.localPath)||f(o.localPath);if(s==="edge-local"&&c||!ae(r,o,a))return null;const d=R(e.bbox)??R(o.bbox)??R(o.downloadActionBbox),l=R(o.downloadActionBbox),u=se(d,t,n),h=wt(d,t,n),w=se(l,t,n);return{messageKey:e.stableMessageKey,kind:f(a.type)||f(a.kind)||f(o.messageType)||r,fileName:f(a.name)||f(a.fileName)||f(o.fileName)||null,mimeType:f(a.mimeType)||f(o.mimeType)||null,size:N(a.size)??N(o.size),mediaStatus:ft(o,a),observedAt:e.observedAt??null,contextText:i??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...h?{screenshotBbox:h}:{},...w?{downloadActionBbox:w}:{}}}function ht(e,t,n,i,r){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,...i?.length?{edgeResolveAttempts:i}:{},...r?{edgeResolveTrace:r}:{}}}function ft(e,t){const n=f(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const i=f(t.availability)||f(e.availability);return i==="pending-download"?"not_downloaded":i==="metadata-only"?"metadata_only":n||null}function ae(e,t,n){const i=`${e} ${f(t.messageType)} ${f(n.type)} ${f(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(i)}function R(e){if(!m(e))return null;const t=N(e.x),n=N(e.y),i=N(e.width),r=N(e.height);return t==null||n==null||i==null||r==null?null:{x:t,y:n,width:i,height:r,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function se(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const i=A(e,t,n);if(!i)return e;const r=n.bounds;if(!r?.width||!r?.height)return{...e,x:i.x-e.width/2,y:i.y-e.height/2,coordinateSpace:"screen"};const o=r.width/t.width,a=r.height/t.height,s=i.x,c=i.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 wt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const i=n.bounds;if(!i?.width||!i?.height)return null;const r=t.width/i.width,o=t.height/i.height;return{x:(e.x-i.x)*r,y:(e.y-i.y)*o,width:e.width*r,height:e.height*o,coordinateSpace:"screenshotPixel"}}function m(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function k(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 N(e){const t=Number(e);return Number.isFinite(t)?t:null}async function cn(e,t){await ce(e,t)}async function ce(e,t,n={}){let i=await e.request("permissions.check",{},t);b(i,"permissions.check");const r=i.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(gt(r))throw new Error(D);if(mt(r))throw new Error(F);if(yt(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(de([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(F);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function bt(e,t,n={}){const r=(n.foreground??"required")!=="background",o=await ce(e,t,{allowUnavailableWindow:r});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:r,allowRecovery:!1,allowLaunch:!1},t);b(c,"windows.ensureReady");const d=c.result;if(!d?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return d}const a=await e.request("windows.ensureReady",{restore:r,focus:r},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 de(e,t){const n=e.filter(x).filter(r=>r.visible!==!1&&r.minimized!==!0),i=t??n.slice().sort((r,o)=>I(o)-I(r))[0];return n.find(r=>xt(r,i))??null}function mt(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(x)||(e.unsafeAuxiliaryWindows??[]).some(x)}function gt(e){if(Number(e.duplicateMainWindowCount??0)>0||(e.duplicateMainWindows??[]).some(x))return!1;const t=e.unsafeAuxiliaryWindows??[];return Number(e.unsafeAuxiliaryWindowCount??0)>0||t.some(ue)?![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(V)||e.wechatWindowAvailable===!1:!1}function yt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function K(e){const t=e.filter(x).filter(r=>r.visible!==!1&&r.minimized!==!0),n=t.filter(V);if(n.length>1)throw new Error(F);const i=n.slice().sort((r,o)=>I(o)-I(r))[0];if(de(t,i))throw new Error(F)}function le(e){const t=e.filter(x),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),i=n.filter(V);return(i.length?i:n.length?n:t).slice().sort((o,a)=>I(a)-I(o))[0]??null}function xt(e,t){return!(!x(e)||t&&pt(e,t)||e.visible===!1||e.minimized===!0||!ue(e)||t&&vt(e,t))}function ue(e){if(!x(e)||e.visible===!1||e.minimized===!0)return!1;const t=Number(e.bounds?.width),n=Number(e.bounds?.height);if(!Number.isFinite(t)||!Number.isFinite(n))return!1;const i=t>=360&&t<=720&&n>=420&&n<=840&&n>t,r=t>=420&&t<=680&&n>=260&&n<=460;return i||r}function vt(e,t){const n=Number(e.rootProcessId),i=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(i)||n!==i)return!1;const r=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(r)||!Number.isFinite(o)?!1:r>o}function V(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function I(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function pt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function dn(e,t){let n=await e.request("windows.list",{},t);b(n,"windows.list");let i=n.result?.windows??[];K(i);let r=le(i);if(!r)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:r.windowId},t);return b(o,"windows.focus"),await he(e,r,t)}async function Ct(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const i=await e.request("windows.focus",{windowId:t.windowId},n);return b(i,"windows.focus"),t}async function kt(e,t){const n=await e.request("windows.list",{},t);b(n,"windows.list");const i=n.result?.windows??[];K(i);const r=le(i);if(!r)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:r.windowId},t);return b(o,"windows.focus"),await he(e,r,t)}async function he(e,t,n){let i;try{i=await e.request("windows.list",{},n)}catch{return t}if(!i.ok)return t;const r=(i.result?.windows??[]).filter(x);return K(r),r.find(o=>o.windowId===t.windowId)??r[0]??t}async function q(e){const{capture:t,ocr:n}=e.observation??await M(e),i=B({capture:t,ocr:n,window:e.window});if(i)return{opened:!1,reason:i};const r=Oe({visibleItems:Et(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!r)return{opened:!1,reason:"conversation_not_visible"};const o=Xt(r.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:r.reason}}async function fe(e){let t={opened:!1,reason:"conversation_not_opened"};for(let i=0;i<De;i+=1){if(t=await Nt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${i}`)}const n=await we(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Nt(e){const t=await M(e),n=B({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let i=!1;if($(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!i){const u=await q({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await _(e)).ok)return{opened:!0,reason:u.reason};const w=await we(e);if(w.opened)return w}}const r=Wt(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),...r?{searchPoint:r}:{}},e.traceId);b(o,"wechat.searchConversation");const a=await It(e);if(a.opened){await S(e,"open-click-settle","search-result-click");const u=await _(e);return u.ok?(e.settleToBottom===!0&&await W({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 At(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await _(e);if(u.ok)return e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const h=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),...r?{searchPoint:r}:{}},e.traceId);b(h,"wechat.searchConversation"),s=void 0}const d=await q({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!d.opened)return d;await S(e,"open-click-settle","visible-final");const l=await _(e);return l.ok?(e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:d.reason}):{opened:!1,reason:l.reason}}async function we(e){const t=await q(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await _(e);return n.ok?(e.settleToBottom===!0&&await W({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),i=Number(t?.y),r=Number(t?.width),o=Number(t?.height);if(![n,i,r,o].every(Number.isFinite)||r<=0||o<=0)return null;const a=Math.max(520,Math.min(r-160,r*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(i+o-s),coordinateSpace:"screen"}}async function It(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),i=y(e.binding.conversationDisplayName)?Pe:1;for(let r=0;r<i;r+=1){r>0&&n>0&&await ye(n);const o=await Mt(e);t=o;const{capture:a,ocr:s}=o,c=B({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Lt(s,a,e.binding.conversationDisplayName);if(!d){if(Ot(s)||$t(s,e.binding.conversationDisplayName))break;continue}const l=A(d.bbox,a,St(a,e.window));if(!l)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:l.x,y:l.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return b(u,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Mt(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},i=await ge(e.helper,n,e.traceId);return{capture:n,ocr:i}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&b(t,"screen.capture")}return M(e)}function St(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function M(e){let{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await _t({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Y("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function _t(e){const t=Tt(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 Tt(e,t,n){const i=e.blocks??[],r=i.find(c=>{const d=String(c.text||""),l=c.bbox;if(!l)return!1;const u=Number(l.x),h=Number(l.y);return Number.isFinite(u)&&Number.isFinite(h)&&h>t.height*.06&&h<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!r)return null;const o=r.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=i.filter(c=>{const d=String(c.text||"").trim(),l=c.bbox;if(!l||d!=="\xD7")return!1;const u=Number(l.x),h=Number(l.y);return Number.isFinite(u)&&Number.isFinite(h)&&u>t.width*.55&&u<t.width*.9&&Math.abs(h-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return A(s?.bbox,t,n)}function Wt(e,t){const{capture:n,ocr:i}=t,r=(i.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),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return A(r?.bbox,n,e.window)}async function be(e,t,n,i){const r=await e.request("windows.capture",{windowId:t,scope:"full-window",...i?{bounds:i}:{}},n);b(r,"windows.capture");const o=r.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 me(e){return e.capture.dataBase64?e.capture:be(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function U(e,t,n,i){const r=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...i?{bounds:i}:{}},n);if(r.ok&&r.result?.capture&&r.result?.ocr)return{capture:r.result.capture,ocr:r.result.ocr};!r.ok&&r.errorCode&&r.errorCode!=="helper_unknown_command"&&r.errorCode!=="helper_command_unsupported"&&b(r,"windows.captureAndOcr");const o=await be(e,t,n,i),a=await ge(e,o,n);return{capture:o,ocr:a}}async function ge(e,t,n){const i=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return b(i,"ocr.recognize"),i.result??{}}function b(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function x(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function ye(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Le(t,Bt(e,n))}async function S(e,t,n){const i=L(e,t,n);i>0&&await ye(i)}function Bt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function At(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);b(n,"keyboard.shortcut")}async function W(e){if(!e.window.bounds)return;const t=e.observation,n=Zt(t?.ocr,t?.capture,e.window);if(n){const i=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);b(i,"mouse.click"),await Y("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function _(e){const{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds),i=B({capture:t,ocr:n,window:e.window});return i?{ok:!1,reason:i}:$(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function B(e){const t=(e.ocr.blocks??[]).map(i=>Pt(i.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Dt(e,t)?D:null}function Et(e,t,n=!0){const i=n?Ft(e.blocks??[],t):[];return i.length?i:e.visibleConversationFingerprints??[]}function Ft(e,t){const n=e.map(r=>xe(r)).filter(r=>!!r).filter(r=>r.text.length>=2).filter(r=>r.x>t.width*.08&&r.x<t.width*.38).filter(r=>r.y>t.height*.06&&r.y<t.height*.96).filter(r=>!ve(r.text)),i=new Set;return n.filter(r=>!Rt(r,n,t)).sort((r,o)=>r.y-o.y||r.x-o.x).map(r=>{const o=r.text.trim();return{title:o,bbox:{x:Math.max(0,r.x-24),y:Math.max(0,r.y-Math.max(24,r.height)),width:Math.max(120,t.width*.3),height:Math.max(48,r.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(r=>{const o=E(r.title);return!o||i.has(o)?!1:(i.add(o),!0)})}function xe(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),i=Number(t?.x),r=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![i,r,o,a].every(Number.isFinite)?null:{text:n,x:i,y:r,width:o,height:a}}function Rt(e,t,n){const i=Math.max(36,Math.min(64,n.height*.058));return t.some(r=>r!==e&&r.y<e.y&&e.y-r.y<=i&&Math.abs(r.x-e.x)<=16&&r.height>=e.height*.85)}function ve(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||O(t)||zt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Lt(e,t,n){const i=(e.blocks??[]).map(s=>xe(s)).filter(s=>!!s),r=i.filter(s=>O(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=r.findIndex(h=>p(h.text)===s);if(c<0)continue;const d=r[c],l=r[c+1]?.y??Number.POSITIVE_INFINITY,u=i.filter(h=>h.y>d.y&&h.y<l).filter(h=>h.x<t.width*.7).filter(h=>h.text.length>=2).filter(h=>!O(p(h.text))).filter(h=>!Ce(h,i)).filter(h=>G(h.text,n)).sort((h,w)=>h.y-w.y||h.x-w.x)[0];if(u)return{bbox:pe(u,t),reason:"search-result-title"}}const a=i.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=>!ve(s.text)).filter(s=>!Ce(s,i)).filter(s=>G(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:pe(a,t),reason:"search-result-title"}:null}function pe(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 O(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function Ot(e){return(e.blocks??[]).some(t=>O(p(t.text)))}function $t(e,t){return(e.visibleConversationFingerprints??[]).some(n=>G(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ce(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 zt(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 Dt(e,t){if(!x(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),i=Number(e.window.bounds?.height||e.capture.height);if(![n,i].every(Number.isFinite)||!(n>=360&&n<=720&&i>=420&&i<=840&&i>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function Pt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function $(e,t,n){const i=y(n)?E(n):z(n);return i?(e.blocks??[]).some(r=>{const o=y(n)?E(r.text):z(r.text);if(!o||!Ie(o,i))return!1;const a=r.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,l=s<t.width*.18&&c<t.height*.12;return d||l}):!1}function j(e){if($(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
+ `),r=[{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 d=Array.isArray(c.source)?c.source.some(l=>c.pattern.test(l)):c.pattern.test(c.source);return s+(d?1:0)},0);if(r>=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)&&r>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&Kt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?E(s.title):z(s.title),d=y(e.binding.conversationDisplayName)?E(e.binding.conversationDisplayName):z(e.binding.conversationDisplayName);return!!(c&&d&&Ie(c,d))}),!1)}function Kt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),i=n.join(`
6
+ `);if(/\b\d+\s*x\s*\d+\b/i.test(i)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(i)||/\b100%\b/.test(i)&&n.length<=20)return!0;const r=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&r.length<=6}function ke(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],i=new Set,r=Vt(e.ocr,e.screenshot).sort((o,a)=>v(a.text).length-v(o.text).length);for(const o of r){const a=v(o.text);if(!a)continue;const s=t.find(d=>qt(d.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;i.has(c)||(i.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 Vt(e,t){const n=(e.blocks??[]).filter(r=>Yt(r,t)&&v(r.text)).sort((r,o)=>C(r.bbox,"y")-C(o.bbox,"y")),i=n.map(r=>({text:String(r.text||"").trim(),bbox:r.bbox}));for(let r=0;r<n.length;r+=1){const o=[n[r]];for(let a=r+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!Ut(o[o.length-1],s,t))break;o.push(s),i.push({text:jt(o.map(c=>String(c.text||"").trim())),bbox:Ht(o.map(c=>c.bbox))})}}return i}function qt(e,t){if(!e||!t)return!1;if(X(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),i=t.replace(/\s+/g,"");return i.length>=8&&n.includes(i)}function Ut(e,t,n){const i=C(e.bbox,"x"),r=C(e.bbox,"y"),o=C(e.bbox,"height"),a=C(t.bbox,"x"),s=C(t.bbox,"y");if(![i,r,o,a,s].every(Number.isFinite)||i<n.width*.5||a<n.width*.5)return!1;const c=s-(r+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-i)<=120}function jt(e){return e.reduce((t,n)=>t?n?`${t}${Gt(t,n)?" ":""}${n}`:t:n,"")}function Gt(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Ht(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),i=t.map(c=>Number(c.y)).filter(Number.isFinite),r=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||!i.length||!r.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...i);return{x:a,y:s,width:Math.max(...r)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Ne(e,t){if(!t.length)return e;const n=[...e];for(const i of t){const r=v(i.anchorText||i.normalizedText||i.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=v(a.anchorText||a.normalizedText||a.textExcerpt);return s&&r&&X(s,r)>=.9})||n.push(i)}return n}function Yt(e,t){const n=e.bbox;if(!n)return!1;const i=Number(n.x),r=Number(n.y);return!Number.isFinite(i)||!Number.isFinite(r)?!1:i>t.width*.34&&i<t.width*.96&&r>t.height*.12&&r<t.height*.86}function Xt(e,t,n){if(!e||typeof e!="object")return null;const i=e,r=Number(i.x),o=Number(i.y),a=Number(i.width),s=Number(i.height);if(![r,o,a,s].every(Number.isFinite))return null;const c=typeof i.coordinateSpace=="string"?i.coordinateSpace:void 0,d=t.width&&n.bounds?.width?t.width/n.bounds.width:1,l=t.height&&n.bounds?.height?t.height/n.bounds.height:d,u={x:r+Math.min(a*.35,110*d),y:o+Math.min(s-8*l,Math.max(18*l,s/2))};if(c==="screen"||!n.bounds)return u;const h=Math.max(0,(t.width-n.bounds.width*d)/2),w=Math.max(0,(t.height-n.bounds.height*l)/2);return{x:n.bounds.x+(u.x-h)/d,y:n.bounds.y+(u.y-w)/l}}function A(e,t,n){if(!e||typeof e!="object")return null;const i=e,r=Number(i.x),o=Number(i.y),a=Number(i.width),s=Number(i.height);if(![r,o,a,s].every(Number.isFinite))return null;const c=typeof i.coordinateSpace=="string"?i.coordinateSpace:void 0,d={x:r+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return d;const l=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:l,h=Math.max(0,(t.width-n.bounds.width*l)/2),w=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(d.x-h)/l,y:n.bounds.y+(d.y-w)/u}}function Zt(e,t,n){if(!e?.blocks?.length||!t)return null;const i=e.blocks.find(r=>{const o=String(r.text||"").trim(),a=r.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 A(i?.bbox,t,n)}function Ie(e,t){return y(t)?Se(e,t):e===t||e.includes(t)||t.includes(e)}function G(e,t){if(y(t))return Se(e,t);const n=Me(p(e)),i=Me(p(t));return!n||!i?!1:n===i?!0:n.replace(/\d+$/u,"")===i}function Me(e){return e.toLowerCase().replace(/[l1]/g,"i")}function z(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 Se(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),i=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&i&&(n===i||n.replace(/\d+$/u,"")===i))}export{T as WeChatChannelUserActivityAbort,U as captureAndRecognizeWeChatWindow,be as captureWeChatWindow,cn as ensureHelperPreflight,bt as ensureWeChatWindowReady,ln as fallbackWeChatMessageInputPointForWindow,kt as focusExistingWeChatWindow,Ct as focusKnownWeChatWindow,dn as focusWeChatWindow,sn as observeWeChatChannelBindingViaHelper,fe as openConversationBySearch,q as openConversationInVisibleList,ge as recognizeWeChatScreenshot};
@@ -45,6 +45,8 @@ export declare class WeChatChannelOutboundSender {
45
45
  private sendTextIfPresent;
46
46
  private sendAttachmentsIfPresent;
47
47
  private withAutomationLease;
48
+ private releaseAutomationLease;
49
+ private waitAfterPaste;
48
50
  private ensureStageIdle;
49
51
  private activitySnapshot;
50
52
  private automationLeaseDecision;
@@ -1 +1 @@
1
- import v from"node:fs";import A from"node:path";import{guardWeChatOutboundRevision as C,markWeChatOutboundFailed as S,markWeChatOutboundManualReview as k,markWeChatOutboundCommitStage as r,markWeChatOutboundSending as x,markWeChatOutboundSentUnconfirmed as T,markWeChatOutboundUserActiveTimeout as h,markWeChatOutboundWaitingUserIdle as w,shouldFailWeChatOutboundForUserActiveTimeout as d}from"./outbound-ledger.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as I,normalizeWeChatChannelActivitySnapshot as R}from"./human-coordination.js";const W=600*1e3,b=450,y=520;async function j(e){const t={sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]},s=e.now??new Date,n=e.maxUserActivityWaitMs??W,i=e.ledger.records.filter(a=>a.bindingId===e.bindingId&&a.sendStatus==="queued");for(const a of i){if(d({record:a,now:s,maxWaitMs:n})){h(a,s),t.failedRecords.push(a);continue}if(!C({record:a,currentLastInboundRevision:e.currentLastInboundRevision,now:s}).ok){t.staleRecords.push(a);continue}try{const o=await e.sender.canStart(a,s);if(o.ok===!1){w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}x(a),await e.sender.send(a),T(a,s),t.sentRecords.push(a)}catch(o){if(o instanceof f){if(D(o)){k(a,"user_takeover_before_return","User activity was detected after paste and before Return; manual WeChat inspection is required",o.stage),t.manualReviewRecords.push(a);continue}w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}S(a,q(o),o instanceof Error?o.message:String(o)),t.failedRecords.push(a)}}return t}class B{options;activeAutomationLeaseId=null;constructor(t){this.options=t}async canStart(t,s=new Date){const n=await this.activitySnapshot();U(n,this.options.platform);const i=_({snapshot:n,stage:"send_start",policy:this.options.activityGatePolicy});return i.ok===!1?{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:I(s,i.waitMs)}:{ok:!0}}async send(t){await this.withAutomationLease(t,()=>this.sendWithLease(t))}async sendWithLease(t){await this.ensureStageIdle(t,"open_conversation"),r(t,"open_conversation");const s=await this.options.openConversation(t.conversationName);if(!s.opened)throw new Error(s.reason||"conversation_not_opened");r(t,"focus_input"),await L(this.options.helper,this.options.traceId,s.inputPoint??void 0),await this.ensureStageIdle(t,"focus_input"),await this.ensureStageIdle(t,"clipboard_snapshot"),r(t,"clipboard_snapshot");const n=await this.options.helper.request("clipboard.snapshot",{},this.options.traceId);c(n,"clipboard.snapshot");let i=!1;try{await this.sendTextIfPresent(t,()=>{i=!0}),await this.sendAttachmentsIfPresent(t,()=>{i=!0})}finally{const a=n.result&&typeof n.result=="object"?n.result:{},p=await this.options.helper.request("clipboard.restore",a,this.options.traceId);i||c(p,"clipboard.restore")}}async sendTextIfPresent(t,s){if(!t.text)return;await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set"),await u(this.options.helper,"a",m(this.options.platform),this.options.traceId),await u(this.options.helper,"backspace",[],this.options.traceId);const n=await this.options.helper.request("clipboard.setText",{text:t.text},this.options.traceId);c(n,"clipboard.setText"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}async sendAttachmentsIfPresent(t,s){for(const n of t.attachmentLocalRefs??[]){O(n),await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set");const i=E(n),a=i==="image"&&g(this.options.platform)?await this.options.helper.request("clipboard.setImage",{filePath:n},this.options.traceId):await this.options.helper.request("clipboard.setFiles",{filePaths:[n]},this.options.traceId);c(a,i==="image"&&g(this.options.platform)?"clipboard.setImage":"clipboard.setFiles"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}}async withAutomationLease(t,s){const n=await this.options.helper.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`send:${t.bindingId}`,ttlMs:6e4},this.options.traceId);c(n,"automation.lease.acquire");const i=typeof n.result?.leaseId=="string"?n.result.leaseId:"";try{this.activeAutomationLeaseId=i||null,await s()}finally{this.activeAutomationLeaseId=null,i&&await this.options.helper.request("automation.lease.release",{leaseId:i},this.options.traceId)}}async ensureStageIdle(t,s){if(this.options.takeoverCheck===!1)return;if(this.activeAutomationLeaseId){const a=await this.automationLeaseDecision();if(a.ok===!1)throw new f({reasonCode:a.reasonCode,nextAttemptAt:a.nextAttemptAt,stage:s,replyId:t.replyId});return}const n=await this.activitySnapshot(),i=_({snapshot:n,stage:M(s),policy:this.options.activityGatePolicy});if(!i.ok)throw new f({reasonCode:i.reasonCode,nextAttemptAt:I(new Date,i.waitMs),stage:s,replyId:t.replyId})}async activitySnapshot(){const t=await this.options.helper.request("activity.snapshot",{},this.options.traceId);return c(t,"activity.snapshot"),R(t.result)}async automationLeaseDecision(){if(!this.activeAutomationLeaseId)return{ok:!0};const t=await this.options.helper.request("automation.lease.status",{},this.options.traceId);c(t,"automation.lease.status");const s=t.result??{};return s.active===!1||s.leaseId!==this.activeAutomationLeaseId||!s.interrupted?{ok:!0}:{ok:!1,reasonCode:P(s.interruptReason),nextAttemptAt:new Date(Date.now()+1e3)}}}class f extends Error{reasonCode;nextAttemptAt;stage;replyId;constructor(t){super(`user_takeover_aborted:${t.reasonCode}:${t.stage}`),this.name="WeChatChannelUserTakeoverAbort",this.reasonCode=t.reasonCode,this.nextAttemptAt=t.nextAttemptAt,this.stage=t.stage,this.replyId=t.replyId}}function M(e){return e==="open_conversation"?"open_conversation":e==="queued"?"send_start":"dangerous_action"}async function L(e,t,s){const n={waitMs:y,...s?{inputPoint:s}:{}};if((await e.request("wechat.focusMessageInput",n,t)).ok){await l(120);const p=await e.request("wechat.focusMessageInput",n,t);c(p,"wechat.focusMessageInput");return}await l(350);const a=await e.request("wechat.focusMessageInput",{...n,waitMs:Math.max(y,700)},t);c(a,"wechat.focusMessageInput")}async function u(e,t,s,n){const i=await e.request("keyboard.shortcut",{key:t,modifiers:s},n);c(i,"keyboard.shortcut")}function O(e){if(!v.statSync(e).isFile())throw new Error(`wechat_channel_attachment_not_file:${e}`)}function E(e){const t=A.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic",".tiff",".bmp"].includes(t)?"image":[".mp4",".mov",".m4v",".avi",".mkv",".webm"].includes(t)?"video":"file"}function g(e){return(e??process.platform)==="win32"}function m(e){return(e??process.platform)==="win32"?["control"]:["command"]}function U(e,t){if((t??process.platform)!=="darwin")return;const s=e?.permissions;if(!s)return;const n=[s.accessibilityTrusted===!1?"accessibility":null,s.iohidPostGranted===!1?"input-monitoring":null].filter(Boolean);if(n.length)throw new Error(`permission_missing:mac_input:${n.join(",")}`)}function l(e){return new Promise(t=>setTimeout(t,e))}function q(e){const t=e instanceof Error?e.message:String(e);return/conversation_not|conversation.*visible|conversation.*open/i.test(t)?"conversation_not_opened":/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/clipboard/i.test(t)?"clipboard_failed":/attachment|not_file|ENOENT|no such file/i.test(t)?"attachment_unavailable":"send_failed"}function P(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 D(e){return e.stage==="before_return"}function c(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}export{B as WeChatChannelOutboundSender,f as WeChatChannelUserTakeoverAbort,j as sendQueuedWeChatOutboundRecords};
1
+ import g from"node:fs";import A from"node:path";import{waitForWeChatChannelPacing as l}from"./pacing.js";import{guardWeChatOutboundRevision as v,markWeChatOutboundFailed as C,markWeChatOutboundManualReview as k,markWeChatOutboundCommitStage as r,markWeChatOutboundSending as S,markWeChatOutboundSentUnconfirmed as x,markWeChatOutboundUserActiveTimeout as h,markWeChatOutboundWaitingUserIdle as w,shouldFailWeChatOutboundForUserActiveTimeout as d}from"./outbound-ledger.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as y,normalizeWeChatChannelActivitySnapshot as R}from"./human-coordination.js";const W=600*1e3,b=520;async function j(e){const t={sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]},s=e.now??new Date,i=e.maxUserActivityWaitMs??W,n=e.ledger.records.filter(a=>a.bindingId===e.bindingId&&a.sendStatus==="queued");for(const a of n){if(d({record:a,now:s,maxWaitMs:i})){h(a,s),t.failedRecords.push(a);continue}if(!v({record:a,currentLastInboundRevision:e.currentLastInboundRevision,now:s}).ok){t.staleRecords.push(a);continue}try{const o=await e.sender.canStart(a,s);if(o.ok===!1){w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:i})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}S(a),await e.sender.send(a),x(a,s),t.sentRecords.push(a)}catch(o){if(o instanceof f){if(E(o)){k(a,"user_takeover_before_return","User activity was detected after paste and before Return; manual WeChat inspection is required",o.stage),t.manualReviewRecords.push(a);continue}w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:i})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}C(a,U(o),o instanceof Error?o.message:String(o)),t.failedRecords.push(a)}}return t}class z{options;activeAutomationLeaseId=null;constructor(t){this.options=t}async canStart(t,s=new Date){const i=await this.activitySnapshot();O(i,this.options.platform);const n=_({snapshot:i,stage:"send_start",policy:this.options.activityGatePolicy});return n.ok===!1?{ok:!1,reasonCode:n.reasonCode,nextAttemptAt:y(s,n.waitMs)}:{ok:!0}}async send(t){await this.withAutomationLease(t,()=>this.sendWithLease(t))}async sendWithLease(t){await this.ensureStageIdle(t,"open_conversation"),r(t,"open_conversation");const s=await this.options.openConversation(t.conversationName);if(!s.opened)throw new Error(s.reason||"conversation_not_opened");r(t,"focus_input"),await M(this.options.helper,this.options.traceId,s.inputPoint??void 0),await this.ensureStageIdle(t,"focus_input"),await this.ensureStageIdle(t,"clipboard_snapshot"),r(t,"clipboard_snapshot");const i=await this.options.helper.request("clipboard.snapshot",{},this.options.traceId);c(i,"clipboard.snapshot");let n=!1;try{await this.sendTextIfPresent(t,()=>{n=!0}),await this.sendAttachmentsIfPresent(t,()=>{n=!0})}finally{const a=i.result&&typeof i.result=="object"?i.result:{},p=await this.options.helper.request("clipboard.restore",a,this.options.traceId);n||c(p,"clipboard.restore")}}async sendTextIfPresent(t,s){if(!t.text)return;await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set"),await u(this.options.helper,"a",m(this.options.platform),this.options.traceId),await u(this.options.helper,"backspace",[],this.options.traceId);const i=await this.options.helper.request("clipboard.setText",{text:t.text},this.options.traceId);c(i,"clipboard.setText"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await this.waitAfterPaste(t),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}async sendAttachmentsIfPresent(t,s){for(const i of t.attachmentLocalRefs??[]){L(i),await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set");const n=q(i),a=n==="image"&&I(this.options.platform)?await this.options.helper.request("clipboard.setImage",{filePath:i},this.options.traceId):await this.options.helper.request("clipboard.setFiles",{filePaths:[i]},this.options.traceId);c(a,n==="image"&&I(this.options.platform)?"clipboard.setImage":"clipboard.setFiles"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await this.waitAfterPaste(t),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}}async withAutomationLease(t,s){const i=await this.options.helper.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`send:${t.bindingId}`,ttlMs:6e4},this.options.traceId);c(i,"automation.lease.acquire");const n=typeof i.result?.leaseId=="string"?i.result.leaseId:"";try{this.activeAutomationLeaseId=n||null,await s()}finally{this.activeAutomationLeaseId=null,n&&await this.releaseAutomationLease(n)}}async releaseAutomationLease(t){try{await this.options.helper.request("automation.lease.release",{leaseId:t},this.options.traceId)}catch{}}async waitAfterPaste(t){await l("send-post-paste",`${this.options.traceId||""}:${t.replyId}:post-paste`,this.options.postPasteSettleMs??void 0)}async ensureStageIdle(t,s){if(this.options.takeoverCheck===!1)return;if(this.activeAutomationLeaseId){const a=await this.automationLeaseDecision();if(a.ok===!1)throw new f({reasonCode:a.reasonCode,nextAttemptAt:a.nextAttemptAt,stage:s,replyId:t.replyId});return}const i=await this.activitySnapshot(),n=_({snapshot:i,stage:T(s),policy:this.options.activityGatePolicy});if(!n.ok)throw new f({reasonCode:n.reasonCode,nextAttemptAt:y(new Date,n.waitMs),stage:s,replyId:t.replyId})}async activitySnapshot(){const t=await this.options.helper.request("activity.snapshot",{},this.options.traceId);return c(t,"activity.snapshot"),R(t.result)}async automationLeaseDecision(){if(!this.activeAutomationLeaseId)return{ok:!0};const t=await this.options.helper.request("automation.lease.status",{},this.options.traceId);c(t,"automation.lease.status");const s=t.result??{};return s.active===!1||s.leaseId!==this.activeAutomationLeaseId||!s.interrupted?{ok:!0}:{ok:!1,reasonCode:P(s.interruptReason),nextAttemptAt:new Date(Date.now()+1e3)}}}class f extends Error{reasonCode;nextAttemptAt;stage;replyId;constructor(t){super(`user_takeover_aborted:${t.reasonCode}:${t.stage}`),this.name="WeChatChannelUserTakeoverAbort",this.reasonCode=t.reasonCode,this.nextAttemptAt=t.nextAttemptAt,this.stage=t.stage,this.replyId=t.replyId}}function T(e){return e==="open_conversation"?"open_conversation":e==="queued"?"send_start":"dangerous_action"}async function M(e,t,s){const i={waitMs:b,...s?{inputPoint:s}:{}};if((await e.request("wechat.focusMessageInput",i,t)).ok){await l("send-focus-stabilize",`${t||""}:focus-stabilize`);const p=await e.request("wechat.focusMessageInput",i,t);c(p,"wechat.focusMessageInput");return}await l("send-focus-retry",`${t||""}:focus-retry`);const a=await e.request("wechat.focusMessageInput",{...i,waitMs:Math.max(b,700)},t);c(a,"wechat.focusMessageInput")}async function u(e,t,s,i){const n=await e.request("keyboard.shortcut",{key:t,modifiers:s},i);c(n,"keyboard.shortcut")}function L(e){if(!g.statSync(e).isFile())throw new Error(`wechat_channel_attachment_not_file:${e}`)}function q(e){const t=A.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic",".tiff",".bmp"].includes(t)?"image":[".mp4",".mov",".m4v",".avi",".mkv",".webm"].includes(t)?"video":"file"}function I(e){return(e??process.platform)==="win32"}function m(e){return(e??process.platform)==="win32"?["control"]:["command"]}function O(e,t){if((t??process.platform)!=="darwin")return;const s=e?.permissions;if(!s)return;const i=[s.accessibilityTrusted===!1?"accessibility":null,s.iohidPostGranted===!1?"input-monitoring":null].filter(Boolean);if(i.length)throw new Error(`permission_missing:mac_input:${i.join(",")}`)}function U(e){const t=e instanceof Error?e.message:String(e);return/conversation_not|conversation.*visible|conversation.*open/i.test(t)?"conversation_not_opened":/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/clipboard/i.test(t)?"clipboard_failed":/attachment|not_file|ENOENT|no such file/i.test(t)?"attachment_unavailable":"send_failed"}function P(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 E(e){return e.stage==="before_return"}function c(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}export{z as WeChatChannelOutboundSender,f as WeChatChannelUserTakeoverAbort,j as sendQueuedWeChatOutboundRecords};
@@ -0,0 +1,3 @@
1
+ export type WeChatChannelPacingProfile = 'open-retry' | 'open-click-settle' | 'search-input-settle' | 'search-result-probe' | 'preview-dismiss-settle' | 'jump-latest-settle' | 'send-focus-stabilize' | 'send-focus-retry' | 'send-post-paste' | 'send-after-submit' | 'observe-retry';
2
+ export declare function weChatChannelPacingDelayMs(profile: WeChatChannelPacingProfile, seed?: string): number;
3
+ export declare function waitForWeChatChannelPacing(profile: WeChatChannelPacingProfile, seed?: string, overrideMs?: number): Promise<number>;
@@ -0,0 +1 @@
1
+ import M from"node:crypto";const o={"open-retry":{minMs:320,maxMs:560},"open-click-settle":{minMs:760,maxMs:1180},"search-input-settle":{minMs:620,maxMs:980},"search-result-probe":{minMs:360,maxMs:640},"preview-dismiss-settle":{minMs:260,maxMs:480},"jump-latest-settle":{minMs:620,maxMs:920},"send-focus-stabilize":{minMs:90,maxMs:190},"send-focus-retry":{minMs:280,maxMs:540},"send-post-paste":{minMs:420,maxMs:760},"send-after-submit":{minMs:820,maxMs:1280},"observe-retry":{minMs:1100,maxMs:2600}};function r(e,a){const t=o[e],s=Math.max(0,Math.floor(t.minMs)),m=Math.max(s,Math.floor(t.maxMs))-s+1;if(m<=1)return s;const i=M.createHash("sha256").update(`${e}:${a||""}`).digest().readUInt32BE(0);return s+i%m}async function u(e,a,t){const s=typeof t=="number"&&Number.isFinite(t)?Math.max(0,Math.floor(t)):r(e,a);return s>0&&await new Promise(n=>setTimeout(n,s)),s}export{u as waitForWeChatChannelPacing,r as weChatChannelPacingDelayMs};
@@ -31,6 +31,13 @@ export type WeChatChannelPreflightInput = {
31
31
  duplicateInstanceBlocked?: boolean;
32
32
  duplicateMainWindowCount?: number;
33
33
  unsafeAuxiliaryWindowCount?: number;
34
+ windowsVisibleDesktopAvailable?: boolean;
35
+ rdpVisibleDesktopAvailable?: boolean;
36
+ desktopSessionVisible?: boolean;
37
+ screenLocked?: boolean;
38
+ rdpDisconnected?: boolean;
39
+ dpiMappingAvailable?: boolean;
40
+ displayTopologySupported?: boolean;
34
41
  } | null;
35
42
  };
36
43
  export type WeChatChannelPreflightResult = {
@@ -1 +1 @@
1
- import{requiredWeChatChannelHelperCapabilitiesForProfile as s,requiredWindowsWeChatChannelHelperCapabilitiesForProfile as t,validateWeChatChannelHelperReady as c}from"./helper-protocol.js";function u(e){const a=[],i=e.platform==="darwin"||e.platform==="win32";if(a.push(r(i,"platform_unsupported","\u5FAE\u4FE1 Channel \u5F53\u524D\u652F\u6301 macOS \u548C Windows \u684C\u9762\u5FAE\u4FE1\u3002")),a.push(r(e.accountTier==="enterprise","enterprise_required","\u5F53\u524D\u9636\u6BB5\u5FAE\u4FE1 Channel \u4EC5\u4F01\u4E1A\u7248\u8D26\u53F7\u53EF\u7528\u3002")),a.push(r(!!e.privacyConsentAccepted,"privacy_consent_required","\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1 Channel \u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002")),a.push(r(!!e.serverDecisionAvailable,"server_decision_unavailable","\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u4E0D\u53EF\u7528\uFF0C\u6682\u4E0D\u80FD\u542F\u7528\u3002")),e.currentMachineId&&e.runtime.machineId&&a.push(r(e.currentMachineId===e.runtime.machineId,"same_machine_required","\u7B2C\u4E00\u7248\u53EA\u652F\u6301\u540C\u673A\u5FAE\u4FE1\u4E0E\u540C\u673A\u795E\u5FF5\u8FD0\u884C\u65F6\u7ED1\u5B9A\u3002")),e.localAgentMachineId&&e.runtime.machineId&&a.push(r(e.localAgentMachineId===e.runtime.machineId,"same_machine_required","\u7ED1\u5B9A\u7684 Agent \u5FC5\u987B\u4E0E\u5FAE\u4FE1\u8FD0\u884C\u65F6\u5728\u540C\u4E00\u53F0\u673A\u5668\u3002")),a.push(r(e.localAgentAvailable!==!1,"local_agent_unavailable","\u672C\u673A Agent \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u5B89\u5168\u63A5\u7BA1\u5FAE\u4FE1\u56DE\u590D\u3002")),!e.helperReady)a.push(r(!1,"helper_not_ready","\u5E73\u53F0 helper \u672A\u5C31\u7EEA\u3002"));else{const l=c(e.helperReady,e.expectedHelperVersion,e.requiredHelperCapabilities??h(e.platform,e.binding));l.ok?a.push(r(!0,"ok",e.platform==="win32"?"Windows helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002":"macOS helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002")):a.push(r(!1,l.errorCode,l.errorSummary))}const n=e.permissions;n&&(a.push(r(n.screenRecording!==!1,"permission_screen_recording_missing","\u7F3A\u5C11\u5C4F\u5E55\u5F55\u5236\u6743\u9650\u3002")),a.push(r(n.automation!==!1,"permission_automation_missing","\u7F3A\u5C11\u81EA\u52A8\u5316\u63A7\u5236\u6743\u9650\u3002")),a.push(r(n.wechatRunning!==!1,"wechat_not_running","\u5FAE\u4FE1\u672A\u8FD0\u884C\u3002")),a.push(r(n.wechatWindowAvailable!==!1,"wechat_window_unavailable","\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\u3002")),a.push(r(n.wechatMainWindowResponsive!==!1,"wechat_window_unresponsive","\u5FAE\u4FE1\u7A97\u53E3\u65E0\u54CD\u5E94\uFF0C\u9700\u6062\u590D\u540E\u518D\u7EE7\u7EED\u3002")),a.push(r(n.duplicateInstanceBlocked!==!0&&Number(n.duplicateMainWindowCount??0)===0&&Number(n.unsafeAuxiliaryWindowCount??0)===0,"wechat_single_main_window_required","\u8BF7\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u5173\u95ED\u91CD\u590D\u4E3B\u7A97\u53E3\u3001\u89C6\u9891\u9884\u89C8\u6216\u767B\u5F55\u63D0\u793A\u540E\u518D\u542F\u7528\u3002")));const o=a.filter(l=>!l.ok&&l.severity==="blocking");return{ok:o.length===0,canEnable:o.length===0,status:o.length===0?"ready":"blocked",checks:a}}function r(e,a,i){return{ok:e,code:e?"ok":a,severity:"blocking",message:i}}function h(e,a){const i=e==="win32"?t:s;return!a||a.allowReply!==!1?i("send"):a.downloadMedia!==!1?i("download"):i("observe")}export{u as evaluateWeChatChannelPreflight};
1
+ import{requiredWeChatChannelHelperCapabilitiesForProfile as o,requiredWindowsWeChatChannelHelperCapabilitiesForProfile as t,validateWeChatChannelHelperReady as d}from"./helper-protocol.js";function p(e){const a=[],r=e.platform==="darwin"||e.platform==="win32";if(a.push(i(r,"platform_unsupported","\u5FAE\u4FE1 Channel \u5F53\u524D\u652F\u6301 macOS \u548C Windows \u684C\u9762\u5FAE\u4FE1\u3002")),a.push(i(e.accountTier==="enterprise","enterprise_required","\u5F53\u524D\u9636\u6BB5\u5FAE\u4FE1 Channel \u4EC5\u4F01\u4E1A\u7248\u8D26\u53F7\u53EF\u7528\u3002")),a.push(i(!!e.privacyConsentAccepted,"privacy_consent_required","\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1 Channel \u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002")),a.push(i(!!e.serverDecisionAvailable,"server_decision_unavailable","\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u4E0D\u53EF\u7528\uFF0C\u6682\u4E0D\u80FD\u542F\u7528\u3002")),e.currentMachineId&&e.runtime.machineId&&a.push(i(e.currentMachineId===e.runtime.machineId,"same_machine_required","\u7B2C\u4E00\u7248\u53EA\u652F\u6301\u540C\u673A\u5FAE\u4FE1\u4E0E\u540C\u673A\u795E\u5FF5\u8FD0\u884C\u65F6\u7ED1\u5B9A\u3002")),e.localAgentMachineId&&e.runtime.machineId&&a.push(i(e.localAgentMachineId===e.runtime.machineId,"same_machine_required","\u7ED1\u5B9A\u7684 Agent \u5FC5\u987B\u4E0E\u5FAE\u4FE1\u8FD0\u884C\u65F6\u5728\u540C\u4E00\u53F0\u673A\u5668\u3002")),a.push(i(e.localAgentAvailable!==!1,"local_agent_unavailable","\u672C\u673A Agent \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u5B89\u5168\u63A5\u7BA1\u5FAE\u4FE1\u56DE\u590D\u3002")),!e.helperReady)a.push(i(!1,"helper_not_ready","\u5E73\u53F0 helper \u672A\u5C31\u7EEA\u3002"));else{const l=d(e.helperReady,e.expectedHelperVersion,e.requiredHelperCapabilities??c(e.platform,e.binding));l.ok?a.push(i(!0,"ok",e.platform==="win32"?"Windows helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002":"macOS helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002")):a.push(i(!1,l.errorCode,l.errorSummary))}const n=e.permissions;n&&(a.push(i(n.screenRecording!==!1,"permission_screen_recording_missing","\u7F3A\u5C11\u5C4F\u5E55\u5F55\u5236\u6743\u9650\u3002")),a.push(i(n.automation!==!1,"permission_automation_missing","\u7F3A\u5C11\u81EA\u52A8\u5316\u63A7\u5236\u6743\u9650\u3002")),a.push(i(n.wechatRunning!==!1,"wechat_not_running","\u5FAE\u4FE1\u672A\u8FD0\u884C\u3002")),a.push(i(n.wechatWindowAvailable!==!1,"wechat_window_unavailable","\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\u3002")),a.push(i(n.wechatMainWindowResponsive!==!1,"wechat_window_unresponsive","\u5FAE\u4FE1\u7A97\u53E3\u65E0\u54CD\u5E94\uFF0C\u9700\u6062\u590D\u540E\u518D\u7EE7\u7EED\u3002")),a.push(i(n.windowsVisibleDesktopAvailable!==!1&&n.rdpVisibleDesktopAvailable!==!1&&n.desktopSessionVisible!==!1&&n.screenLocked!==!0&&n.rdpDisconnected!==!0,"windows_visible_desktop_unavailable","Windows \u53EF\u89C1\u684C\u9762\u4E0D\u53EF\u7528\uFF0C\u8BF7\u89E3\u9501\u6216\u4FDD\u6301 RDP \u53EF\u89C1\u684C\u9762\u4F1A\u8BDD\u540E\u518D\u542F\u7528\u3002")),a.push(i(n.dpiMappingAvailable!==!1&&n.displayTopologySupported!==!1,"dpi_mapping_failed","Windows DPI / \u591A\u5C4F\u5750\u6807\u6620\u5C04\u4E0D\u53EF\u7528\uFF0C\u8BF7\u628A\u5FAE\u4FE1\u5B8C\u6574\u653E\u5728\u4E00\u4E2A\u53EF\u89C1\u663E\u793A\u5668\u4E0A\u540E\u518D\u542F\u7528\u3002")),a.push(i(n.duplicateInstanceBlocked!==!0&&Number(n.duplicateMainWindowCount??0)===0&&Number(n.unsafeAuxiliaryWindowCount??0)===0,"wechat_single_main_window_required","\u8BF7\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u5173\u95ED\u91CD\u590D\u4E3B\u7A97\u53E3\u3001\u89C6\u9891\u9884\u89C8\u6216\u767B\u5F55\u63D0\u793A\u540E\u518D\u542F\u7528\u3002")));const s=a.filter(l=>!l.ok&&l.severity==="blocking");return{ok:s.length===0,canEnable:s.length===0,status:s.length===0?"ready":"blocked",checks:a}}function i(e,a,r){return{ok:e,code:e?"ok":a,severity:"blocking",message:r}}function c(e,a){const r=e==="win32"?t:o;return!a||a.allowReply!==!1?r("send"):a.downloadMedia!==!1?r("download"):r("observe")}export{p as evaluateWeChatChannelPreflight};
@@ -31,7 +31,6 @@ export declare class WeChatChannelProductRunner {
31
31
  tick(): Promise<WeChatChannelSchedulerTickResult | void>;
32
32
  stop(): Promise<void>;
33
33
  private ensureHelperStarted;
34
- private reportUnrecoverableWindowsWeChat;
35
34
  private shouldSkipObserveForUnchangedFingerprint;
36
35
  }
37
36
  export declare function createWeChatChannelProductRunner(options: WeChatChannelProductRunnerOptions): WeChatChannelProductRunner;
@@ -1 +1 @@
1
- import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as b}from"./client.js";import{WeChatChannelHelperClient as c}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as v,WECHAT_CHANNEL_HELPER_VERSION as y}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as h}from"./helper-protocol.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as p,fallbackWeChatMessageInputPointForWindow as k,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as S,openConversationBySearch as I,recognizeWeChatScreenshot as P}from"./observer.js";import{WeChatChannelScheduler as A}from"./scheduler.js";import{loadWeChatChannelLedger as O}from"./ledger.js";import{buildWeChatConversationFingerprint as F,matchWeChatConversationFingerprints as x}from"./fingerprint.js";import{WeChatChannelOutboundSender as _}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as D,nextWeChatChannelActivityRetryAt as H,normalizeWeChatChannelActivitySnapshot as T}from"./human-coordination.js";class R{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??b();this.helper=e.helper??M(e),this.scheduler=new A({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(t,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 i=D({snapshot:T(o.result),stage:"observe"});return i.ok?{ok:!0}:{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:H(a,i.waitMs)}}},shouldSkipObserve:t=>this.shouldSkipObserveForUnchangedFingerprint(t),outboundSender:new _({helper:this.helper,platform:e.runtime.policy.platform,openConversation:async t=>{const a=await p(this.helper),o=await f(this.helper,a);return{...await I({helper:this.helper,window:o,settleToBottom:!1,binding:{bindingId:`outbound:${t}`,sessionId:"outbound",conversationDisplayName:t,enabled:!0,allowReply:!0,downloadMedia:!0}}),inputPoint:k(o)}}}),recoverWeChat:(t,a)=>this.reportUnrecoverableWindowsWeChat(t,a),observeBinding:(t,a)=>S({runtime:e.runtime,binding:t,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:E(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,t.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,onObservationEvidence:({ocr:o})=>{N({statePath:g(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:t,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(){return await this.ensureHelperStarted(),this.scheduler.tick()}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 reportUnrecoverableWindowsWeChat(e,n){return this.options.runtime.policy.platform!=="win32",!1}async shouldSkipObserveForUnchangedFingerprint(e){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const n=g(this.options.workDir,this.options.runtime.runtimeId),t=u(n,this.options.runtime.runtimeId).bindings[e.bindingId];if(!t?.fingerprint)return{skip:!1};try{const a=await p(this.helper),o=await f(this.helper,a),i=await W(this.helper,o.windowId,void 0,o.bounds),C=await P(this.helper,i),s=w({binding:e,ocr:C,prior:t});return!s?.fingerprint||s.fingerprint!==t.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:s.fingerprint}}catch{return{skip:!1}}}}function ee(r){return new R(r)}function E(r,e,n,t=3){return(O(r,e).bindings[n]?.recent??[]).slice(-t).map(i=>({stableMessageKey:i.stableMessageKey,senderRole:i.senderRole,kind:i.kind,anchorText:i.anchorText??i.normalizedText??i.textExcerpt??null,anchorMetadata:i.anchorMetadata??null}))}function M(r){if(r.helperClientOptions)return new c({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??m(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"});const e=v();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new c({helperPath:e.helperPath,expectedHelperVersion:e.version||y,requiredCapabilities:m(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32"})}function m(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 l.join(r,"wechat-channel",`${e}.ledger.json`)}function g(r,e){return l.join(r,"wechat-channel",`${q(e)}.conversation-fingerprints.json`)}function u(r,e){try{const n=JSON.parse(d.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 L(r,e){d.mkdirSync(l.dirname(r),{recursive:!0}),d.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=w({binding:r.binding,ocr:r.ocr,prior:u(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=u(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()},L(r.statePath,n)}function w(r){const e=r.ocr.visibleConversationFingerprints??[],n=x({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 t=n.item.fingerprint||F(n.item);return t?{item:n.item,fingerprint:t}:null}function q(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{R as WeChatChannelProductRunner,E as buildLocalLedgerTailAnchors,ee as createWeChatChannelProductRunner};
1
+ import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as v}from"./client.js";import{WeChatChannelHelperClient as c}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as y,WECHAT_CHANNEL_HELPER_VERSION as k}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as u}from"./helper-protocol.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as p,fallbackWeChatMessageInputPointForWindow as S,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as I,openConversationBySearch as P,recognizeWeChatScreenshot as A}from"./observer.js";import{WeChatChannelScheduler as O}from"./scheduler.js";import{loadWeChatChannelLedger as F}from"./ledger.js";import{buildWeChatConversationFingerprint as x,matchWeChatConversationFingerprints as _}from"./fingerprint.js";import{WeChatChannelOutboundSender as D}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as H,nextWeChatChannelActivityRetryAt as M,normalizeWeChatChannelActivitySnapshot as T}from"./human-coordination.js";class R{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??v();this.helper=e.helper??L(e),this.scheduler=new O({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(t,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 i=H({snapshot:T(o.result),stage:"observe"});return i.ok?{ok:!0}:{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:M(a,i.waitMs)}}},shouldSkipObserve:(t,a)=>this.shouldSkipObserveForUnchangedFingerprint(t,a.foregroundMode),outboundSender:new D({helper:this.helper,platform:e.runtime.policy.platform,openConversation:async t=>{const a=await p(this.helper),o=await f(this.helper,a);return{...await P({helper:this.helper,window:o,settleToBottom:!1,binding:{bindingId:`outbound:${t}`,sessionId:"outbound",conversationDisplayName:t,enabled:!0,allowReply:!0,downloadMedia:!0}}),inputPoint:S(o)}}}),observeBinding:(t,a)=>I({runtime:e.runtime,binding:t,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:E(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,t.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{N({statePath:g(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:t,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(){return await this.ensureHelperStarted(),this.scheduler.tick()}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 t=g(this.options.workDir,this.options.runtime.runtimeId),a=h(t,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=await p(this.helper,void 0,{foreground:n}),i=n==="background"?o:await f(this.helper,o),C=await W(this.helper,i.windowId,void 0,i.bounds),b=await A(this.helper,C),s=w({binding:e,ocr:b,prior:a});return!s?.fingerprint||s.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:s.fingerprint}}catch{return{skip:!1}}}}function re(r){return new R(r)}function E(r,e,n,t=3){return(F(r,e).bindings[n]?.recent??[]).slice(-t).map(i=>({stableMessageKey:i.stableMessageKey,senderRole:i.senderRole,kind:i.kind,anchorText:i.anchorText??i.normalizedText??i.textExcerpt??null,anchorMetadata:i.anchorMetadata??null}))}function L(r){if(r.helperClientOptions)return new c({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??m(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"});const e=y();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new c({helperPath:e.helperPath,expectedHelperVersion:e.version||k,requiredCapabilities:m(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32"})}function m(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?u("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?u("download"):u("observe")}function U(r,e){return l.join(r,"wechat-channel",`${e}.ledger.json`)}function g(r,e){return l.join(r,"wechat-channel",`${$(e)}.conversation-fingerprints.json`)}function h(r,e){try{const n=JSON.parse(d.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 q(r,e){d.mkdirSync(l.dirname(r),{recursive:!0}),d.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=w({binding:r.binding,ocr:r.ocr,prior:h(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=h(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()},q(r.statePath,n)}function w(r){const e=r.ocr.visibleConversationFingerprints??[],n=_({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 t=n.item.fingerprint||x(n.item);return t?{item:n.item,fingerprint:t}:null}function $(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{R as WeChatChannelProductRunner,E as buildLocalLedgerTailAnchors,re as createWeChatChannelProductRunner};
@@ -3,7 +3,7 @@ import type { WeChatChannelApiClient, WeChatChannelObservedMessage, WeChatChanne
3
3
  import { type WeChatChannelBindingLedger } from './ledger.js';
4
4
  import { type WeChatChannelOutboundRecord } from './outbound-ledger.js';
5
5
  import { type WeChatChannelOutboundSender } from './outbound-sender.js';
6
- import type { WeChatChannelLocalOutboundEchoAnchor } from './observer.js';
6
+ import type { WeChatChannelForegroundMode, WeChatChannelLocalOutboundEchoAnchor } from './observer.js';
7
7
  import type { WeChatChannelHumanActivityReasonCode } from './helper-protocol.js';
8
8
  export type WeChatChannelSchedulerOptions = {
9
9
  runtime: WeChatChannelRuntime;
@@ -13,16 +13,17 @@ export type WeChatChannelSchedulerOptions = {
13
13
  } & Partial<Pick<WeChatChannelApiClient, 'reportOutboundStatus'>>;
14
14
  observeBinding: (binding: WeChatChannelBindingConfig, context?: {
15
15
  localOutboundEchoAnchors?: WeChatChannelLocalOutboundEchoAnchor[];
16
+ foregroundMode?: WeChatChannelForegroundMode;
16
17
  }) => Promise<WeChatChannelObservedMessage[]>;
17
18
  shouldSkipObserve?: (binding: WeChatChannelBindingConfig, context: {
18
19
  ledgerBinding?: WeChatChannelBindingLedger;
19
20
  outboundRecords: WeChatChannelOutboundRecord[];
20
21
  now: Date;
22
+ foregroundMode: WeChatChannelForegroundMode;
21
23
  }) => Promise<WeChatChannelObserveSkipDecision> | WeChatChannelObserveSkipDecision;
22
24
  onInboundMessages?: (binding: WeChatChannelBindingConfig, messages: WeChatChannelObservedMessage[]) => Promise<void> | void;
23
25
  outboundSender?: WeChatChannelOutboundSender;
24
26
  activityGate?: WeChatChannelSchedulerActivityGate;
25
- recoverWeChat?: (binding: WeChatChannelBindingConfig, error: unknown) => Promise<boolean>;
26
27
  ledgerPath?: string;
27
28
  outboundLedgerPath?: string;
28
29
  maxUserActivityWaitMs?: number;
@@ -50,7 +51,7 @@ export type WeChatChannelSchedulerTickResult = {
50
51
  }[];
51
52
  export type WeChatChannelObserveSkipDecision = {
52
53
  skip: true;
53
- reasonCode: 'conversation_fingerprint_unchanged';
54
+ reasonCode: 'conversation_fingerprint_unchanged' | 'conversation_not_visible_background';
54
55
  fingerprint?: string | null;
55
56
  } | {
56
57
  skip: false;
@@ -1 +1 @@
1
- import m from"node:path";import{loadWeChatChannelLedger as C,markWeChatChannelBindingDisabled as R,saveWeChatChannelLedger as w,updateWeChatChannelBindingLedger as I}from"./ledger.js";import{classifyWeChatOutboundEchoes as _,isWeChatOutboundTerminalDeliveryStatus as y,loadWeChatChannelOutboundLedger as S,markWeChatOutboundUserActiveTimeout as O,markWeChatOutboundWaitingUserIdle as W,saveWeChatChannelOutboundLedger as A,shouldFailWeChatOutboundForUserActiveTimeout as E}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as x,isWeChatChannelCooldownActive as M,noteWeChatChannelBillingPause as k,noteWeChatChannelInterruption as L,noteWeChatChannelStableRun as U}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as P}from"./outbound-sender.js";const T=600*1e3;class J{options;timer=null;running=!1;constructor(o){this.options=o}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(){if(!this.running){this.running=!0;try{const o=this.options.ledgerPath||v(this.options.workDir,this.options.runtime.runtimeId),i=this.options.outboundLedgerPath||F(this.options.workDir,this.options.runtime.runtimeId),t=C(o,this.options.runtime.runtimeId),s=S(i,this.options.runtime.runtimeId),u=[],c=new Date;for(const e of this.options.runtime.bindings.filter(d=>!d.enabled))t.bindings[e.bindingId]?.disabledSince||R({ledger:t,bindingId:e.bindingId});for(const e of this.options.runtime.bindings.filter(d=>d.enabled)){await this.options.api.upsertRuntime(this.options.runtime,e);const d=t.bindings[e.bindingId];if(d?.cooldown&&(d.cooldown=x(d.cooldown),M(d.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"cooldown",reasonCode:d.cooldown.manualReviewReason||"user_interruption_cooldown"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d.revision});continue}const b=this.options.activityGate?await this.options.activityGate.canObserve(e,c):{ok:!0};if(b.ok===!1){const r=N({records:s.records,bindingId:e.bindingId,reasonCode:b.reasonCode,nextAttemptAt:b.nextAttemptAt,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??T});await this.reportOutboundProjections(e,[...r.waitingRecords,...r.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_user_active",reasonCode:r.failedRecords[0]?.failureCode||b.reasonCode}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:r.failedRecords.length,waitingOutboundCount:r.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}const p=s.records.filter(r=>r.bindingId===e.bindingId);if(this.options.shouldSkipObserve&&K(d,p)){const r=await this.options.shouldSkipObserve(e,{ledgerBinding:d,outboundRecords:p,now:c});if(r.skip){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"baseline_or_no_change",reasonCode:r.reasonCode}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}}let f;try{f=await this.options.observeBinding(e,{localOutboundEchoAnchors:q(s.records,e.bindingId)})}catch(r){if(D(r)){const g=await this.options.recoverWeChat?.(e,r).catch(()=>!1)??!1;await this.options.api.reportRunStatus(this.options.runtime,e,{status:g?"wechat_recovered_restart_pending_login":"wechat_recovery_failed",reasonCode:"wechat_window_unresponsive",lastErrorSummary:r instanceof Error?r.message:"wechat_window_unresponsive"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(B(r)){const g=d??{bindingId:e.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};g.cooldown=k({state:g.cooldown,reason:"insufficient_credits",now:c}),t.bindings[e.bindingId]=g,await this.options.api.reportRunStatus(this.options.runtime,e,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:r instanceof Error?r.message:"insufficient_credits"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:g.revision});continue}throw r}const h=_({ledger:s,bindingId:e.bindingId,messages:f}),l=I({ledger:t,bindingId:e.bindingId,observedMessages:h.remainingMessages}),a=this.options.outboundSender?await P({ledger:s,bindingId:e.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=U(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,e,{idempotencyKey:`${e.bindingId}:${l.binding.revision}:${$(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(e,l.newMessages)),await this.reportOutboundProjections(e,[...h.confirmedRecords,...h.manualReviewRecords,...a.sentRecords,...a.staleRecords,...a.failedRecords,...a.waitingRecords,...a.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:G(l.newMessages.length,h.confirmedRecords,h.manualReviewRecords,a.sentRecords,a.staleRecords,a.failedRecords,a.waitingRecords,a.manualReviewRecords),reasonCode:a.waitingRecords[0]?.deferReason}),u.push({bindingId:e.bindingId,observedCount:f.length,newInboundCount:l.newMessages.length,outboundSentCount:a.sentRecords.length,staleOutboundCount:a.staleRecords.length,failedOutboundCount:a.failedRecords.length,waitingOutboundCount:a.waitingRecords.length,confirmedEchoCount:h.confirmedRecords.length,manualReviewCount:h.manualReviewRecords.length+a.manualReviewRecords.length,revision:l.binding.revision})}return w(o,t),A(i,s),u}finally{this.running=!1}}}noteInterruption(o,i,t=new Date){const s=this.options.ledgerPath||v(this.options.workDir,this.options.runtime.runtimeId),u=C(s,this.options.runtime.runtimeId),c=u.bindings[o]??{bindingId:o,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=L({state:c.cooldown,reason:i,now:t}),u.bindings[o]=c,w(s,u)}async reportOutboundProjections(o,i){if(this.options.api.reportOutboundStatus)for(const t of i)await this.options.api.reportOutboundStatus(this.options.runtime,o,{replyId:t.replyId,idempotencyKey:t.idempotencyKey,status:t.sendStatus,replyBaseRevision:t.replyBaseRevision,sentAt:t.sentAt,confirmedAt:t.confirmedAt,failureCode:t.failureCode,lastErrorSummary:t.lastErrorSummary,nextAttemptAt:t.nextAttemptAt,attemptCount:t.attemptCount,commitStage:t.commitStage,copyableContentRef:t.copyableContentRef,copyableText:j(t)?t.text:void 0,agentVisible:y(t)?!1:void 0})}}function B(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function D(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function K(n,o){return!n?.baselineEstablished||n.disabledSince||!n.recent.length?!1:!o.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")}function j(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function v(n,o){return m.join(n,"wechat-channel",`${o}.ledger.json`)}function F(n,o){return m.join(n,"wechat-channel",`${o}.outbound-ledger.json`)}function $(n){return n.at(-1)?.stableMessageKey||"empty"}function G(n,o,i,t=[],s=[],u=[],c=[],e=[]){return i.length>0||e.length>0?"manual_review":u.length>0?"outbound_send_failed":s.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":t.length>0?"outbound_sent_unconfirmed":o.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function q(n,o){return n.filter(i=>i.bindingId===o&&i.sendStatus==="sent_unconfirmed").map(i=>({replyId:i.replyId,idempotencyKey:i.idempotencyKey,expectedEchoAnchor:i.expectedEchoAnchor||i.textNormalized||i.text||""}))}function N(n){const o=n.records.filter(s=>s.bindingId===n.bindingId&&s.sendStatus==="queued"),i=[],t=[];for(const s of o)W({record:s,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),E({record:s,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(O(s,n.now),i.push(s)):t.push(s);return{waitingRecords:t,failedRecords:i}}export{J as WeChatChannelScheduler};
1
+ import w from"node:path";import{loadWeChatChannelLedger as C,markWeChatChannelBindingDisabled as I,saveWeChatChannelLedger as v,updateWeChatChannelBindingLedger as S}from"./ledger.js";import{classifyWeChatOutboundEchoes as y,isWeChatOutboundTerminalDeliveryStatus as O,loadWeChatChannelOutboundLedger as E,markWeChatOutboundUserActiveTimeout as W,markWeChatOutboundWaitingUserIdle as A,saveWeChatChannelOutboundLedger as k,shouldFailWeChatOutboundForUserActiveTimeout as x}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as M,isWeChatChannelCooldownActive as U,noteWeChatChannelBillingPause as L,noteWeChatChannelInterruption as P,noteWeChatChannelStableRun as T}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as B}from"./outbound-sender.js";const D=600*1e3;class tn{options;timer=null;running=!1;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(){if(!this.running){this.running=!0;try{const t=this.options.ledgerPath||R(this.options.workDir,this.options.runtime.runtimeId),i=this.options.outboundLedgerPath||N(this.options.workDir,this.options.runtime.runtimeId),o=C(t,this.options.runtime.runtimeId),s=E(i,this.options.runtime.runtimeId),a=[],c=new Date;for(const e of this.options.runtime.bindings.filter(d=>!d.enabled))o.bindings[e.bindingId]?.disabledSince||I({ledger:o,bindingId:e.bindingId});for(const e of this.options.runtime.bindings.filter(d=>d.enabled)){await this.options.api.upsertRuntime(this.options.runtime,e);const d=o.bindings[e.bindingId],f=s.records.filter(r=>r.bindingId===e.bindingId),p=K(this.options.runtime,f);if(d?.cooldown&&(d.cooldown=M(d.cooldown),U(d.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"cooldown",reasonCode:d.cooldown.manualReviewReason||"user_interruption_cooldown"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d.revision});continue}const b=this.options.activityGate?await this.options.activityGate.canObserve(e,c):{ok:!0};if(b.ok===!1){const r=Y({records:s.records,bindingId:e.bindingId,reasonCode:b.reasonCode,nextAttemptAt:b.nextAttemptAt,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??D});await this.reportOutboundProjections(e,[...r.waitingRecords,...r.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_user_active",reasonCode:r.failedRecords[0]?.failureCode||b.reasonCode}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:r.failedRecords.length,waitingOutboundCount:r.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(this.options.shouldSkipObserve&&G(d,f)){const r=await this.options.shouldSkipObserve(e,{ledgerBinding:d,outboundRecords:f,now:c,foregroundMode:p});if(r.skip){await this.options.api.reportRunStatus(this.options.runtime,e,{status:F(r.reasonCode),reasonCode:r.reasonCode}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}}let m;try{m=await this.options.observeBinding(e,{localOutboundEchoAnchors:H(s.records,e.bindingId),foregroundMode:p})}catch(r){if(q(r)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_window_unavailable",reasonCode:_(r,"wechat_window_unavailable"),lastErrorSummary:r instanceof Error?r.message:"wechat_window_unavailable"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if($(r)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"wechat_window_unresponsive",reasonCode:"wechat_window_unresponsive",lastErrorSummary:r instanceof Error?r.message:"wechat_window_unresponsive"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(j(r)){const g=d??{bindingId:e.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};g.cooldown=L({state:g.cooldown,reason:"insufficient_credits",now:c}),o.bindings[e.bindingId]=g,await this.options.api.reportRunStatus(this.options.runtime,e,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:r instanceof Error?r.message:"insufficient_credits"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:g.revision});continue}throw r}const h=y({ledger:s,bindingId:e.bindingId,messages:m}),l=S({ledger:o,bindingId:e.bindingId,observedMessages:h.remainingMessages}),u=this.options.outboundSender?await B({ledger:s,bindingId:e.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=T(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,e,{idempotencyKey:`${e.bindingId}:${l.binding.revision}:${Q(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(e,l.newMessages)),await this.reportOutboundProjections(e,[...h.confirmedRecords,...h.manualReviewRecords,...u.sentRecords,...u.staleRecords,...u.failedRecords,...u.waitingRecords,...u.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:V(l.newMessages.length,h.confirmedRecords,h.manualReviewRecords,u.sentRecords,u.staleRecords,u.failedRecords,u.waitingRecords,u.manualReviewRecords),reasonCode:u.waitingRecords[0]?.deferReason}),a.push({bindingId:e.bindingId,observedCount:m.length,newInboundCount:l.newMessages.length,outboundSentCount:u.sentRecords.length,staleOutboundCount:u.staleRecords.length,failedOutboundCount:u.failedRecords.length,waitingOutboundCount:u.waitingRecords.length,confirmedEchoCount:h.confirmedRecords.length,manualReviewCount:h.manualReviewRecords.length+u.manualReviewRecords.length,revision:l.binding.revision})}return v(t,o),k(i,s),a}finally{this.running=!1}}}noteInterruption(t,i,o=new Date){const s=this.options.ledgerPath||R(this.options.workDir,this.options.runtime.runtimeId),a=C(s,this.options.runtime.runtimeId),c=a.bindings[t]??{bindingId:t,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=P({state:c.cooldown,reason:i,now:o}),a.bindings[t]=c,v(s,a)}async reportOutboundProjections(t,i){if(this.options.api.reportOutboundStatus)for(const o of i)await this.options.api.reportOutboundStatus(this.options.runtime,t,{replyId:o.replyId,idempotencyKey:o.idempotencyKey,status:o.sendStatus,replyBaseRevision:o.replyBaseRevision,sentAt:o.sentAt,confirmedAt:o.confirmedAt,failureCode:o.failureCode,lastErrorSummary:o.lastErrorSummary,nextAttemptAt:o.nextAttemptAt,attemptCount:o.attemptCount,commitStage:o.commitStage,copyableContentRef:o.copyableContentRef,copyableText:z(o)?o.text:void 0,agentVisible:O(o)?!1:void 0})}}function j(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function K(n,t){return n.foregroundPolicy==="work"||t.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")?"required":"background"}function F(n){return n==="conversation_not_visible_background"?"observe_deferred_window_unavailable":"baseline_or_no_change"}function q(n){const t=_(n);return t==="wechat_window_unavailable"||t==="wechat_window_not_found"||t==="conversation_not_visible_background"||t==="wechat_window_obstructed_background"}function _(n,t="unknown_error"){if(n&&typeof n=="object"){const s=n.reasonCode??n.errorCode;if(typeof s=="string"&&s.trim())return s.trim()}return(n instanceof Error?n.message:String(n||"")).match(/^([a-z0-9_./-]+)(?::|\b)/i)?.[1]||t}function $(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function G(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 z(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function R(n,t){return w.join(n,"wechat-channel",`${t}.ledger.json`)}function N(n,t){return w.join(n,"wechat-channel",`${t}.outbound-ledger.json`)}function Q(n){return n.at(-1)?.stableMessageKey||"empty"}function V(n,t,i,o=[],s=[],a=[],c=[],e=[]){return i.length>0||e.length>0?"manual_review":a.length>0?"outbound_send_failed":s.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":o.length>0?"outbound_sent_unconfirmed":t.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function H(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 Y(n){const t=n.records.filter(s=>s.bindingId===n.bindingId&&s.sendStatus==="queued"),i=[],o=[];for(const s of t)A({record:s,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),x({record:s,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(W(s,n.now),i.push(s)):o.push(s);return{waitingRecords:o,failedRecords:i}}export{tn as WeChatChannelScheduler};
@@ -1,5 +1,6 @@
1
1
  import type { ExternalChannelAdapter, ExternalChannelConfig, ExternalChannelRuntimeStatus, ExternalChannelSendResult, ExternalMessageEvent, ExternalReply } from './base.js';
2
2
  import { type WeChatRpaObservedMessage } from './wechat-rpa/normalizer.js';
3
+ import { type WeChatRpaProductRunnerFactory } from './wechat-rpa/product-channel.js';
3
4
  type WeChatRpaSecret = {
4
5
  type: 'wechat-rpa';
5
6
  source?: 'wechat-channel' | 'macos-probe' | 'macos-flow' | 'windows-visual-flow' | 'wechat-rpa-lab' | 'fixture-jsonl';
@@ -27,10 +28,13 @@ type WeChatRpaEvent = ExternalMessageEvent & {
27
28
  };
28
29
  export declare class WeChatRpaChannelAdapter implements ExternalChannelAdapter {
29
30
  private onMessage?;
31
+ private options;
30
32
  readonly type: "wechat-rpa";
31
33
  private secrets;
32
34
  private connections;
33
- constructor(onMessage?: ((event: WeChatRpaEvent) => ExternalMessageEvent | void) | undefined);
35
+ constructor(onMessage?: ((event: WeChatRpaEvent) => ExternalMessageEvent | void) | undefined, options?: {
36
+ createProductRunner?: WeChatRpaProductRunnerFactory;
37
+ });
34
38
  connect(config: ExternalChannelConfig): Promise<void>;
35
39
  disconnect(config: ExternalChannelConfig): Promise<void>;
36
40
  syncNow(config: ExternalChannelConfig): Promise<ExternalMessageEvent[]>;