shennian 0.2.128 → 0.2.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +10 -10
- package/dist/src/channels/runtime.js +2 -2
- package/dist/src/channels/wechat-channel/client.d.ts +23 -0
- package/dist/src/channels/wechat-channel/client.js +1 -1
- package/dist/src/channels/wechat-channel/observer.js +1 -1
- package/dist/src/channels/wechat-rpa-session-sync.d.ts +2 -0
- package/dist/src/channels/wechat-rpa-session-sync.js +1 -1
- package/dist/src/manager/runtime.d.ts +2 -0
- package/dist/src/manager/runtime.js +12 -12
- package/package.json +1 -1
|
@@ -156,8 +156,8 @@
|
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
"file": "src/channels/runtime.js",
|
|
159
|
-
"beforeBytes":
|
|
160
|
-
"afterBytes":
|
|
159
|
+
"beforeBytes": 33833,
|
|
160
|
+
"afterBytes": 18029
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"file": "src/channels/secret-registry.js",
|
|
@@ -181,8 +181,8 @@
|
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
183
|
"file": "src/channels/wechat-channel/client.js",
|
|
184
|
-
"beforeBytes":
|
|
185
|
-
"afterBytes":
|
|
184
|
+
"beforeBytes": 8656,
|
|
185
|
+
"afterBytes": 4761
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
"file": "src/channels/wechat-channel/cooldown.js",
|
|
@@ -261,8 +261,8 @@
|
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"file": "src/channels/wechat-channel/observer.js",
|
|
264
|
-
"beforeBytes":
|
|
265
|
-
"afterBytes":
|
|
264
|
+
"beforeBytes": 111237,
|
|
265
|
+
"afterBytes": 50157
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
"file": "src/channels/wechat-channel/outbound-ledger.js",
|
|
@@ -326,8 +326,8 @@
|
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
328
|
"file": "src/channels/wechat-rpa-session-sync.js",
|
|
329
|
-
"beforeBytes":
|
|
330
|
-
"afterBytes":
|
|
329
|
+
"beforeBytes": 6696,
|
|
330
|
+
"afterBytes": 2520
|
|
331
331
|
},
|
|
332
332
|
{
|
|
333
333
|
"file": "src/channels/wechat-rpa.js",
|
|
@@ -526,8 +526,8 @@
|
|
|
526
526
|
},
|
|
527
527
|
{
|
|
528
528
|
"file": "src/manager/runtime.js",
|
|
529
|
-
"beforeBytes":
|
|
530
|
-
"afterBytes":
|
|
529
|
+
"beforeBytes": 68071,
|
|
530
|
+
"afterBytes": 34702
|
|
531
531
|
},
|
|
532
532
|
{
|
|
533
533
|
"file": "src/native-fusion/config.js",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import M from"node:crypto";import u from"node:fs";import h from"node:path";import{ChannelConfigRegistry as P}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as B}from"./wecom.js";import{WeChatRpaChannelAdapter as D}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as W}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new P;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,n={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new B(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(o.type,o);const a=new x(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(a.type,a);const i=new D(r=>this.ingest(r),{createProductRunner:n.createWeChatRpaProductRunner,automationLane:n.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const n=this.configs.get(e.channelId),o=e.managerSessionId??n?.sessionId??n?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const a=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:a};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=F(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let a=!1;for(const i of o){const r=i.idempotencyKey;if(r&&this.isReplyCompleted(t,e.conversationId,r))continue;const d=await n.send(t,{...e,...i});if(d?.status==="queued"){a=!0;continue}if(d?.status==="manual-review")return{ok:!1,error:d.reason||"Reply requires manual review before retry"};r&&this.markReplyCompleted(t,e.conversationId,r)}return a?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,n){return this.loadReplyCompletionSet(e).has(A(e.id,t,n))}markReplyCompleted(e,t,n){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,n));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=h.resolve(e.workDir),n=this.completedReplyKeys.get(t);if(n)return n;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await n.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,n={}){const o=this.configs.list().filter(d=>(d.sessionId??d.managerSessionId)===e&&d.type===t),a=o.find(d=>d.enabled)??o.at(-1);if(!a)return null;const i=this.secrets.get(a.secretRef),r=this.adapters.get(a.type)?.runtimeStatus?.(a)??{};return{id:a.id,type:a.type,name:a.name,sessionId:a.sessionId??a.managerSessionId,managerSessionId:a.managerSessionId,workDir:a.workDir,agentType:a.agentType,agentSessionId:a.agentSessionId,modelId:a.modelId,enabled:a.enabled,wsUrl:i?.wsUrl??"",token:n.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...C(i),...r}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),a=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...C(o),...a,managedBy:n.managedBy}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,a=await n.syncNow(t)??[],i=K(a,this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:a,recentMessages:i}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await n.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const a=Date.parse(o.receivedAt);return!Number.isFinite(a)||a>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,a={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(a.secretRef),r=e.wsUrl?.trim()||i?.wsUrl||"",d=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(a.enabled&&(!r||!d))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(l=>l.id!==a.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(a),this.configs.replaceAll(f),(r||d)&&this.secrets.upsert(a.secretRef,{type:"websocket",wsUrl:r,token:d,canReply:p,systemPrompt:m});const c=this.adapters.get(a.type);for(const l of n)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return a.enabled&&c?.connect(a).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,a=g(e.groups);if(e.enabled&&!a.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&a.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");if(e.enabled){const c=this.findConflictingWeChatRpaBinding({excludeChannelId:e.id,boundSessionId:o,groupNames:a.map(l=>l.name)});if(c)throw new Error(`\u5FAE\u4FE1\u5BF9\u8BDD\u300C${c.groupName}\u300D\u5DF2\u88AB\u53E6\u4E00\u4E2A\u667A\u80FD\u4F53\u7ED1\u5B9A(\u901A\u9053 ${c.channelName}),\u540C\u4E00\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u6B21\u3002\u8BF7\u5148\u5728\u539F\u667A\u80FD\u4F53\u89E3\u7ED1,\u518D\u91CD\u65B0\u7ED1\u5B9A\u3002`)}const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`,managedBy:e.managedBy??t?.managedBy},r=this.secrets.get(i.secretRef),d=e.source||(r?.source==="wechat-channel"||r?.source==="macos-probe"||r?.source==="fixture-jsonl"||r?.source==="macos-flow"||r?.source==="windows-visual-flow"||r?.source==="wechat-rpa-lab"?r.source:E());if(e.enabled&&d==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!r?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=n.filter(c=>c.id!==i.id).map(c=>(c.sessionId??c.managerSessionId)===o&&c.type==="wechat-rpa"?{...c,enabled:!1}:c);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:d,groups:a,pollIntervalMs:R(e.pollIntervalMs,r?.pollIntervalMs),recentLimit:R(e.recentLimit,r?.recentLimit),idleSeconds:R(e.idleSeconds,r?.idleSeconds),forceForeground:e.forceForeground??(r?.forceForeground===void 0?!0:!!r.forceForeground),noRestore:e.noRestore??(r?.noRestore===void 0?!0:!!r.noRestore),downloadAttachments:e.downloadAttachments??(r?.downloadAttachments===void 0?!0:!!r.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(r?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(r?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(r?.flowScriptPath),canReply:e.canReply??r?.canReply??!1,systemPrompt:e.systemPrompt??(typeof r?.systemPrompt=="string"?r.systemPrompt:"")});const f=this.adapters.get(i.type);for(const c of n)(c.sessionId??c.managerSessionId)===o&&c.type==="wechat-rpa"&&c.enabled&&await this.adapters.get(c.type)?.disconnect(c).catch(()=>{});return i.enabled&&f?.connect(i).catch(c=>{console.error(`[wechat-rpa] connect failed id=${i.id}: ${c instanceof Error?c.message:String(c)}`)}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}listChannelIdsManagedBy(e){return this.configs.list().filter(t=>t.managedBy===e).map(t=>t.id)}async deleteManagerWeChatRpaChannel(e){const t=this.configs.get(e);if(!t)return{removed:!1};t.enabled&&await this.adapters.get(t.type)?.disconnect(t).catch(()=>{});const n=this.configs.remove(e);return t.secretRef&&this.secrets.remove(t.secretRef),{removed:n}}findConflictingWeChatRpaBinding(e){const t=new Set(e.groupNames);if(!t.size)return null;for(const n of this.configs.list()){if(n.type!=="wechat-rpa"||!n.enabled||n.id===e.excludeChannelId||(n.sessionId??n.managerSessionId)===e.boundSessionId)continue;const o=this.secrets.get(n.secretRef),a=g(Array.isArray(o?.groups)?o.groups:[]);for(const i of a)if(t.has(i.name))return{channelId:n.id,channelName:n.name||n.id,groupName:i.name}}return null}}function y(s,e){return!e||e.type!==s.type?!1:s.type==="wechat-rpa"?!0:s.type==="websocket"?!!(e.wsUrl&&e.token):s.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function C(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:"",wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):void 0,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):void 0,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):void 0,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?void 0:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:"",selfNickname:typeof s.selfNickname=="string"?s.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function w(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:null,wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):null,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):null,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):null,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?null:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:null,selfNickname:typeof s.selfNickname=="string"?s.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function g(s){const e=new Set,t=[];for(const n of s){const o=String(n?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function k(s){const e=[];if(s.lastHelperPermissions&&typeof s.lastHelperPermissions=="object"){const t=s.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:s.source!=="windows-visual-flow",severity:"blocking",message:s.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!s.privacyConsentAccepted,severity:"blocking",message:s.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function F(s,e){const t=W(e.text);if(!t.length&&!e.attachment)return[];if(s==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const n=t.map((o,a)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${a+1}`:e.idempotencyKey}));return e.attachment&&n.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),n}function K(...s){const e=new Set,t=[];for(const n of s.flat()){const o=I(n);if(!o){t.push(n);continue}e.has(o)||(e.add(o),t.push(n))}return t}function I(s){const e=S(s.channelId),t=S(s.conversationId),n=S(s.messageId);return!e||!t||!n?null:`${e}
|
|
1
|
+
import M from"node:crypto";import h from"node:fs";import u from"node:path";import{ChannelConfigRegistry as P}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as B}from"./wecom.js";import{WeChatRpaChannelAdapter as D}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as W}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new P;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,n={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new B(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(o.type,o);const a=new x(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(a.type,a);const i=new D(r=>this.ingest(r),{createProductRunner:n.createWeChatRpaProductRunner,automationLane:n.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const n=this.configs.get(e.channelId),o=e.managerSessionId??n?.sessionId??n?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const a=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:a};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=F(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let a=!1;for(const i of o){const r=i.idempotencyKey;if(r&&this.isReplyCompleted(t,e.conversationId,r))continue;const l=await n.send(t,{...e,...i});if(l?.status==="queued"){a=!0;continue}if(l?.status==="manual-review")return{ok:!1,error:l.reason||"Reply requires manual review before retry"};r&&this.markReplyCompleted(t,e.conversationId,r)}return a?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,n){return this.loadReplyCompletionSet(e).has(A(e.id,t,n))}markReplyCompleted(e,t,n){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,n));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),n=this.completedReplyKeys.get(t);if(n)return n;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await n.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,n={}){const o=this.configs.list().filter(l=>(l.sessionId??l.managerSessionId)===e&&l.type===t),a=o.find(l=>l.enabled)??o.at(-1);if(!a)return null;const i=this.secrets.get(a.secretRef),r=this.adapters.get(a.type)?.runtimeStatus?.(a)??{};return{id:a.id,type:a.type,name:a.name,sessionId:a.sessionId??a.managerSessionId,managerSessionId:a.managerSessionId,workDir:a.workDir,agentType:a.agentType,agentSessionId:a.agentSessionId,modelId:a.modelId,enabled:a.enabled,wsUrl:i?.wsUrl??"",token:n.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...C(i),...r}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),a=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...C(o),...a,managedBy:n.managedBy}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,a=await n.syncNow(t)??[],i=K(a,this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:a,recentMessages:i}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await n.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const a=Date.parse(o.receivedAt);return!Number.isFinite(a)||a>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,a={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(a.secretRef),r=e.wsUrl?.trim()||i?.wsUrl||"",l=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(a.enabled&&(!r||!l))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(d=>d.id!==a.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type===e.type?{...d,enabled:!1}:d);f.push(a),this.configs.replaceAll(f),(r||l)&&this.secrets.upsert(a.secretRef,{type:"websocket",wsUrl:r,token:l,canReply:p,systemPrompt:m});const c=this.adapters.get(a.type);for(const d of n)(d.sessionId??d.managerSessionId)===o&&d.type===e.type&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return a.enabled&&c?.connect(a).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),n=e.sessionId||e.managerSessionId,o=g(e.groups);if(e.enabled&&!o.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&o.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");if(e.enabled){const c=this.findConflictingWeChatRpaBinding({excludeChannelId:e.id,boundSessionId:n,groupNames:o.map(d=>d.name)});if(c){const d=this.configs.get(c.channelId);if(e.managedBy==="session-sync"&&d?.managedBy==="session-sync")await this.deleteManagerWeChatRpaChannel(c.channelId);else throw new Error(`\u5FAE\u4FE1\u5BF9\u8BDD\u300C${c.groupName}\u300D\u5DF2\u88AB\u53E6\u4E00\u4E2A\u667A\u80FD\u4F53\u7ED1\u5B9A(\u901A\u9053 ${c.channelName}),\u540C\u4E00\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u6B21\u3002\u8BF7\u5148\u5728\u539F\u667A\u80FD\u4F53\u89E3\u7ED1,\u518D\u91CD\u65B0\u7ED1\u5B9A\u3002`)}}const a=this.configs.list(),i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:n,managerSessionId:n,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`,managedBy:e.managedBy??t?.managedBy},r=this.secrets.get(i.secretRef),l=e.source||(r?.source==="wechat-channel"||r?.source==="macos-probe"||r?.source==="fixture-jsonl"||r?.source==="macos-flow"||r?.source==="windows-visual-flow"||r?.source==="wechat-rpa-lab"?r.source:E());if(e.enabled&&l==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!r?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=a.filter(c=>c.id!==i.id).map(c=>(c.sessionId??c.managerSessionId)===n&&c.type==="wechat-rpa"?{...c,enabled:!1}:c);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:l,groups:o,pollIntervalMs:R(e.pollIntervalMs,r?.pollIntervalMs),recentLimit:R(e.recentLimit,r?.recentLimit),idleSeconds:R(e.idleSeconds,r?.idleSeconds),forceForeground:e.forceForeground??(r?.forceForeground===void 0?!0:!!r.forceForeground),noRestore:e.noRestore??(r?.noRestore===void 0?!0:!!r.noRestore),downloadAttachments:e.downloadAttachments??(r?.downloadAttachments===void 0?!0:!!r.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(r?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(r?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(r?.flowScriptPath),canReply:e.canReply??r?.canReply??!1,systemPrompt:e.systemPrompt??(typeof r?.systemPrompt=="string"?r.systemPrompt:"")});const f=this.adapters.get(i.type);for(const c of a)(c.sessionId??c.managerSessionId)===n&&c.type==="wechat-rpa"&&c.enabled&&await this.adapters.get(c.type)?.disconnect(c).catch(()=>{});return i.enabled&&f?.connect(i).catch(c=>{console.error(`[wechat-rpa] connect failed id=${i.id}: ${c instanceof Error?c.message:String(c)}`)}),this.getManagerChannel(n,"wechat-rpa",{includeSecret:!0})}listChannelIdsManagedBy(e){return this.configs.list().filter(t=>t.managedBy===e).map(t=>t.id)}async deleteManagerWeChatRpaChannel(e){const t=this.configs.get(e);if(!t)return{removed:!1};t.enabled&&await this.adapters.get(t.type)?.disconnect(t).catch(()=>{});const n=this.configs.remove(e);return t.secretRef&&this.secrets.remove(t.secretRef),{removed:n}}findConflictingWeChatRpaBinding(e){const t=new Set(e.groupNames);if(!t.size)return null;for(const n of this.configs.list()){if(n.type!=="wechat-rpa"||!n.enabled||n.id===e.excludeChannelId||(n.sessionId??n.managerSessionId)===e.boundSessionId)continue;const o=this.secrets.get(n.secretRef),a=g(Array.isArray(o?.groups)?o.groups:[]);for(const i of a)if(t.has(i.name))return{channelId:n.id,channelName:n.name||n.id,groupName:i.name}}return null}}function y(s,e){return!e||e.type!==s.type?!1:s.type==="wechat-rpa"?!0:s.type==="websocket"?!!(e.wsUrl&&e.token):s.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function C(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:"",wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):void 0,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):void 0,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):void 0,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?void 0:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:"",selfNickname:typeof s.selfNickname=="string"?s.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function w(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:null,wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):null,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):null,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):null,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?null:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:null,selfNickname:typeof s.selfNickname=="string"?s.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function g(s){const e=new Set,t=[];for(const n of s){const o=String(n?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function k(s){const e=[];if(s.lastHelperPermissions&&typeof s.lastHelperPermissions=="object"){const t=s.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:s.source!=="windows-visual-flow",severity:"blocking",message:s.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!s.privacyConsentAccepted,severity:"blocking",message:s.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function F(s,e){const t=W(e.text);if(!t.length&&!e.attachment)return[];if(s==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const n=t.map((o,a)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${a+1}`:e.idempotencyKey}));return e.attachment&&n.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),n}function K(...s){const e=new Set,t=[];for(const n of s.flat()){const o=I(n);if(!o){t.push(n);continue}e.has(o)||(e.add(o),t.push(n))}return t}function I(s){const e=S(s.channelId),t=S(s.conversationId),n=S(s.messageId);return!e||!t||!n?null:`${e}
|
|
2
2
|
${t}
|
|
3
3
|
${n}`}function S(s){return typeof s=="string"?s.trim():""}function A(s,e,t){return M.createHash("sha256").update(`${s}
|
|
4
4
|
${e}
|
|
5
|
-
${t}`).digest("hex").slice(0,32)}function v(s){return
|
|
5
|
+
${t}`).digest("hex").slice(0,32)}function v(s){return u.join(s,".shennian","external-reply-idempotency.json")}function T(s){try{const e=JSON.parse(h.readFileSync(v(s),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&typeof n.key=="string"?n.key:"").filter(Boolean))}catch{return new Set}}function $(s,e){const t=v(s);h.mkdirSync(u.dirname(t),{recursive:!0});const n=Array.from(e).slice(-500);h.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:n.map(o=>({key:o}))},null,2)),e.clear();for(const o of n)e.add(o)}function R(s,e){const t=Number(s??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(s){return typeof s=="string"&&s.trim()?s.trim():void 0}export{J as ChannelRuntime,F as planExternalReplySends};
|
|
@@ -203,6 +203,26 @@ export type WeChatChannelBindingResponse = {
|
|
|
203
203
|
updatedAt?: string | null;
|
|
204
204
|
} | null;
|
|
205
205
|
};
|
|
206
|
+
export type WeChatChannelBindingsResponse = {
|
|
207
|
+
ok: true;
|
|
208
|
+
machineId: string;
|
|
209
|
+
bindings: Array<{
|
|
210
|
+
id: string;
|
|
211
|
+
runtimeId?: string | null;
|
|
212
|
+
machineId: string;
|
|
213
|
+
sessionId: string;
|
|
214
|
+
conversationName: string;
|
|
215
|
+
enabled: boolean;
|
|
216
|
+
allowReply?: boolean;
|
|
217
|
+
downloadMedia?: boolean;
|
|
218
|
+
updatedAt?: string | null;
|
|
219
|
+
sessionTitle?: string | null;
|
|
220
|
+
sessionWorkDir?: string | null;
|
|
221
|
+
sessionAgentType?: string | null;
|
|
222
|
+
sessionAgentSessionId?: string | null;
|
|
223
|
+
sessionModelId?: string | null;
|
|
224
|
+
}>;
|
|
225
|
+
};
|
|
206
226
|
export type WeChatChannelOutboundStatusInput = {
|
|
207
227
|
replyId: string;
|
|
208
228
|
idempotencyKey: string;
|
|
@@ -242,6 +262,9 @@ export declare function createWeChatChannelApiClient(options?: WeChatChannelApiC
|
|
|
242
262
|
machineId?: string | null;
|
|
243
263
|
sessionId: string;
|
|
244
264
|
}) => Promise<WeChatChannelBindingResponse>;
|
|
265
|
+
listBindings: (input?: {
|
|
266
|
+
machineId?: string | null;
|
|
267
|
+
}) => Promise<WeChatChannelBindingsResponse>;
|
|
245
268
|
upsertRuntime: (runtime: WeChatChannelRuntime, binding?: WeChatChannelBindingConfig, input?: {
|
|
246
269
|
preflight?: WeChatChannelRuntimePreflightInput;
|
|
247
270
|
helperVersion?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SERVERS as f}from"../../region.js";import{loadConfig as y}from"../../config/index.js";class h extends Error{reasonCode;statusCode;details;constructor(o,d,c,I){super(o),this.reasonCode=d,this.statusCode=c,this.details=I,this.name="WeChatChannelApiError"}}function A(r={}){const o=y(),d=v(r.serverUrl||o.serverUrl||f.cn.url),c=r.machineToken||o.machineToken,I=r.fetchImpl||fetch;if(!c)throw new Error("WeChat channel requires a paired machine token");async function t(s,n){const e=C(n);let a;try{a=await I(`${d}/api/channels/wechat${s}`,{method:n===void 0?"GET":"POST",headers:{authorization:`Bearer ${c}`,...n===void 0?{}:{"content-type":"application/json"},...e?{"x-trace-id":e}:{}},body:n===void 0?void 0:JSON.stringify(n)})}catch(i){const u=i instanceof Error?i.message:String(i||"network request failed");throw new h(`WeChat channel API ${s} network failed: ${u}`,"wechat_channel_network_failed",0,{path:s,traceId:e||null,cause:u})}const m=await a.text();let l=null;try{l=m?JSON.parse(m):null}catch{throw new h(`WeChat channel API ${s} returned invalid JSON`,"wechat_channel_invalid_response",a.status,{path:s,traceId:e||null,text:m.slice(0,200)})}if(!a.ok||l?.ok===!1){const i=w(l?.reasonCode)||a.statusText||"request_failed";throw new h(`WeChat channel API ${s} failed: ${i}`,i,a.status,{path:s,traceId:e||null,statusCode:a.status})}return l}return{getRuntimePolicy:()=>t("/runtime-policy"),getBinding:s=>{const n=new URLSearchParams;return s.machineId&&n.set("machineId",s.machineId),n.set("sessionId",s.sessionId),t(`/binding?${n}`)},upsertRuntime:(s,n,e)=>t("/runtime",{runtimeId:s.runtimeId,machineId:s.machineId,pollIntervalSeconds:Math.round(s.policy.pollIntervalMs/1e3),foregroundPolicy:s.foregroundPolicy,clientRuntimeVersion:e?.clientRuntimeVersion??String(s.policy.runtimeVersion),helperVersion:e?.helperVersion,preflight:e?.preflight,...n?g(n):{}}),observe:(s,n,e)=>t("/observe",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),structureWindow:(s,n,e)=>t("/structure-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),embedVisual:(s,n,e)=>t("/embed-visual",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots??[],visualBlocks:e.visualBlocks,traceId:e.traceId}),classifyWindow:(s,n,e)=>t("/classify-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,screenshot:e.screenshot,traceId:e.traceId}),ingest:(s,n,e)=>t("/ingest",{runtimeId:s.runtimeId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,messages:e.messages}),reportOutboundStatus:(s,n,e)=>t("/outbound-status",{runtimeId:s.runtimeId,replyId:e.replyId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,replyBaseRevision:e.replyBaseRevision,sentAt:e.sentAt,confirmedAt:e.confirmedAt,failureCode:e.failureCode,lastErrorSummary:e.lastErrorSummary,nextAttemptAt:e.nextAttemptAt,attemptCount:e.attemptCount,commitStage:e.commitStage,copyableContentRef:e.copyableContentRef,copyableText:e.copyableText,agentVisible:e.agentVisible}),reportRunStatus:(s,n,e)=>t("/run-status",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,reasonCode:e.reasonCode,traceId:e.traceId,lastErrorSummary:e.lastErrorSummary,preflight:e.preflight})}}function g(r){return{bindingId:r.bindingId,sessionId:r.sessionId,conversationName:r.conversationDisplayName,enabled:r.enabled,allowReply:r.allowReply,downloadMedia:r.downloadMedia}}function v(r){return r.replace(/\/+$/,"")}function C(r){if(!r||typeof r!="object")return;const o=r.traceId;return(typeof o=="string"?o.trim():"")||void 0}function w(r){return typeof r=="string"?r.trim():""}export{h as WeChatChannelApiError,A as createWeChatChannelApiClient};
|
|
1
|
+
import{SERVERS as f}from"../../region.js";import{loadConfig as y}from"../../config/index.js";class h extends Error{reasonCode;statusCode;details;constructor(o,d,c,I){super(o),this.reasonCode=d,this.statusCode=c,this.details=I,this.name="WeChatChannelApiError"}}function A(r={}){const o=y(),d=v(r.serverUrl||o.serverUrl||f.cn.url),c=r.machineToken||o.machineToken,I=r.fetchImpl||fetch;if(!c)throw new Error("WeChat channel requires a paired machine token");async function t(s,n){const e=C(n);let a;try{a=await I(`${d}/api/channels/wechat${s}`,{method:n===void 0?"GET":"POST",headers:{authorization:`Bearer ${c}`,...n===void 0?{}:{"content-type":"application/json"},...e?{"x-trace-id":e}:{}},body:n===void 0?void 0:JSON.stringify(n)})}catch(i){const u=i instanceof Error?i.message:String(i||"network request failed");throw new h(`WeChat channel API ${s} network failed: ${u}`,"wechat_channel_network_failed",0,{path:s,traceId:e||null,cause:u})}const m=await a.text();let l=null;try{l=m?JSON.parse(m):null}catch{throw new h(`WeChat channel API ${s} returned invalid JSON`,"wechat_channel_invalid_response",a.status,{path:s,traceId:e||null,text:m.slice(0,200)})}if(!a.ok||l?.ok===!1){const i=w(l?.reasonCode)||a.statusText||"request_failed";throw new h(`WeChat channel API ${s} failed: ${i}`,i,a.status,{path:s,traceId:e||null,statusCode:a.status})}return l}return{getRuntimePolicy:()=>t("/runtime-policy"),getBinding:s=>{const n=new URLSearchParams;return s.machineId&&n.set("machineId",s.machineId),n.set("sessionId",s.sessionId),t(`/binding?${n}`)},listBindings:(s={})=>{const n=new URLSearchParams;s.machineId&&n.set("machineId",s.machineId);const e=n.toString()?`?${n}`:"";return t(`/bindings${e}`)},upsertRuntime:(s,n,e)=>t("/runtime",{runtimeId:s.runtimeId,machineId:s.machineId,pollIntervalSeconds:Math.round(s.policy.pollIntervalMs/1e3),foregroundPolicy:s.foregroundPolicy,clientRuntimeVersion:e?.clientRuntimeVersion??String(s.policy.runtimeVersion),helperVersion:e?.helperVersion,preflight:e?.preflight,...n?g(n):{}}),observe:(s,n,e)=>t("/observe",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),structureWindow:(s,n,e)=>t("/structure-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),embedVisual:(s,n,e)=>t("/embed-visual",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots??[],visualBlocks:e.visualBlocks,traceId:e.traceId}),classifyWindow:(s,n,e)=>t("/classify-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,screenshot:e.screenshot,traceId:e.traceId}),ingest:(s,n,e)=>t("/ingest",{runtimeId:s.runtimeId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,messages:e.messages}),reportOutboundStatus:(s,n,e)=>t("/outbound-status",{runtimeId:s.runtimeId,replyId:e.replyId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,replyBaseRevision:e.replyBaseRevision,sentAt:e.sentAt,confirmedAt:e.confirmedAt,failureCode:e.failureCode,lastErrorSummary:e.lastErrorSummary,nextAttemptAt:e.nextAttemptAt,attemptCount:e.attemptCount,commitStage:e.commitStage,copyableContentRef:e.copyableContentRef,copyableText:e.copyableText,agentVisible:e.agentVisible}),reportRunStatus:(s,n,e)=>t("/run-status",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,reasonCode:e.reasonCode,traceId:e.traceId,lastErrorSummary:e.lastErrorSummary,preflight:e.preflight})}}function g(r){return{bindingId:r.bindingId,sessionId:r.sessionId,conversationName:r.conversationDisplayName,enabled:r.enabled,allowReply:r.allowReply,downloadMedia:r.downloadMedia}}function v(r){return r.replace(/\/+$/,"")}function C(r){if(!r||typeof r!="object")return;const o=r.traceId;return(typeof o=="string"?o.trim():"")||void 0}function w(r){return typeof r=="string"?r.trim():""}export{h as WeChatChannelApiError,A as createWeChatChannelApiClient};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import W from"node:fs";import J from"node:path";import{WeChatChannelApiError as De}from"./client.js";import{defaultWeChatChannelAttachmentDir as Le,resolveVisibleWeChatChannelMedia as Ke}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as $e,loadWeChatChannelVectorStore as ze,saveWeChatChannelVectorStore as Ve,upsertWeChatChannelVectorReferences as qe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as Z,normalizeWeChatChannelMessageKind as je}from"./core/schema.js";import{waitForWeChatChannelPacing as Q,weChatChannelPacingDelayMs as Ge}from"./pacing.js";import{matchWeChatConversationFingerprints as He}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as ee}from"./anchor.js";import{decideWeChatChannelActivityGate as Ue,normalizeWeChatChannelActivitySnapshot as Ye}from"./human-coordination.js";const Xe=3,Je=5,Ze="wechat_login_required",Qe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function Rn(e){return await ut(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>et({...e,helper:n}))}async function et(e){const t=e.foregroundMode??"required";let n=await tt(e,{foregroundMode:t});if(t!=="background"&&(n=await Ie(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=E({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!O(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(Y({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),de(e)?le({...e,window:n,capture:s,ocr:a}):q({...e,window:n,capture:s,ocr:a})}const r=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const s=await se({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await Ie(e.helper,s,e.traceId);const a=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(Y({capture:i,ocr:o,binding:e.binding})){n=await Gt(e.helper,e.traceId);const s=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),Y({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),de(e)?le({...e,window:n,capture:i,ocr:o}):q({...e,window:n,capture:i,ocr:o})}async function tt(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Vt(e.helper,e.traceId,{foreground:n});return n==="background"?r:se({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const x=new Map;function nt(){return process.env.SHENNIAN_WECHAT_VISION_LAYOUT_REUSE!=="0"}function te(e){if(!nt()||!e.classification||e.classification.windowKind!=="chat_main")return;const t=e.window.windowId;if(!t)return;const n=ae(e.window),r=F(e.classification.layout?.messageInputRect,e.screenshot,e.window,"message-input"),i=F(e.classification.layout?.searchInputRect,e.screenshot,e.window,"search-input");if(!r&&!i)return;const o={windowId:t,boundsKey:n,messageInputPoint:r,searchInputPoint:i,updatedAt:new Date().toISOString()},s=`${e.runtime.runtimeId}:${e.binding.bindingId}:${t}`,a=`${e.runtime.runtimeId}:window:${t}:${n}`;x.set(s,o),x.set(a,o),R(e,s,o),R(e,a,o)}async function Fn(e){const t=await ne(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function rt(e){const t=await ne(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function ne(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=ae(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,s=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,a=x.get(o)??x.get(s)??ie(e,o)??ie(e,s);if(a&&a.windowId===r&&a.boundsKey===i&&(!t||a[t]))return x.set(o,a),x.set(s,a),a;const c=e.screenshot??await T(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=F(l.layout?.messageInputRect,c,e.window,"message-input"),u=F(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return x.set(o,d),x.set(s,d),R(e,o,d),R(e,s,d),d}function re(e){const t=e.workDir;return t?J.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function ie(e,t){const n=re(e);if(!n)return null;try{const r=JSON.parse(W.readFileSync(n,"utf8"));return it(r[t])}catch{return null}}function R(e,t,n){const r=re(e);if(r)try{let i={};try{i=JSON.parse(W.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,W.mkdirSync(J.dirname(r),{recursive:!0}),W.writeFileSync(r,`${JSON.stringify(i,null,2)}
|
|
2
2
|
`,"utf8")}catch{}}function it(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=oe(t.messageInputPoint??t.point),o=oe(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function oe(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function ae(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function F(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),s=Number(e?.width),a=Number(e?.height);if(![i,o,s,a].every(Number.isFinite)||s<=0||a<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:s,height:a,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:s/999*t.width,height:a/999*t.height,coordinateSpace:"screenshotPixel"},l=ot(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function ot(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=at({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function at(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),s=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-s)/i}}async function se(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(D).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=dt(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const s of r)try{const a=await T(e.helper,s.windowId,e.traceId,s.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:a.mimeType,dataBase64:a.dataBase64,width:a.width,height:a.height,windowId:s.windowId},traceId:e.traceId});i.push({window:s,classification:c,screenshot:a,score:lt(c,e.requireTargetConversation===!0)})}catch(a){if(st(a))throw a;i.push({window:s,classification:null,errorSummary:a instanceof Error?a.message:String(a||"window classification failed"),score:-1})}const o=i.filter(s=>s.score>0).sort((s,a)=>a.score-s.score)[0];if(!o){const s=i.map(a=>({windowId:a.window.windowId,appName:a.window.appName,title:a.window.title,score:a.score,errorSummary:a.errorSummary,classification:a.classification?{windowKind:a.classification.windowKind,reasonCode:a.classification.reasonCode,confidence:a.classification.confidence,conversationTitle:a.classification.conversationTitle,isTargetConversation:a.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.screenshot&&te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:o.window,classification:o.classification,screenshot:o.screenshot}),o.window}async function An(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function st(e){const t=ct(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ct(e){if(e instanceof De||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function dt(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function lt(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ut(e,t,n){if(!t)return{helper:e,withLease:async(a,c,l)=>await l(e)};let r=null;const i=a=>{t.onEvent?.({...a,at:new Date().toISOString()})},o=async(a,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},f=u.ok&&d.interrupted?ce(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:a,action:c,ok:u.ok===!0&&!d.interrupted,...f?{reasonCode:f}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:ce(d.interruptReason),stage:a,action:c})}const w=Date.now()+ht(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:a,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});const d=Ue({snapshot:Ye(u.result),stage:a,policy:t.policy});if(i({phase:"snapshot",stage:a,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const f=new N({reasonCode:d.reasonCode,stage:a,action:c});if(!l.settle)throw f;const X=w-Date.now();if(X<=0)throw f;await U(Math.max(50,Math.min(d.waitMs,X)))}},s={request:async(a,c,l)=>{const w=wt(a);w&&await o(w.stage,`${a}:before`);const u=await e.request(a,c,l);return w&&u.ok&&await o(w.stage,`${a}:after`),u}};return{helper:s,async withLease(a,c,l){if(await Lt(e,n),await o(a,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(s);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:a,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:a,action:c});try{const u=await l(s);return await o(a,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function wt(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function ce(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function ht(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Qe}function de(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function q(e){const t=await Se(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return We(r,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function le(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Se(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(ft(c))return q(e);throw c}const r=It(n.structuredMessages??n.observedMessages??[]),i=await mt({...e,messages:r,screenshot:t}),o=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),s=We(o,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function ft(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function mt(e){const t=bt(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await yt({...e,seedBlocks:t}):t.filter(s=>h(s.vectorBase64)||h(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??$e(e.workDir||process.cwd(),e.runtime.runtimeId),i=ze(r,e.runtime.runtimeId),o=qe({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ve(r,i),{messages:pt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function bt(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,s=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:s,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function gt(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return vt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function yt(e){try{return await gt(e)}catch(t){if(xt(t))throw t;return[]}}function xt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function vt(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function pt(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:Ct(r,i)}:r})}function Ct(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=h(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(l=>l.blockId===a);return c?ue(c,o):we(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(ue(o));return r}function ue(e,t){return{...t?we(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function we(e){const{vectorBase64:t,...n}=e;return n}function It(e){return e.map((t,n)=>_t(t,n)).filter(t=>t!=null)}function _t(e,t){if(!b(e))return null;const n=je(e.kind),r=Mt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=h(e.stableMessageKey)||kt({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:s,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:St(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:s}}}function kt(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function St(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),b(t.attachment)&&(t.attachment=he(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?he(n):n)),t}function he(e){const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),t}function Mt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function fe(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>Rt(o,e.screenshot,e.window,Wt(e.messages,s))).filter((o,s)=>Nt(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Le(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ke({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const s=i.get(o.stableMessageKey);return s?{...o,mediaMetadata:Ft(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function Nt(e,t,n){const r=be(n);if(!r)return!0;const i=Tt(r),o=e.map((s,a)=>be(Pt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===Bt(e,i);for(const s of o)if(t>=s&&t<=s+8||t===s-1&&!e.slice(s+1,Math.min(e.length,s+9)).some(me))return!0;return!1}function Tt(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Bt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(me(e[n]))return Et(e[n],t)?n:-1;return-1}function me(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return ge(t,n,r)}function Et(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),s=String(i.mimeType||"").toLowerCase(),a=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||s.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(a):t==="image"?/image|photo|picture/.test(n)||o==="image"||s.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(a):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!s.startsWith("image/")&&!s.startsWith("video/")}function Pt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
|
|
3
3
|
`)}function be(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Wt(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
|
|
4
|
-
`)||null}function Rt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!ge(i,o,s))return null;const l=A(e.bbox)??A(o.bbox)??A(o.downloadActionBbox),w=A(o.downloadActionBbox),u=ye(l,t,n),d=Dt(l,t,n),f=ye(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:At(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Ft(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function At(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ge(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function A(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function ye(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Dt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Dn(e,t){await ve(e,t)}async function Lt(e,t){await xe(e,t)}async function xe(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if($t(r.platform)){const i=await Kt(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function ve(e,t,n={}){const r=await xe(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(jt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function Kt(e,t,n){const r=Ot(t);if(!r||process.platform!=="darwin")return r;const i=zt(r);try{await e.request(i,{},n)}catch{}return r}function Ot(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function $t(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function zt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Vt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ve(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await qt(e,l,t)}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}async function qt(e,t,n){if(pe(t.title))return t;try{const r=await e.request("windows.list",{},n);return m(r,"windows.list"),(r.result?.windows??[]).find(o=>o.windowId&&o.visible!==!1&&o.minimized!==!0&&D(o)&&pe(o.title))??t}catch{return t}}function pe(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function jt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(D),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function Ln(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Gt(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(D);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function j(e){const{capture:t,ocr:n}=e.observation??await k(e),r=E({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:on(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=pn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function G(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Ht(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Ht(e){const t=await k(e),n=E({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await j({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await ke(e);if(f.opened)return f}}const i=await Qt(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await Ut(e);if(s.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:u.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await tn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const l=await j({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await j(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Ut(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await U(n);const o=await Yt(e);t=o;const{capture:s,ocr:a}=o,c=E({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=cn(a,s,e.binding.conversationDisplayName);if(!l){if(dn(a)||ln(a,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,s,Xt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Yt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Xt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Jt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Q("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Jt(e){const t=Zt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Zt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(a?.bbox,t,n)}async function Qt(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function H(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),s=await Me(e,o,n);return{capture:o,ocr:s}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function D(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function U(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Ge(t,en(e,n))}async function S(e,t,n){const r=L(e,t,n);r>0&&await U(r)}function en(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function tn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Cn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await Q("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=E({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!nn(n)&&await rn({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function nn(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function rn(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function E(e){const n=(e.ocr.blocks??[]).map(r=>wn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function on(e,t,n=!0){const r=n?an(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function an(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!sn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function sn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||un(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function cn(e,t,n){const r=(e.blocks??[]).map(a=>Ne(a)).filter(a=>!!a),i=r.filter(a=>K(p(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>p(d.text)===a);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Te(a.text)).filter(a=>!Ee(a,r)).filter(a=>z(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Be(s,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function dn(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function ln(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function un(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function wn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const l=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return l||w}):!1}function Y(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
|
|
4
|
+
`)||null}function Rt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!ge(i,o,s))return null;const l=A(e.bbox)??A(o.bbox)??A(o.downloadActionBbox),w=A(o.downloadActionBbox),u=ye(l,t,n),d=Dt(l,t,n),f=ye(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:At(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Ft(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function At(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ge(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function A(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function ye(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Dt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Dn(e,t){await ve(e,t)}async function Lt(e,t){await xe(e,t)}async function xe(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if($t(r.platform)){const i=await Kt(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function ve(e,t,n={}){const r=await xe(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(jt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function Kt(e,t,n){const r=Ot(t);if(!r||process.platform!=="darwin")return r;const i=zt(r);try{await e.request(i,{},n)}catch{}return r}function Ot(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function $t(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function zt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Vt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ve(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await qt(e,l,t)}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}async function qt(e,t,n){if(pe(t.title))return t;try{const r=await e.request("windows.list",{},n);m(r,"windows.list");const o=(r.result?.windows??[]).find(s=>s.windowId&&s.visible!==!1&&s.minimized!==!0&&D(s)&&pe(s.title));if(!o)return t;if(t.windowId&&t.windowId!==o.windowId)try{await e.request("windows.closeWindow",{windowId:t.windowId},n)}catch{}return o}catch{return t}}function pe(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function jt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(D),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function Ln(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Gt(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(D);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function j(e){const{capture:t,ocr:n}=e.observation??await k(e),r=E({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:on(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=pn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function G(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Ht(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Ht(e){const t=await k(e),n=E({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await j({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await ke(e);if(f.opened)return f}}const i=await Qt(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await Ut(e);if(s.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:u.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await tn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const l=await j({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await j(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Ut(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await U(n);const o=await Yt(e);t=o;const{capture:s,ocr:a}=o,c=E({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=cn(a,s,e.binding.conversationDisplayName);if(!l){if(dn(a)||ln(a,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,s,Xt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Yt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Xt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Jt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Q("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Jt(e){const t=Zt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Zt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(a?.bbox,t,n)}async function Qt(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function H(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),s=await Me(e,o,n);return{capture:o,ocr:s}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function D(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function U(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Ge(t,en(e,n))}async function S(e,t,n){const r=L(e,t,n);r>0&&await U(r)}function en(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function tn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Cn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await Q("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=E({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!nn(n)&&await rn({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function nn(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function rn(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function E(e){const n=(e.ocr.blocks??[]).map(r=>wn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function on(e,t,n=!0){const r=n?an(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function an(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!sn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function sn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||un(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function cn(e,t,n){const r=(e.blocks??[]).map(a=>Ne(a)).filter(a=>!!a),i=r.filter(a=>K(p(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>p(d.text)===a);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Te(a.text)).filter(a=>!Ee(a,r)).filter(a=>z(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Be(s,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function dn(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function ln(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function un(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function wn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const l=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return l||w}):!1}function Y(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
|
|
5
5
|
`),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((a,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return a+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(a=>Number(a.bbox?.y)<e.capture.height*.14).map(a=>String(a.text||"")).join(`
|
|
6
6
|
`);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&hn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?P(a.title):V(a.title),l=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):V(e.binding.conversationDisplayName);return!!(c&&l&&Re(c,l))}),!1)}function hn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
|
|
7
7
|
`);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function Pe(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=fn(e.ocr,e.screenshot).sort((o,s)=>v(s.text).length-v(o.text).length);for(const o of i){const s=v(o.text);if(!s)continue;const a=t.find(l=>mn(l.normalized,s));if(!a)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${a.idempotencyKey||a.replyId||a.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:s,anchorText:s,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function fn(e,t){const n=(e.blocks??[]).filter(i=>vn(i,t)&&v(i.text)).sort((i,o)=>C(i.bbox,"y")-C(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let s=i+1;s<n.length&&o.length<5;s+=1){const a=n[s];if(!bn(o[o.length-1],a,t))break;o.push(a),r.push({text:gn(o.map(c=>String(c.text||"").trim())),bbox:xn(o.map(c=>c.bbox))})}}return r}function mn(e,t){if(!e||!t)return!1;if(ee(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function bn(e,t,n){const r=C(e.bbox,"x"),i=C(e.bbox,"y"),o=C(e.bbox,"height"),s=C(t.bbox,"x"),a=C(t.bbox,"y");if(![r,i,o,s,a].every(Number.isFinite)||r<n.width*.5||s<n.width*.5)return!1;const c=a-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(s-r)<=120}function gn(e){return e.reduce((t,n)=>t?n?`${t}${yn(t,n)?" ":""}${n}`:t:n,"")}function yn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function xn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const s=Math.min(...n),a=Math.min(...r);return{x:s,y:a,width:Math.max(...i)-s,height:Math.max(...o)-a,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function We(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=v(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&ee(a,i)>=.9})||n.push(r)}return n}function vn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function pn(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(s*.35,110*l),y:o+Math.min(a-8*w,Math.max(18*w,a/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),f=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-f)/w}}function $(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+s/2,y:o+a/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),f=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-f)/u}}function Cn(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),s=i.bbox;if(!s||!o)return!1;const a=Number(s.x),c=Number(s.y);return!Number.isFinite(a)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:a>t.width*.55&&c>t.height*.45&&c<t.height*.92});return $(r?.bbox,t,n)}function Re(e,t){return y(t)?Ae(e,t):e===t||e.includes(t)||t.includes(e)}function z(e,t){if(y(t))return Ae(e,t);const n=Fe(p(e)),r=Fe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Fe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function V(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ae(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,H as captureAndRecognizeWeChatWindow,T as captureWeChatWindow,Fn as detectWeChatMessageInputPointByServerVision,rt as detectWeChatSearchInputPointByServerVision,Lt as ensureHelperPermissionPreflight,Dn as ensureHelperPreflight,Vt as ensureWeChatWindowReady,Gt as focusExistingWeChatWindow,Ie as focusKnownWeChatWindow,Ln as focusWeChatWindow,Rn as observeWeChatChannelBindingViaHelper,G as openConversationBySearch,j as openConversationInVisibleList,Me as recognizeWeChatScreenshot,tt as selectWeChatWindowForBinding};
|
|
@@ -12,12 +12,14 @@ export type WeChatRpaSessionBindingSyncOptions = {
|
|
|
12
12
|
channelRuntime: Pick<ChannelRuntime, 'upsertManagerWeChatRpaChannel' | 'deleteManagerWeChatRpaChannel' | 'listChannelIdsManagedBy'>;
|
|
13
13
|
getLocalMachineId: () => string;
|
|
14
14
|
listSessions: () => Iterable<WeChatRpaSessionContext>;
|
|
15
|
+
listAuthoritativeSessions?: () => Promise<Iterable<WeChatRpaSessionContext> | null | undefined>;
|
|
15
16
|
};
|
|
16
17
|
export declare class WeChatRpaSessionBindingSync {
|
|
17
18
|
private options;
|
|
18
19
|
private reconciling;
|
|
19
20
|
constructor(options: WeChatRpaSessionBindingSyncOptions);
|
|
20
21
|
reconcileAll(): Promise<void>;
|
|
22
|
+
private listDesiredSessionContexts;
|
|
21
23
|
reconcileSession(session: WeChatRpaSessionContext): Promise<void>;
|
|
22
24
|
private applyUpsert;
|
|
23
25
|
private toDesiredBinding;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import c from"node:os";const l="session-sync";class d{options;reconciling=null;constructor(e){this.options=e}async reconcileAll(){for(;this.reconciling;)await this.reconciling;let e=()=>{};this.reconciling=new Promise(t=>{e=t});try{const t=this.options.getLocalMachineId(),n=new Map,s=await this.listDesiredSessionContexts();for(const a of s){const o=this.toDesiredBinding(a,t);o&&n.set(o.channelId,o)}const i=new Set(this.options.channelRuntime.listChannelIdsManagedBy(l));for(const a of i)n.has(a)||await this.options.channelRuntime.deleteManagerWeChatRpaChannel(a).catch(()=>{});for(const a of n.values())await this.applyUpsert(a)}finally{e(),this.reconciling=null}}async listDesiredSessionContexts(){if(this.options.listAuthoritativeSessions){const e=await this.options.listAuthoritativeSessions().catch(t=>(console.error(`[wechat-rpa-sync] authoritative list failed: ${t instanceof Error?t.message:String(t)}`),null));if(e)return e}return this.options.listSessions()}async reconcileSession(e){for(;this.reconciling;)await this.reconciling;let t=()=>{};this.reconciling=new Promise(n=>{t=n});try{const n=this.options.getLocalMachineId(),s=this.toDesiredBinding(e,n);if(s){await this.applyUpsert(s);return}const i=e.externalChannel?.channelId;i&&this.options.channelRuntime.listChannelIdsManagedBy(l).includes(i)&&await this.options.channelRuntime.deleteManagerWeChatRpaChannel(i).catch(()=>{})}finally{t(),this.reconciling=null}}async applyUpsert(e){await this.options.channelRuntime.upsertManagerWeChatRpaChannel({id:e.channelId,managerSessionId:e.sessionId,sessionId:e.sessionId,workDir:e.workDir,name:e.conversationName,agentType:e.agentType,agentSessionId:e.agentSessionId,modelId:e.modelId,enabled:!0,groups:[{name:e.conversationName}],canReply:e.canReply,source:e.source,downloadAttachments:e.downloadAttachments,privacyConsentAccepted:!0,managedBy:l}).catch(t=>{console.error(`[wechat-rpa-sync] upsert failed channelId=${e.channelId}: ${t instanceof Error?t.message:String(t)}`)})}toDesiredBinding(e,t){const n=e.externalChannel;if(!n||n.type!=="wechat-rpa"||!n.configured||!n.channelId)return null;const s=(n.name??"").trim();return!s||n.machineId&&t&&n.machineId!==t?null:{channelId:n.channelId,sessionId:e.sessionId,conversationName:s,workDir:e.workDir?.trim()||c.homedir(),agentType:e.agentType??void 0,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,canReply:n.canReply!==!1,downloadAttachments:n.downloadAttachments!==!1,source:"wechat-channel"}}}export{d as WeChatRpaSessionBindingSync};
|
|
@@ -29,6 +29,8 @@ export declare class ManagerRuntimeService {
|
|
|
29
29
|
private readonly weChatAutomationLane;
|
|
30
30
|
constructor(opts: ManagerRuntimeServiceOptions);
|
|
31
31
|
private listWeChatRpaSessionContexts;
|
|
32
|
+
private listAuthoritativeWeChatRpaSessionContexts;
|
|
33
|
+
private listLocalWeChatRpaSessionContexts;
|
|
32
34
|
notifyWeChatRpaSessionBinding(session: WeChatRpaSessionContext): Promise<void>;
|
|
33
35
|
reconcileWeChatRpaSessionBindings(): Promise<void>;
|
|
34
36
|
start(): Promise<void>;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import J from"node:http";import{randomBytes as Y,randomUUID as
|
|
2
|
-
`).trim()}function fe(r){try{const e=JSON.parse(r),a=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",n=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=n.length>220?`${n.slice(0,220)}...`:n;return s?`[${a}] ${t}: ${s}`:`[${a}] ${t}`}catch{return"[tool]"}}function B(r){if(!r||typeof r!="object")return;const e=r,a=String(e.kind||""),t=String(e.name||""),n=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(a!=="image"&&a!=="video"&&a!=="file")&&!(!t||!n||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:a,name:t,mimeType:n,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function q(r){const e=r.binding&&typeof r.binding=="object"&&!Array.isArray(r.binding)?r.binding:{},a=String(e.sessionId||r.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),n=String(e.conversationId||"").trim(),s=String(e.conversationName||r.conversation||"").trim(),o=String(e.workDir||r.workDir||"").trim();if(!a)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!n)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:a,channelId:t,conversationId:n,conversationName:s,workDir:o}}function ye(r,e){const a=[...r].sort((d,l)=>d.ts-l.ts),t=[];let n=null,s=null,o=null,i="";const c=()=>{if(!n)return;const d=i.trim();d&&t.push({...n,id:`${n.id}-compact`,payload:d}),n=null,s=null,o=null,i=""};for(const d of a){if(te(d.payload)){c();continue}if(d.role==="user"){c(),t.push(d);continue}if(ne(d.payload)){c(),t.push({...d,payload:fe(d.payload)});continue}const l=Q(d.payload);if(!l.trim())continue;const h=ge(d.id),g=me(d.id);n&&n.role===d.role&&s===h&&h&&g!==null&&o!==null&&g===o+1?(i+=l,n.ts=d.ts,o=g):(c(),n=d,s=h,o=g,i=l)}return c(),t.slice(-e).sort((d,l)=>l.ts-d.ts)}async function we(r){const e=[];let a=0;for await(const n of r){const s=Buffer.from(n);if(a+=s.byteLength,Number.isFinite(C)&&C>0&&a>C)throw new Error(`Manager IPC request body is too large. Max: ${C} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function w(r,e,a,t){r.client.sendRes({type:"res",id:e,ok:a,...a?{payload:t}:{error:String(t.error||"unknown error")}})}function L(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function F(){return Ie()?y.join(P.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):ce("runtime","manager-ipc.json")}function Ie(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Ye{opts;registry=new re;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=Y(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??he(),this.channelRuntime=e.channelRuntime??new se((a,t)=>{this.handleExternalMessage(a,t)},a=>this.registry.createReplyTarget(a).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new ie({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||W().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[a,t]of e)yield{sessionId:a,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(a=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${a instanceof Error?a.message:String(a)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=J.createServer((a,t)=>{this.handleIpc(a,t)}),this.server.requestTimeout=A,this.server.timeout=A,this.server.keepAliveTimeout=A,this.server.headersTimeout=A+5e3,await new Promise((a,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>a())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=F();k.mkdirSync(y.dirname(e),{recursive:!0}),k.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),k.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=F(),a=JSON.parse(k.readFileSync(e,"utf-8"));(a.url===this.ipcUrl||a.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,a,t,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:a??"",SHENNIAN_MANAGER_WORKDIR:R(t),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:R(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,a,t){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:a??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const a=this.registry.getManager(e);return{agentType:a?.defaultWorkerAgentType??null,modelId:a?.defaultWorkerModelId??null}}noteManagerAgentSession(e,a,t,n){this.registry.upsertManager({sessionId:e,agentSessionId:a,workDir:R(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n})}noteAgentEvent(e,a){if(!this.findWorker(e))return;const n={lastActivityAt:new Date().toISOString(),runId:a.runId};a.agentSessionId&&(n.agentSessionId=a.agentSessionId);const s=`${e}:${a.runId}`;if(a.state==="delta"&&a.text&&!a.thinking){const i=(this.workerTextAcc.get(s)??"")+a.text;this.workerTextAcc.set(s,i);const c=_(i);c&&(n.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(a.state==="final"||a.state==="error"||a.state==="aborted"){n.status=a.state;const i=_(this.workerTextAcc.get(s)??"");i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else a.state==="start"&&(n.status="running");const o=this.registry.updateWorker(e,n);o&&(a.state==="final"||a.state==="error"||a.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,a.state,a.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const a=this.opts.getRuntime(),t=e.params??{};try{const n=String(t.managerSessionId||t.sessionId||"");if(!n)throw new Error("sessionId is required");const s=this.registry.getManager(n),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",d=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const l=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${n}`),managerSessionId:n,sessionId:n,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(n),w(a,e.id,!0,{channel:l});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"wechat-rpa",{includeSecret:!0})});return}if(i){const l=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${n}`),managerSessionId:n,sessionId:n,workDir:R(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:O(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:U(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(n),w(a,e.id,!0,{channel:l});return}if(c){const l=await this.channelRuntime.syncManagerWeChatRpaChannel(n);this.broadcastManagerChannelStatus(n),w(a,e.id,!0,l);return}if(d){const l=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:n,idempotencyKey:typeof t.idempotencyKey=="string"?t.idempotencyKey:null,replyId:typeof t.replyId=="string"?t.replyId:null,reason:typeof t.reason=="string"?t.reason:"user_cancelled"});this.broadcastManagerChannelStatus(n),w(a,e.id,!0,l);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(n){w(a,e.id,!1,{error:n instanceof Error?n.message:String(n)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const a=this.opts.getRuntime();if(!a.client?.sendEvent)return;const t=this.registry.getManager(e),n=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:n}}})}async handleIpc(e,a){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(a,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),n=await we(e),s=String(n.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(a,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(n.agentType||n.agent||o.defaultWorkerAgentType||"codex");if(!pe(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=R(String(n.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const d=String(n.message||"");if(!d)throw new Error("message is required");const l=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:d.slice(0,120)}),h=String(n.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(l.sessionId,i,c,d,null,h),u(a,200,{ok:!0,session:l});return}if(t.pathname==="/sessions/send"){const i=String(n.sessionId||""),c=String(n.message||""),d=n.enqueue===void 0?!0:!!n.enqueue,l=this.registry.getWorkerForManager(s,i);if(!l)throw new Error("Worker not found in this manager scope");const h=String(n.modelId||(l.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));d?await this.dispatchChatEnqueue(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h):await this.dispatchChatSend(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h),u(a,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(a,200,{ok:!0,queue:d});return}if(t.pathname==="/sessions/queue/edit"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${p()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||""),text:String(n.message||n.text||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${p()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(a,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(n.sessionId||""),c=Number(n.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=ae(i,{limit:Math.max(c*20,c)});u(a,200,{ok:!0,messages:ye(l,c),rawMessageCount:l.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(a,200,{ok:!0,path:y.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof n.replyTarget=="string"?this.registry.getReplyTarget(n.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(n.text||""),d=B(n.attachment),l=String(n.idempotencyKey||p()),h=String(n.channelId||""),g=String(n.conversationId||""),M=!i&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,I=i?.channelId||h||M?.channelId||"",T=i?.conversationId||g||M?.conversationId||"",N=I?this.channelRuntime.getChannelById(I):void 0,z=N&&(!!(i||h)||N.managedBy!=="session-sync");if(I&&z){if(!T)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:s,channelId:I,conversationId:T,messageId:i?.messageId??void 0,text:c,attachment:d,idempotencyKey:l});u(a,f.ok?200:400,f);return}const b=await this.tryDirectWeChatRpaReply(s,c,d);if(b){u(a,b.ok?200:400,b);return}let S;try{S=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:d,idempotencyKey:l})}catch(f){const D=f instanceof Error?f.message:String(f);if(!L(D))throw f;S={ok:!1,error:D}}if(S.ok){u(a,200,{ok:!0,payload:S.payload});return}if(!L(S.error||"")||!I||!T){u(a,400,{ok:!1,error:S.error||"External send failed"});return}u(a,400,{ok:!1,error:`No local external channel is configured for ${I}`});return}if(t.pathname==="/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof n.name=="string"?n.name:void 0,enabled:!!n.enabled,wsUrl:typeof n.wsUrl=="string"?n.wsUrl:void 0,token:typeof n.token=="string"?n.token:void 0,canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=q(n),c=m(n.limit)??10,d=typeof n.traceId=="string"?n.traceId:void 0;let l;try{l=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>ue(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:n.download==="never"?"never":"auto",traceId:d,timeoutMs:m(n.timeoutMs)}))}catch(h){u(a,400,{ok:!1,...j(h,d)});return}u(a,200,{ok:!0,messages:l.messages,outDir:l.outDir,helperTracePath:l.helperTracePath,activityGuardPath:l.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=q(n),c=String(n.text||""),d=B(n.attachment),l=typeof n.traceId=="string"?n.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>v(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:d,traceId:l,timeoutMs:m(n.timeoutMs)}))}catch(g){u(a,400,{ok:!1,...j(g,l)});return}u(a,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=R(String(n.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(n.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,groups:O(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:U(n.source),pollIntervalMs:m(n.pollIntervalMs),recentLimit:m(n.recentLimit),idleSeconds:m(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,selfTriggerMarker:typeof n.selfTriggerMarker=="string"?n.selfTriggerMarker:void 0,deferInitialPoll:n.deferInitialPoll===void 0?void 0:!!n.deferInitialPoll,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:null,replyId:typeof n.replyId=="string"?n.replyId:null,reason:typeof n.reason=="string"?n.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,...i});return}u(a,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(a,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,a,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${p()}`,method:"chat.send",params:{sessionId:e,text:n,agentType:a,workDir:t,agentSessionId:s,modelId:o}})}async dispatchChatEnqueue(e,a,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e,text:n,agentType:a,workDir:t,agentSessionId:s,modelId:o}})}async tryDirectWeChatRpaReply(e,a,t){const n=this.opts.getRuntime().sessions.get(e),s=n?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||W().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!a.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=n?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:le(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>v(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:a,attachment:t}))}}catch(d){return{ok:!1,error:d instanceof Error?d.message:String(d)}}}async sendManagedWeComReply(e){const a=oe(e.text);if(!a.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const n=[];for(const[s,o]of a.entries()){const i=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:a.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};n.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:a.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};n.push(s.payload)}return{ok:!0,payload:n.length===1?n[0]:n}}wakeManagerForWorker(e,a,t,n){const s=this.registry.getManager(e);if(!s)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
|
|
1
|
+
import J from"node:http";import{randomBytes as Y,randomUUID as m}from"node:crypto";import k from"node:fs";import N from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as V,extractPayloadText as Q,formatExternalConversationText as X,formatExternalMessageLine as Z,formatExternalAttachmentReference as ee,isAgentHiddenPayload as te,isToolPayload as ne}from"@shennian/wire";import{ManagerRegistry as re}from"./registry.js";import{readMessages as ae}from"../session/store.js";import{listProjectedSessions as se}from"../session/projection.js";import{ChannelRuntime as ie}from"../channels/runtime.js";import{WeChatRpaSessionBindingSync as oe}from"../channels/wechat-rpa-session-sync.js";import{splitExternalReplyText as ce}from"../channels/reply-split.js";import{loadConfig as b,resolveShennianPath as le}from"../config/index.js";import{weChatChannelConversationId as de}from"../channels/wechat-rpa/product-channel.js";import{buildExternalChannelInstructions as ue}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as he,sendDirectWeChatMessageOnce as P}from"../commands/wechat.js";import{createWeChatChannelApiClient as pe}from"../channels/wechat-channel/client.js";import{createWeChatAutomationLane as me}from"../channels/wechat-channel/automation-lane.js";const M=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),A=12*6e4;let $=null;function Qe(a){$=a}function Xe(){return $}function R(a){return y.resolve(a||N.homedir())}function u(a,e,r){a.writeHead(e,{"content-type":"application/json; charset=utf-8"}),a.end(JSON.stringify(r))}function ge(a){return/^agent-(.+)-\d+$/.exec(a)?.[1]??null}function fe(a){return a==="manager"?!1:a.startsWith("custom:")?!0:V.includes(a)}function ye(a){const e=/^agent-.+-(\d+)$/.exec(a);if(!e)return null;const r=Number(e[1]);return Number.isInteger(r)&&r>=0?r:null}function _(a){return a.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function we(a){try{const e=JSON.parse(a),r=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",n=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=n.length>220?`${n.slice(0,220)}...`:n;return s?`[${r}] ${t}: ${s}`:`[${r}] ${t}`}catch{return"[tool]"}}function B(a){if(!a||typeof a!="object")return;const e=a,r=String(e.kind||""),t=String(e.name||""),n=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(r!=="image"&&r!=="video"&&r!=="file")&&!(!t||!n||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:r,name:t,mimeType:n,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function L(a){const e=a.binding&&typeof a.binding=="object"&&!Array.isArray(a.binding)?a.binding:{},r=String(e.sessionId||a.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),n=String(e.conversationId||"").trim(),s=String(e.conversationName||a.conversation||"").trim(),o=String(e.workDir||a.workDir||"").trim();if(!r)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!n)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:r,channelId:t,conversationId:n,conversationName:s,workDir:o}}function Ie(a,e){const r=[...a].sort((d,l)=>d.ts-l.ts),t=[];let n=null,s=null,o=null,i="";const c=()=>{if(!n)return;const d=i.trim();d&&t.push({...n,id:`${n.id}-compact`,payload:d}),n=null,s=null,o=null,i=""};for(const d of r){if(te(d.payload)){c();continue}if(d.role==="user"){c(),t.push(d);continue}if(ne(d.payload)){c(),t.push({...d,payload:we(d.payload)});continue}const l=Q(d.payload);if(!l.trim())continue;const h=ge(d.id),p=ye(d.id);n&&n.role===d.role&&s===h&&h&&p!==null&&o!==null&&p===o+1?(i+=l,n.ts=d.ts,o=p):(c(),n=d,s=h,o=p,i=l)}return c(),t.slice(-e).sort((d,l)=>l.ts-d.ts)}async function Se(a){const e=[];let r=0;for await(const n of a){const s=Buffer.from(n);if(r+=s.byteLength,Number.isFinite(M)&&M>0&&r>M)throw new Error(`Manager IPC request body is too large. Max: ${M} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function w(a,e,r,t){a.client.sendRes({type:"res",id:e,ok:r,...r?{payload:t}:{error:String(t.error||"unknown error")}})}function q(a){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(a)}function F(){return ke()?y.join(N.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):le("runtime","manager-ipc.json")}function ke(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Ze{opts;registry=new re;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=Y(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??me(),this.channelRuntime=e.channelRuntime??new ie((r,t)=>{this.handleExternalMessage(r,t)},r=>this.registry.createReplyTarget(r).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new oe({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||b().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts(),listAuthoritativeSessions:()=>this.listAuthoritativeWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[r,t]of e)yield{sessionId:r,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async listAuthoritativeWeChatRpaSessionContexts(){const e=b(),r=process.env.SHENNIAN_MACHINE_ID||e.machineId||"";if(!e.machineToken||!r)return null;const t=this.listLocalWeChatRpaSessionContexts(r);return(await pe({serverUrl:e.serverUrl,machineToken:e.machineToken}).listBindings({machineId:r})).bindings.filter(s=>s.enabled!==!1).map(s=>{const o=t.get(s.sessionId);return{sessionId:s.sessionId,workDir:s.sessionWorkDir?.trim()||o?.workDir?.trim()||N.homedir(),agentType:s.sessionAgentType?.trim()||o?.agentType||void 0,agentSessionId:s.sessionAgentSessionId??o?.agentSessionId??null,modelId:s.sessionModelId??o?.modelId??null,externalChannel:{connected:!0,configured:!0,type:"wechat-rpa",channelId:s.id,machineId:s.machineId,name:s.conversationName,canReply:s.allowReply!==!1,systemPrompt:null,wechatRpaSource:"wechat-channel",wechatRpaGroups:[{name:s.conversationName}],downloadAttachments:s.downloadMedia!==!1}}})}listLocalWeChatRpaSessionContexts(e){const r=new Map;for(const t of this.listWeChatRpaSessionContexts())r.set(t.sessionId,t);for(const t of se())!t?.id||t.deletedAt||t.machineId&&e&&t.machineId!==e||r.has(t.id)||r.set(t.id,{sessionId:t.id,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,modelId:t.modelId,externalChannel:t.externalChannel??null});return r}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(r=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${r instanceof Error?r.message:String(r)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=J.createServer((r,t)=>{this.handleIpc(r,t)}),this.server.requestTimeout=A,this.server.timeout=A,this.server.keepAliveTimeout=A,this.server.headersTimeout=A+5e3,await new Promise((r,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>r())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=F();k.mkdirSync(y.dirname(e),{recursive:!0}),k.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),k.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=F(),r=JSON.parse(k.readFileSync(e,"utf-8"));(r.url===this.ipcUrl||r.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,r,t,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:r??"",SHENNIAN_MANAGER_WORKDIR:R(t),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:R(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,r,t){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:r??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const r=this.registry.getManager(e);return{agentType:r?.defaultWorkerAgentType??null,modelId:r?.defaultWorkerModelId??null}}noteManagerAgentSession(e,r,t,n){this.registry.upsertManager({sessionId:e,agentSessionId:r,workDir:R(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n})}noteAgentEvent(e,r){if(!this.findWorker(e))return;const n={lastActivityAt:new Date().toISOString(),runId:r.runId};r.agentSessionId&&(n.agentSessionId=r.agentSessionId);const s=`${e}:${r.runId}`;if(r.state==="delta"&&r.text&&!r.thinking){const i=(this.workerTextAcc.get(s)??"")+r.text;this.workerTextAcc.set(s,i);const c=_(i);c&&(n.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(r.state==="final"||r.state==="error"||r.state==="aborted"){n.status=r.state;const i=_(this.workerTextAcc.get(s)??"");i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else r.state==="start"&&(n.status="running");const o=this.registry.updateWorker(e,n);o&&(r.state==="final"||r.state==="error"||r.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,r.state,r.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const r=this.opts.getRuntime(),t=e.params??{};try{const n=String(t.managerSessionId||t.sessionId||"");if(!n)throw new Error("sessionId is required");const s=this.registry.getManager(n),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",d=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(r,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const l=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${n}`),managerSessionId:n,sessionId:n,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(n),w(r,e.id,!0,{channel:l});return}if(o){w(r,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"wechat-rpa",{includeSecret:!0})});return}if(i){const l=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${n}`),managerSessionId:n,sessionId:n,workDir:R(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:O(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:U(t.source),pollIntervalMs:g(t.pollIntervalMs),recentLimit:g(t.recentLimit),idleSeconds:g(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(n),w(r,e.id,!0,{channel:l});return}if(c){const l=await this.channelRuntime.syncManagerWeChatRpaChannel(n);this.broadcastManagerChannelStatus(n),w(r,e.id,!0,l);return}if(d){const l=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:n,idempotencyKey:typeof t.idempotencyKey=="string"?t.idempotencyKey:null,replyId:typeof t.replyId=="string"?t.replyId:null,reason:typeof t.reason=="string"?t.reason:"user_cancelled"});this.broadcastManagerChannelStatus(n),w(r,e.id,!0,l);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(n){w(r,e.id,!1,{error:n instanceof Error?n.message:String(n)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const r=this.opts.getRuntime();if(!r.client?.sendEvent)return;const t=this.registry.getManager(e),n=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");r.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:n}}})}async handleIpc(e,r){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(r,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),n=await Se(e),s=String(n.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(r,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(n.agentType||n.agent||o.defaultWorkerAgentType||"codex");if(!fe(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=R(String(n.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const d=String(n.message||"");if(!d)throw new Error("message is required");const l=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:d.slice(0,120)}),h=String(n.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(l.sessionId,i,c,d,null,h),u(r,200,{ok:!0,session:l});return}if(t.pathname==="/sessions/send"){const i=String(n.sessionId||""),c=String(n.message||""),d=n.enqueue===void 0?!0:!!n.enqueue,l=this.registry.getWorkerForManager(s,i);if(!l)throw new Error("Worker not found in this manager scope");const h=String(n.modelId||(l.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));d?await this.dispatchChatEnqueue(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h):await this.dispatchChatSend(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h),u(r,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(r,200,{ok:!0,queue:d});return}if(t.pathname==="/sessions/queue/edit"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${m()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||""),text:String(n.message||n.text||"")}}),u(r,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${m()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||"")}}),u(r,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${m()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(r,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(n.sessionId||""),c=Number(n.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=ae(i,{limit:Math.max(c*20,c)});u(r,200,{ok:!0,messages:Ie(l,c),rawMessageCount:l.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(r,200,{ok:!0,path:y.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof n.replyTarget=="string"?this.registry.getReplyTarget(n.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(n.text||""),d=B(n.attachment),l=String(n.idempotencyKey||m()),h=String(n.channelId||""),p=String(n.conversationId||""),C=!i&&(!h||!p)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,I=i?.channelId||h||C?.channelId||"",E=i?.conversationId||p||C?.conversationId||"",W=I?this.channelRuntime.getChannelById(I):void 0,z=W&&(!!(i||h)||W.managedBy!=="session-sync");if(I&&z){if(!E)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:s,channelId:I,conversationId:E,messageId:i?.messageId??void 0,text:c,attachment:d,idempotencyKey:l});u(r,f.ok?200:400,f);return}const x=await this.tryDirectWeChatRpaReply(s,c,d);if(x){u(r,x.ok?200:400,x);return}let S;try{S=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:d,idempotencyKey:l})}catch(f){const v=f instanceof Error?f.message:String(f);if(!q(v))throw f;S={ok:!1,error:v}}if(S.ok){u(r,200,{ok:!0,payload:S.payload});return}if(!q(S.error||"")||!I||!E){u(r,400,{ok:!1,error:S.error||"External send failed"});return}u(r,400,{ok:!1,error:`No local external channel is configured for ${I}`});return}if(t.pathname==="/channel/get"){u(r,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof n.name=="string"?n.name:void 0,enabled:!!n.enabled,wsUrl:typeof n.wsUrl=="string"?n.wsUrl:void 0,token:typeof n.token=="string"?n.token:void 0,canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(r,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=L(n),c=g(n.limit)??10,d=typeof n.traceId=="string"?n.traceId:void 0;let l;try{l=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>he(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:n.download==="never"?"never":"auto",traceId:d,timeoutMs:g(n.timeoutMs)}))}catch(h){u(r,400,{ok:!1,...j(h,d)});return}u(r,200,{ok:!0,messages:l.messages,outDir:l.outDir,helperTracePath:l.helperTracePath,activityGuardPath:l.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=L(n),c=String(n.text||""),d=B(n.attachment),l=typeof n.traceId=="string"?n.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>P(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:d,traceId:l,timeoutMs:g(n.timeoutMs)}))}catch(p){u(r,400,{ok:!1,...j(p,l)});return}u(r,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(r,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=R(String(n.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(n.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,groups:O(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:U(n.source),pollIntervalMs:g(n.pollIntervalMs),recentLimit:g(n.recentLimit),idleSeconds:g(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,selfTriggerMarker:typeof n.selfTriggerMarker=="string"?n.selfTriggerMarker:void 0,deferInitialPoll:n.deferInitialPoll===void 0?void 0:!!n.deferInitialPoll,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(r,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(r,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:null,replyId:typeof n.replyId=="string"?n.replyId:null,reason:typeof n.reason=="string"?n.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(r,200,{ok:!0,...i});return}u(r,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(r,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,r,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${m()}`,method:"chat.send",params:{sessionId:e,text:n,agentType:r,workDir:t,agentSessionId:s,modelId:o}})}async dispatchChatEnqueue(e,r,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${m()}`,method:"chat.enqueue",params:{sessionId:e,text:n,agentType:r,workDir:t,agentSessionId:s,modelId:o}})}async tryDirectWeChatRpaReply(e,r,t){const n=this.opts.getRuntime().sessions.get(e),s=n?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||b().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!r.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=n?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:de(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>P(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:r,attachment:t}))}}catch(d){return{ok:!1,error:d instanceof Error?d.message:String(d)}}}async sendManagedWeComReply(e){const r=ce(e.text);if(!r.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const n=[];for(const[s,o]of r.entries()){const i=await t.sendReq({type:"req",id:`external-send-${m()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:r.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};n.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${m()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:r.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};n.push(s.payload)}return{ok:!0,payload:n.length===1?n[0]:n}}wakeManagerForWorker(e,r,t,n){const s=this.registry.getManager(e);if(!s)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
|
|
3
3
|
|
|
4
|
-
Worker: ${
|
|
4
|
+
Worker: ${r.sessionId}
|
|
5
5
|
\u72B6\u6001\uFF1A${t}
|
|
6
6
|
\u6700\u7EC8\u7ED3\u679C\uFF1A
|
|
7
|
-
${n||
|
|
7
|
+
${n||r.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
|
|
8
8
|
|
|
9
|
-
\u8BF7\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA/\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`;this.interruptAndResumeManager(s,o,t==="final"?"worker.final":`worker.${t}`)}handleExternalMessage(e,
|
|
9
|
+
\u8BF7\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA/\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`;this.interruptAndResumeManager(s,o,t==="final"?"worker.final":`worker.${t}`)}handleExternalMessage(e,r){const t=this.channelRuntime.getChannelById(r.channelId)??this.channelRuntime.getManagerChannel(e,r.channelType),n=this.channelRuntime.getChannelStatusById(r.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),c=t?.agentSessionId??s?.agentSessionId??null,d=t?.modelId||s?.modelId||"",l=Re(r.attachments,r.channelType),h=Me(r);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:c,modelId:d,text:h,attachments:l,externalChannel:Ee(n),replyTarget:r.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${m()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),r=this.registry.load();for(const t of Object.values(r.workers)){if(t.status!=="running")continue;const n=e-Date.parse(t.createdAt);if(n<10*6e4)continue;const s=t.healthNotifiedAt?Date.parse(t.healthNotifiedAt):0;if(s&&e-s<10*6e4)continue;const o=r.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),c=`Worker ${t.sessionId} \u5DF2\u8FD0\u884C ${Math.floor(n/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
10
10
|
|
|
11
11
|
\u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
|
|
12
12
|
- \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
|
|
13
13
|
- \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${t.lastActivityAt}
|
|
14
14
|
- \u6700\u8FD1 ${i} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002
|
|
15
15
|
|
|
16
|
-
\u8BF7\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u7B49\u5F85\u3001\u8BE2\u95EE\u7528\u6237\u3001\u505C\u6B62 worker\u3001\u6216\u521B\u5EFA\u5176\u4ED6 worker \u534F\u52A9\u3002`;this.registry.updateWorker(t.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:c}),o.status==="idle"&&this.dispatchChatSend(o.sessionId,"manager",o.workDir,c,o.agentSessionId,o.modelId)}}async interruptAndResumeManager(e,
|
|
16
|
+
\u8BF7\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u7B49\u5F85\u3001\u8BE2\u95EE\u7528\u6237\u3001\u505C\u6B62 worker\u3001\u6216\u521B\u5EFA\u5176\u4ED6 worker \u534F\u52A9\u3002`;this.registry.updateWorker(t.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:c}),o.status==="idle"&&this.dispatchChatSend(o.sessionId,"manager",o.workDir,c,o.agentSessionId,o.modelId)}}async interruptAndResumeManager(e,r,t){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&(this.registry.upsertManager({...e,status:"interrupting"}),await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${m()}`,method:"chat.abort",params:{sessionId:e.sessionId}}));const o=t?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${t}
|
|
17
17
|
|
|
18
|
-
${
|
|
18
|
+
${r}`:r;await this.dispatchChatSend(e.sessionId,"manager",e.workDir,o,e.agentSessionId,e.modelId)}bindManagerAdapterEvents(e,r){r.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,t.agentSessionId,n.workDir,n.modelId)}t.state==="start"&&this.updateManagerStatus(e,"running"),(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.updateManagerStatus(e,"idle")})}updateManagerStatus(e,r){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:r})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(r=>ue(r,void 0,e,"manager")).join(`
|
|
19
19
|
|
|
20
|
-
`).trim()}}function O(
|
|
21
|
-
${h}`;return s.has(
|
|
22
|
-
`)}function
|
|
23
|
-
`);return[X({conversationName:
|
|
24
|
-
`)}function
|
|
20
|
+
`).trim()}}function O(a){return Array.isArray(a)?a.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function U(a){return a==="wechat-channel"||a==="macos-flow"||a==="macos-probe"||a==="windows-visual-flow"||a==="wechat-rpa-lab"||a==="fixture-jsonl"?a:void 0}function Re(a,e){const r=a.map(t=>H(t,e)).filter(t=>t!=null);return r.length?r:void 0}function Ce(a,e,r,t){const n=e||"",s=new Set;return a.map((i,c)=>{const d=H(i,t),l=ee({type:i.type,name:d?.name||i.name,path:d?.path,mimeType:d?.mimeType||i.mimeType,availability:i.availability,providerError:i.providerError||Te(i)},c),h=d?.path||"";if(h){const C=`${l}
|
|
21
|
+
${h}`;return s.has(C)||n.includes(h)?"":(s.add(C),`${r}: ${l}`)}const p=l;return s.has(p)?"":(s.add(p),`${r}: ${l}`)}).filter(i=>!n.includes(i)).join(`
|
|
22
|
+
`)}function Me(a){const r=(a.channelType==="wechat-rpa"?Ae(a.sender.name):a.sender.name||a.sender.id)||(a.channelType==="wechat-rpa"?"\u5BF9\u65B9":a.sender.id)||"\u5BF9\u65B9",t=Ce(a.attachments,a.text,r,a.channelType),s=[a.text?Z({senderName:r,senderId:a.sender.id,text:a.text}):t?"":`${r}:`,t].filter(Boolean).join(`
|
|
23
|
+
`);return[X({conversationName:a.conversationName||a.conversationId,messages:[]}),s].filter(Boolean).join(`
|
|
24
|
+
`)}function Ae(a){const e=a?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function Te(a){return!a.localPath||a.availability&&a.availability!=="edge-local"||D(a.localPath)?"":"edge-local-unavailable"}function H(a,e){return a.localPath&&xe(a,e)&&D(a.localPath)?{path:a.localPath,name:a.name||y.basename(a.localPath)||"attachment",mimeType:a.mimeType||K(a)}:a.url&&Ne(a)?{path:a.url,name:a.name||a.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:a.mimeType||K(a)}:a.thumbnailPath&&!G(a,e)&&D(a.thumbnailPath)?{path:a.thumbnailPath,name:a.name?`${a.name}-preview.png`:y.basename(a.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function Ee(a){return a?a.type!=="wechat-rpa"?a:{configured:a.configured,connected:a.connected,type:a.type,channelId:a.channelId,name:a.name,canReply:a.canReply,systemPrompt:a.systemPrompt,wechatRpaSource:a.wechatRpaSource,wechatRpaGroups:a.wechatRpaGroups,pollIntervalMs:a.pollIntervalMs,recentLimit:a.recentLimit,idleSeconds:a.idleSeconds,forceForeground:a.forceForeground,noRestore:a.noRestore,downloadAttachments:a.downloadAttachments,selfNickname:a.selfNickname,wechatRpaPrivacyConsentAccepted:a.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:a.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:a.wechatRpaPreflightChecks,wechatRpaRuntimeState:a.wechatRpaRuntimeState,wechatRpaLastMessageAt:a.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:a.wechatRpaPendingReplyCount,wechatRpaLastError:a.wechatRpaLastError}:null}function xe(a,e){return a.providerError||a.availability&&a.availability!=="edge-local"?!1:G(a,e)?a.materializationKind==="original-file"&&a.isOriginal===!0&&a.mimeKindMatches===!0&&!be(a.localPath):!0}function Ne(a){return!a.providerError&&(!a.availability||a.availability==="server-url")}function G(a,e){if(e!=="wechat-rpa")return!1;const r=String(a.type||"").toLowerCase();return r==="file"||r==="video"||r==="video-file"}function be(a){const e=String(a||"").replace(/\\/g,"/"),r=y.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(r)}function D(a){try{return k.statSync(a).isFile()}catch{return!1}}function K(a){return a.type==="image"?"image/*":a.type==="video"?"video/*":a.type==="audio"?"audio/*":"application/octet-stream"}function g(a){const e=Number(a);return Number.isFinite(e)?e:void 0}function j(a,e){const r=T(a,"reasonCode")||De(a),t=T(a,"outDir"),n=T(a,"helperTracePath"),s=T(a,"activityGuardPath");return{error:a instanceof Error?a.message:String(a||r),reasonCode:r,...t?{outDir:t}:{},...n?{helperTracePath:n}:{},...s?{activityGuardPath:s}:{},...e?{traceId:e}:{}}}function T(a,e){if(!a||typeof a!="object")return"";const r=a[e];return typeof r=="string"&&r.trim()?r.trim():""}function De(a){const e=a instanceof Error?a.message:String(a||"");return/^([a-z][a-z0-9_]*)(?::|\b)/i.exec(e.trim())?.[1]||"wechat_tool_failed"}export{Ze as ManagerRuntimeService,Xe as getManagerRuntimeService,Qe as setManagerRuntimeService};
|