shennian 0.2.131 → 0.2.133

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.
@@ -131,8 +131,8 @@
131
131
  },
132
132
  {
133
133
  "file": "src/agents/pi.js",
134
- "beforeBytes": 31585,
135
- "afterBytes": 14905
134
+ "beforeBytes": 31836,
135
+ "afterBytes": 15039
136
136
  },
137
137
  {
138
138
  "file": "src/agents/platform-instructions.js",
@@ -261,8 +261,8 @@
261
261
  },
262
262
  {
263
263
  "file": "src/channels/wechat-channel/observer.js",
264
- "beforeBytes": 111237,
265
- "afterBytes": 50157
264
+ "beforeBytes": 112602,
265
+ "afterBytes": 50766
266
266
  },
267
267
  {
268
268
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -321,8 +321,8 @@
321
321
  },
322
322
  {
323
323
  "file": "src/channels/wechat-rpa/product-channel.js",
324
- "beforeBytes": 18332,
325
- "afterBytes": 9837
324
+ "beforeBytes": 20794,
325
+ "afterBytes": 10908
326
326
  },
327
327
  {
328
328
  "file": "src/channels/wechat-rpa-session-sync.js",
@@ -526,8 +526,8 @@
526
526
  },
527
527
  {
528
528
  "file": "src/manager/runtime.js",
529
- "beforeBytes": 68071,
530
- "afterBytes": 34702
529
+ "beforeBytes": 68372,
530
+ "afterBytes": 34842
531
531
  },
532
532
  {
533
533
  "file": "src/native-fusion/config.js",
@@ -641,8 +641,8 @@
641
641
  },
642
642
  {
643
643
  "file": "src/session/queue.js",
644
- "beforeBytes": 12692,
645
- "afterBytes": 6156
644
+ "beforeBytes": 14160,
645
+ "afterBytes": 6773
646
646
  },
647
647
  {
648
648
  "file": "src/session/remote-attachments.js",
@@ -35,6 +35,12 @@ export declare class PiAdapter extends AgentAdapter {
35
35
  }): void;
36
36
  start(sessionId: string, workDir: string, _agentSessionId?: string | null): Promise<void>;
37
37
  send(text: string, modelId?: string): Promise<void>;
38
+ getStatus(): Promise<{
39
+ active: boolean;
40
+ runId?: string | null;
41
+ runPhase?: null;
42
+ canStop?: boolean;
43
+ }>;
38
44
  private initAgent;
39
45
  /**
40
46
  * After each turn, compress old messages and actually replace them in the
@@ -1,12 +1,12 @@
1
- import{randomUUID as b}from"node:crypto";import l from"node:fs";import g from"node:path";import{execFile as _}from"node:child_process";import{promisify as A}from"node:util";import{Agent as T,streamProxy as I}from"@mariozechner/pi-agent-core";import{streamOpenAICompletions as j}from"@mariozechner/pi-ai/openai-completions";import{Type as p}from"@sinclair/typebox";import{AgentAdapter as D,registerAgent as $}from"./adapter.js";import{buildExternalChannelInstructions as F}from"./external-channel-instructions.js";import{loadConfig as L}from"../config/index.js";import{getManagedAgentProviderConfig as O}from"./config-status.js";import{SERVERS as N}from"../region.js";import{buildAgentProcessEnv as q}from"../agent-env.js";import{buildRollingSummary as E,buildShellCommandSpec as P,cloneMessages as S,CONTEXT_TOKEN_THRESHOLD as v,createPiModel as M,estimateTokens as w,getSessionDir as z,KEEP_RECENT_MESSAGES as k,LEGACY_SUMMARY_FILENAME as R,loadAgentsMdInstructions as U,longestCommonPrefixLength as B,MESSAGES_FILENAME as K,messagesToText as G,PI_DEFAULT_MODEL_ID as J,requestProxySummary as H,SNAPSHOT_FILENAME as Y,SYSTEM_PROMPT as W,SUMMARY_FILENAME as V}from"./pi-context.js";import{buildShellCommandSpec as Mt}from"./pi-context.js";const X=A(_),Q="https://dashscope.aliyuncs.com/compatible-mode/v1";function y(o,t){return g.resolve(o,t)}async function Z(o,t,e,s){const n=P(t),{stdout:i,stderr:a}=await X(n.file,n.args,{cwd:o,env:q(e),timeout:3e4,signal:s,maxBuffer:1024*1024,windowsHide:!0});return{stdout:i,stderr:a,shell:n.shell}}function C(o){return typeof o=="string"?o:Buffer.isBuffer(o)?o.toString("utf8"):""}function tt(o){if(!o||typeof o!="object")return String(o);const t=o,e=[t.message||String(o)],s=C(t.stdout).trim(),n=C(t.stderr).trim();return s&&e.push(`---stdout---
1
+ import{randomUUID as b}from"node:crypto";import l from"node:fs";import g from"node:path";import{execFile as _}from"node:child_process";import{promisify as A}from"node:util";import{Agent as I,streamProxy as T}from"@mariozechner/pi-agent-core";import{streamOpenAICompletions as j}from"@mariozechner/pi-ai/openai-completions";import{Type as p}from"@sinclair/typebox";import{AgentAdapter as D,registerAgent as $}from"./adapter.js";import{buildExternalChannelInstructions as F}from"./external-channel-instructions.js";import{loadConfig as L}from"../config/index.js";import{getManagedAgentProviderConfig as O}from"./config-status.js";import{SERVERS as N}from"../region.js";import{buildAgentProcessEnv as q}from"../agent-env.js";import{buildRollingSummary as E,buildShellCommandSpec as P,cloneMessages as S,CONTEXT_TOKEN_THRESHOLD as v,createPiModel as M,estimateTokens as w,getSessionDir as z,KEEP_RECENT_MESSAGES as k,LEGACY_SUMMARY_FILENAME as R,loadAgentsMdInstructions as U,longestCommonPrefixLength as B,MESSAGES_FILENAME as K,messagesToText as G,PI_DEFAULT_MODEL_ID as J,requestProxySummary as H,SNAPSHOT_FILENAME as Y,SYSTEM_PROMPT as W,SUMMARY_FILENAME as V}from"./pi-context.js";import{buildShellCommandSpec as Mt}from"./pi-context.js";const X=A(_),Q="https://dashscope.aliyuncs.com/compatible-mode/v1";function y(o,t){return g.resolve(o,t)}async function Z(o,t,e,s){const n=P(t),{stdout:i,stderr:a}=await X(n.file,n.args,{cwd:o,env:q(e),timeout:3e4,signal:s,maxBuffer:1024*1024,windowsHide:!0});return{stdout:i,stderr:a,shell:n.shell}}function C(o){return typeof o=="string"?o:Buffer.isBuffer(o)?o.toString("utf8"):""}function tt(o){if(!o||typeof o!="object")return String(o);const t=o,e=[t.message||String(o)],s=C(t.stdout).trim(),n=C(t.stderr).trim();return s&&e.push(`---stdout---
2
2
  ${s}`),n&&e.push(`---stderr---
3
3
  ${n}`),t.code!==void 0&&e.push(`exitCode: ${String(t.code)}`),t.signal&&e.push(`signal: ${t.signal}`),e.join(`
4
4
  `)}function et(o,t={}){return[{name:"read_file",label:"\u8BFB\u53D6\u6587\u4EF6",description:"Read the full content of a file at the given path.",parameters:p.Object({path:p.String({description:"Absolute or relative file path"})}),async execute(e,{path:s}){let n="";try{return n=y(o,s),{content:[{type:"text",text:l.readFileSync(n,"utf-8")}],details:{path:n}}}catch(i){return{content:[{type:"text",text:`Error: ${i instanceof Error?i.message:String(i)}`}],details:{path:n}}}}},{name:"write_file",label:"\u5199\u5165\u6587\u4EF6",description:"Write content to a file, creating it or overwriting if it exists.",parameters:p.Object({path:p.String({description:"Absolute or relative file path"}),content:p.String({description:"File content to write"})}),async execute(e,{path:s,content:n}){let i="";try{return i=y(o,s),l.mkdirSync(g.dirname(i),{recursive:!0}),l.writeFileSync(i,n,"utf-8"),{content:[{type:"text",text:`Written ${n.length} bytes to ${i}`}],details:{path:i}}}catch(a){return{content:[{type:"text",text:`Error: ${a instanceof Error?a.message:String(a)}`}],details:{path:i}}}}},{name:"list_directory",label:"\u5217\u51FA\u76EE\u5F55",description:"List files and directories at the given path.",parameters:p.Object({path:p.String({description:"Directory path to list",default:"."})}),async execute(e,{path:s}){let n="";try{return n=y(o,s),{content:[{type:"text",text:l.readdirSync(n,{withFileTypes:!0}).map(h=>`${h.isDirectory()?"d":"f"} ${h.name}`).join(`
5
5
  `)||"(empty)"}],details:{path:n}}}catch(i){return{content:[{type:"text",text:`Error: ${i instanceof Error?i.message:String(i)}`}],details:{path:n}}}}},{name:"bash",label:"\u6267\u884C\u547D\u4EE4",description:"Execute a shell command in the working directory. Uses PowerShell on Windows and bash on macOS/Linux. Timeout: 30s.",parameters:p.Object({command:p.String({description:"Shell command to execute"})}),async execute(e,{command:s},n){try{const{stdout:i,stderr:a,shell:h}=await Z(o,s,t,n);return{content:[{type:"text",text:[i,a].filter(Boolean).join(`
6
6
  ---stderr---
7
- `)||"(no output)"}],details:{command:s,shell:h}}}catch(i){return{content:[{type:"text",text:`Error: ${tt(i)}`}],details:{command:s,shell:P(s).shell}}}}},{name:"read_image",label:"\u8BFB\u53D6\u56FE\u7247",description:"Read an image file and return its content for visual analysis. Supports png, jpg, jpeg, gif, webp. Max 10 MB.",parameters:p.Object({path:p.String({description:"Absolute or relative path to the image file"})}),async execute(e,{path:s}){let n;try{n=y(o,s)}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],details:{path:s}}}const i=g.extname(n).toLowerCase().slice(1),h={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp"}[i];if(!h)return{content:[{type:"text",text:`Error: unsupported image type ".${i}". Supported: png, jpg, jpeg, gif, webp`}],details:{path:n}};try{const r=l.statSync(n);return r.size>10*1024*1024?{content:[{type:"text",text:`Error: image too large (${(r.size/1024/1024).toFixed(1)} MB, max 10 MB)`}],details:{path:n}}:{content:[{type:"image",data:l.readFileSync(n).toString("base64"),mimeType:h}],details:{path:n,mimeType:h,size:r.size}}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],details:{path:n}}}}}]}class st extends D{type="pi";agent=null;sessionId=null;workDir=null;seq=0;runId="";emittedLengths=new Map;terminalState="open";authToken=null;proxyUrl=null;providerConfig;sessionDir=null;messagesPath=null;snapshotPath=null;summaryPath=null;cachedSummary=null;restoredMessages=[];compressing=!1;lastSummaryMsgCount=0;pendingBaseMessages=[];finalizePromise=Promise.resolve();sendGeneration=0;externalChannel=null;shennianSessionId=null;extraEnv={};pendingSendStart=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,s){this.sessionId=t,this.workDir=e,this.seq=0,this.sessionDir=z(t),this.messagesPath=g.join(this.sessionDir,K),this.snapshotPath=g.join(this.sessionDir,Y),this.summaryPath=g.join(this.sessionDir,V),this.loadSnapshot(),this.loadSummary()}async send(t,e){const s=++this.sendGeneration,n=this.agent&&this.terminalState==="open"?S(this.pendingBaseMessages):null;n&&(this.agent?.abort(),this.agent=null,this.restoredMessages=n,this.finalizePromise=Promise.resolve());const i=L(),a=i.machineToken??i.accessToken;if(!a){this.emit("agentEvent",{state:"error",runId:this.runId,seq:++this.seq,message:"\u672A\u914D\u5BF9\uFF0C\u8BF7\u5148\u8FD0\u884C shennian \u5B8C\u6210\u673A\u5668\u914D\u5BF9"});return}this.authToken=a,this.proxyUrl=(i.serverUrl??N.cn.url).replace(/\/$/,"");const h=O("pi"),r=i.apiKeys?.dashscope?.trim();this.providerConfig=h??(r?{agent:"pi",token:r,updatedAt:""}:void 0),this.runId=b(),this.seq=0,this.emittedLengths.clear(),this.terminalState="open",this.agent||this.initAgent(),await this.finalizePromise.catch(()=>{}),this.agent?.setModel(M(e??J)),this.pendingBaseMessages=S(this.agent?.state.messages??[]);const d=this.runId,m=new Promise((c,u)=>{this.pendingSendStart={runId:d,resolve:c,reject:u}});this.emit("agentEvent",{state:"init",runId:d,seq:++this.seq}),this.agent.prompt(t).then(async()=>{s===this.sendGeneration&&(this.resolvePendingSendStart(d),await this.finalizePromise.catch(()=>{}))}).catch(c=>{if(s!==this.sendGeneration||(this.rejectPendingSendStart(d,c),this.terminalState!=="open"))return;this.terminalState="error";const u=c instanceof Error?c.message:String(c);u.includes("429")||u.includes("daily_quota_exceeded")||u.includes("nian_quota_exceeded")?this.emit("agentEvent",{state:"error",runId:d,seq:++this.seq,message:"Nian \u4ECA\u65E5\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u6B21\u65E5\u81EA\u52A8\u6062\u590D\u3002"}):this.emit("agentEvent",{state:"error",runId:d,seq:++this.seq,message:u})}),await m}initAgent(){const t=this.workDir??process.cwd(),e=et(t,this.extraEnv),s=U(t),n=new T({initialState:{systemPrompt:[W,s,`\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1A${t}`,F(this.externalChannel,t,this.shennianSessionId??void 0)].filter(Boolean).join(`
7
+ `)||"(no output)"}],details:{command:s,shell:h}}}catch(i){return{content:[{type:"text",text:`Error: ${tt(i)}`}],details:{command:s,shell:P(s).shell}}}}},{name:"read_image",label:"\u8BFB\u53D6\u56FE\u7247",description:"Read an image file and return its content for visual analysis. Supports png, jpg, jpeg, gif, webp. Max 10 MB.",parameters:p.Object({path:p.String({description:"Absolute or relative path to the image file"})}),async execute(e,{path:s}){let n;try{n=y(o,s)}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],details:{path:s}}}const i=g.extname(n).toLowerCase().slice(1),h={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp"}[i];if(!h)return{content:[{type:"text",text:`Error: unsupported image type ".${i}". Supported: png, jpg, jpeg, gif, webp`}],details:{path:n}};try{const r=l.statSync(n);return r.size>10*1024*1024?{content:[{type:"text",text:`Error: image too large (${(r.size/1024/1024).toFixed(1)} MB, max 10 MB)`}],details:{path:n}}:{content:[{type:"image",data:l.readFileSync(n).toString("base64"),mimeType:h}],details:{path:n,mimeType:h,size:r.size}}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],details:{path:n}}}}}]}class st extends D{type="pi";agent=null;sessionId=null;workDir=null;seq=0;runId="";emittedLengths=new Map;terminalState="open";authToken=null;proxyUrl=null;providerConfig;sessionDir=null;messagesPath=null;snapshotPath=null;summaryPath=null;cachedSummary=null;restoredMessages=[];compressing=!1;lastSummaryMsgCount=0;pendingBaseMessages=[];finalizePromise=Promise.resolve();sendGeneration=0;externalChannel=null;shennianSessionId=null;extraEnv={};pendingSendStart=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,s){this.sessionId=t,this.workDir=e,this.seq=0,this.sessionDir=z(t),this.messagesPath=g.join(this.sessionDir,K),this.snapshotPath=g.join(this.sessionDir,Y),this.summaryPath=g.join(this.sessionDir,V),this.loadSnapshot(),this.loadSummary()}async send(t,e){const s=++this.sendGeneration,n=this.agent&&this.terminalState==="open"?S(this.pendingBaseMessages):null;n&&(this.agent?.abort(),this.agent=null,this.restoredMessages=n,this.finalizePromise=Promise.resolve());const i=L(),a=i.machineToken??i.accessToken;if(!a){this.emit("agentEvent",{state:"error",runId:this.runId,seq:++this.seq,message:"\u672A\u914D\u5BF9\uFF0C\u8BF7\u5148\u8FD0\u884C shennian \u5B8C\u6210\u673A\u5668\u914D\u5BF9"});return}this.authToken=a,this.proxyUrl=(i.serverUrl??N.cn.url).replace(/\/$/,"");const h=O("pi"),r=i.apiKeys?.dashscope?.trim();this.providerConfig=h??(r?{agent:"pi",token:r,updatedAt:""}:void 0),this.runId=b(),this.seq=0,this.emittedLengths.clear(),this.terminalState="open",this.agent||this.initAgent(),await this.finalizePromise.catch(()=>{}),this.agent?.setModel(M(e??J)),this.pendingBaseMessages=S(this.agent?.state.messages??[]);const d=this.runId,m=new Promise((c,u)=>{this.pendingSendStart={runId:d,resolve:c,reject:u}});this.emit("agentEvent",{state:"init",runId:d,seq:++this.seq}),this.agent.prompt(t).then(async()=>{s===this.sendGeneration&&(this.resolvePendingSendStart(d),await this.finalizePromise.catch(()=>{}))}).catch(c=>{if(s!==this.sendGeneration||(this.rejectPendingSendStart(d,c),this.terminalState!=="open"))return;this.terminalState="error";const u=c instanceof Error?c.message:String(c);u.includes("429")||u.includes("daily_quota_exceeded")||u.includes("nian_quota_exceeded")?this.emit("agentEvent",{state:"error",runId:d,seq:++this.seq,message:"Nian \u4ECA\u65E5\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u6B21\u65E5\u81EA\u52A8\u6062\u590D\u3002"}):this.emit("agentEvent",{state:"error",runId:d,seq:++this.seq,message:u})}),await m}async getStatus(){const t=!!(this.runId&&this.terminalState==="open");return{active:t,runId:this.runId||null,runPhase:null,canStop:t}}initAgent(){const t=this.workDir??process.cwd(),e=et(t,this.extraEnv),s=U(t),n=new I({initialState:{systemPrompt:[W,s,`\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1A${t}`,F(this.externalChannel,t,this.shennianSessionId??void 0)].filter(Boolean).join(`
8
8
 
9
- `),model:M(),tools:e},streamFn:(i,a,h)=>{const r=this.providerConfig;return r?.token?j(M(i.id,{provider:"dashscope",baseUrl:r.baseUrl||Q,compat:{supportsDeveloperRole:!1,supportsStore:!1,supportsReasoningEffort:!1,maxTokensField:"max_tokens",thinkingFormat:"qwen"}}),a,{...h,apiKey:r.token}):I(i,a,{...h,authToken:this.authToken,proxyUrl:this.proxyUrl})},transformContext:i=>this.compressContext(i)});this.agent=n,this.restoredMessages.length>0?n.replaceMessages(S(this.restoredMessages)):this.cachedSummary&&(n.appendMessage({role:"user",content:[{type:"text",text:`[\u4E4B\u524D\u7684\u5BF9\u8BDD\u6458\u8981]
9
+ `),model:M(),tools:e},streamFn:(i,a,h)=>{const r=this.providerConfig;return r?.token?j(M(i.id,{provider:"dashscope",baseUrl:r.baseUrl||Q,compat:{supportsDeveloperRole:!1,supportsStore:!1,supportsReasoningEffort:!1,maxTokensField:"max_tokens",thinkingFormat:"qwen"}}),a,{...h,apiKey:r.token}):T(i,a,{...h,authToken:this.authToken,proxyUrl:this.proxyUrl})},transformContext:i=>this.compressContext(i)});this.agent=n,this.restoredMessages.length>0?n.replaceMessages(S(this.restoredMessages)):this.cachedSummary&&(n.appendMessage({role:"user",content:[{type:"text",text:`[\u4E4B\u524D\u7684\u5BF9\u8BDD\u6458\u8981]
10
10
  ${this.cachedSummary}`}],timestamp:Date.now()}),n.appendMessage({role:"assistant",content:[{type:"text",text:"\u597D\u7684\uFF0C\u6211\u5DF2\u4E86\u89E3\u4E4B\u524D\u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u8BF7\u7EE7\u7EED\u3002"}],timestamp:Date.now()})),n.subscribe(i=>{if(this.agent!==n||this.terminalState!=="open")return;const a=++this.seq,h=this.runId;switch(i.type){case"agent_start":this.resolvePendingSendStart(h),this.emit("agentEvent",{state:"start",runId:h,seq:a});break;case"message_update":{const r=i.message;if(r.role!=="assistant")break;const d=r.content??[];for(let m=0;m<d.length;m++){const c=d[m];if(c.type==="text"&&c.text){const u=`text:${m}`,f=this.emittedLengths.get(u)??0;if(c.text.length>f){const x=c.text.slice(f);this.emittedLengths.set(u,c.text.length),this.emit("agentEvent",{state:"delta",runId:h,seq:a,text:x})}}else if(c.type==="thinking"&&c.thinking){const u=`thinking:${m}`,f=this.emittedLengths.get(u)??0;if(c.thinking.length>f){const x=c.thinking.slice(f);this.emittedLengths.set(u,c.thinking.length),this.emit("agentEvent",{state:"delta",runId:h,seq:a,text:x,thinking:!0})}}}break}case"tool_execution_start":this.emit("agentEvent",{state:"tool-call",runId:h,seq:a,name:i.toolName,args:i.args});break;case"tool_execution_end":this.emit("agentEvent",{state:"tool-result",runId:h,seq:a,name:i.toolName,result:typeof i.result=="string"?i.result:JSON.stringify(i.result)});break;case"agent_end":{this.terminalState="final";const r=i.messages,m=[...r].reverse().find(c=>c.role==="assistant")?.usage;this.emit("agentEvent",{state:"final",runId:h,seq:a,usage:m?{inputTokens:m.input??0,outputTokens:m.output??0}:void 0}),this.finalizePromise=this.finalizeTurn(r);break}}})}async compressAndReplace(){if(!this.agent||this.compressing)return;const t=this.agent.state.messages;if(w(t)<v)return;const s=this.splitMessages(t);if(!s)return;const n=await this.generateSummary(s.toCompress);if(!n)return;this.cachedSummary=n,this.lastSummaryMsgCount+=s.toCompress.length;const i=[{role:"user",content:[{type:"text",text:`[\u5386\u53F2\u5BF9\u8BDD\u6458\u8981]
11
11
  ${n}`}],timestamp:Date.now()},{role:"assistant",content:[{type:"text",text:"\u597D\u7684\uFF0C\u6211\u5DF2\u4E86\u89E3\u4E0A\u4E0B\u6587\u3002"}],timestamp:Date.now()},...s.toKeep];this.agent.replaceMessages(i)}async compressContext(t){if(w(t)<v||!this.cachedSummary)return t;const s=this.splitMessages(t);return s?[{role:"user",content:[{type:"text",text:`[\u5386\u53F2\u5BF9\u8BDD\u6458\u8981]
12
12
  ${this.cachedSummary}`}],timestamp:Date.now()},...s.toKeep]:t}splitMessages(t){const e=t.filter(n=>n.role!=="system");if(e.length<=k)return null;let s=e.length-k;for(;s>0&&e[s].role==="toolResult";)s--;return s<=0?null:{toCompress:e.slice(0,s),toKeep:e.slice(s)}}async generateSummary(t){if(!this.authToken||!this.proxyUrl||this.compressing)return this.cachedSummary;this.compressing=!0;try{const e=G(t);if(e.length<100)return null;const s=e.length>1e4?e.slice(0,1e4)+`
@@ -1,7 +1,7 @@
1
- import W from"node:fs";import J from"node:path";import{WeChatChannelApiError as De}from"./client.js";import{defaultWeChatChannelAttachmentDir as Le,resolveVisibleWeChatChannelMedia as Ke}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as $e,loadWeChatChannelVectorStore as ze,saveWeChatChannelVectorStore as Ve,upsertWeChatChannelVectorReferences as qe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as Z,normalizeWeChatChannelMessageKind as je}from"./core/schema.js";import{waitForWeChatChannelPacing as Q,weChatChannelPacingDelayMs as Ge}from"./pacing.js";import{matchWeChatConversationFingerprints as He}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as ee}from"./anchor.js";import{decideWeChatChannelActivityGate as Ue,normalizeWeChatChannelActivitySnapshot as Ye}from"./human-coordination.js";const Xe=3,Je=5,Ze="wechat_login_required",Qe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function Rn(e){return await ut(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>et({...e,helper:n}))}async function et(e){const t=e.foregroundMode??"required";let n=await tt(e,{foregroundMode:t});if(t!=="background"&&(n=await Ie(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=E({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!O(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(Y({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),de(e)?le({...e,window:n,capture:s,ocr:a}):q({...e,window:n,capture:s,ocr:a})}const r=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const s=await se({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await Ie(e.helper,s,e.traceId);const a=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(Y({capture:i,ocr:o,binding:e.binding})){n=await Gt(e.helper,e.traceId);const s=await G({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),Y({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),de(e)?le({...e,window:n,capture:i,ocr:o}):q({...e,window:n,capture:i,ocr:o})}async function tt(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Vt(e.helper,e.traceId,{foreground:n});return n==="background"?r:se({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const x=new Map;function nt(){return process.env.SHENNIAN_WECHAT_VISION_LAYOUT_REUSE!=="0"}function te(e){if(!nt()||!e.classification||e.classification.windowKind!=="chat_main")return;const t=e.window.windowId;if(!t)return;const n=ae(e.window),r=F(e.classification.layout?.messageInputRect,e.screenshot,e.window,"message-input"),i=F(e.classification.layout?.searchInputRect,e.screenshot,e.window,"search-input");if(!r&&!i)return;const o={windowId:t,boundsKey:n,messageInputPoint:r,searchInputPoint:i,updatedAt:new Date().toISOString()},s=`${e.runtime.runtimeId}:${e.binding.bindingId}:${t}`,a=`${e.runtime.runtimeId}:window:${t}:${n}`;x.set(s,o),x.set(a,o),R(e,s,o),R(e,a,o)}async function Fn(e){const t=await ne(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function rt(e){const t=await ne(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function ne(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=ae(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,s=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,a=x.get(o)??x.get(s)??ie(e,o)??ie(e,s);if(a&&a.windowId===r&&a.boundsKey===i&&(!t||a[t]))return x.set(o,a),x.set(s,a),a;const c=e.screenshot??await T(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=F(l.layout?.messageInputRect,c,e.window,"message-input"),u=F(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return x.set(o,d),x.set(s,d),R(e,o,d),R(e,s,d),d}function re(e){const t=e.workDir;return t?J.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function ie(e,t){const n=re(e);if(!n)return null;try{const r=JSON.parse(W.readFileSync(n,"utf8"));return it(r[t])}catch{return null}}function R(e,t,n){const r=re(e);if(r)try{let i={};try{i=JSON.parse(W.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,W.mkdirSync(J.dirname(r),{recursive:!0}),W.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
- `,"utf8")}catch{}}function it(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=oe(t.messageInputPoint??t.point),o=oe(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function oe(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function ae(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function F(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),s=Number(e?.width),a=Number(e?.height);if(![i,o,s,a].every(Number.isFinite)||s<=0||a<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:s,height:a,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:s/999*t.width,height:a/999*t.height,coordinateSpace:"screenshotPixel"},l=ot(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function ot(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=at({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function at(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),s=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-s)/i}}async function se(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(D).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=dt(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const s of r)try{const a=await T(e.helper,s.windowId,e.traceId,s.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:a.mimeType,dataBase64:a.dataBase64,width:a.width,height:a.height,windowId:s.windowId},traceId:e.traceId});i.push({window:s,classification:c,screenshot:a,score:lt(c,e.requireTargetConversation===!0)})}catch(a){if(st(a))throw a;i.push({window:s,classification:null,errorSummary:a instanceof Error?a.message:String(a||"window classification failed"),score:-1})}const o=i.filter(s=>s.score>0).sort((s,a)=>a.score-s.score)[0];if(!o){const s=i.map(a=>({windowId:a.window.windowId,appName:a.window.appName,title:a.window.title,score:a.score,errorSummary:a.errorSummary,classification:a.classification?{windowKind:a.classification.windowKind,reasonCode:a.classification.reasonCode,confidence:a.classification.confidence,conversationTitle:a.classification.conversationTitle,isTargetConversation:a.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.screenshot&&te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:o.window,classification:o.classification,screenshot:o.screenshot}),o.window}async function An(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function st(e){const t=ct(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ct(e){if(e instanceof De||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function dt(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function lt(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ut(e,t,n){if(!t)return{helper:e,withLease:async(a,c,l)=>await l(e)};let r=null;const i=a=>{t.onEvent?.({...a,at:new Date().toISOString()})},o=async(a,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},f=u.ok&&d.interrupted?ce(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:a,action:c,ok:u.ok===!0&&!d.interrupted,...f?{reasonCode:f}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:ce(d.interruptReason),stage:a,action:c})}const w=Date.now()+ht(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:a,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});const d=Ue({snapshot:Ye(u.result),stage:a,policy:t.policy});if(i({phase:"snapshot",stage:a,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const f=new N({reasonCode:d.reasonCode,stage:a,action:c});if(!l.settle)throw f;const X=w-Date.now();if(X<=0)throw f;await U(Math.max(50,Math.min(d.waitMs,X)))}},s={request:async(a,c,l)=>{const w=wt(a);w&&await o(w.stage,`${a}:before`);const u=await e.request(a,c,l);return w&&u.ok&&await o(w.stage,`${a}:after`),u}};return{helper:s,async withLease(a,c,l){if(await Lt(e,n),await o(a,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(s);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:a,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:a,action:c});try{const u=await l(s);return await o(a,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function wt(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function ce(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function ht(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Qe}function de(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function q(e){const t=await Se(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return We(r,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function le(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Se(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(ft(c))return q(e);throw c}const r=It(n.structuredMessages??n.observedMessages??[]),i=await mt({...e,messages:r,screenshot:t}),o=await fe({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),s=We(o,Pe({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function ft(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function mt(e){const t=bt(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await yt({...e,seedBlocks:t}):t.filter(s=>h(s.vectorBase64)||h(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??$e(e.workDir||process.cwd(),e.runtime.runtimeId),i=ze(r,e.runtime.runtimeId),o=qe({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ve(r,i),{messages:pt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function bt(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,s=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:s,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function gt(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return vt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function yt(e){try{return await gt(e)}catch(t){if(xt(t))throw t;return[]}}function xt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function vt(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function pt(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:Ct(r,i)}:r})}function Ct(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=h(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(l=>l.blockId===a);return c?ue(c,o):we(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(ue(o));return r}function ue(e,t){return{...t?we(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function we(e){const{vectorBase64:t,...n}=e;return n}function It(e){return e.map((t,n)=>_t(t,n)).filter(t=>t!=null)}function _t(e,t){if(!b(e))return null;const n=je(e.kind),r=Mt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=h(e.stableMessageKey)||kt({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:s,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:St(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:s}}}function kt(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function St(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),b(t.attachment)&&(t.attachment=he(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?he(n):n)),t}function he(e){const t={...e};return I(t,"availability")&&(t.availability=Z(t.availability)),t}function Mt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function fe(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>Rt(o,e.screenshot,e.window,Wt(e.messages,s))).filter((o,s)=>Nt(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Le(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ke({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const s=i.get(o.stableMessageKey);return s?{...o,mediaMetadata:Ft(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function Nt(e,t,n){const r=be(n);if(!r)return!0;const i=Tt(r),o=e.map((s,a)=>be(Pt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===Bt(e,i);for(const s of o)if(t>=s&&t<=s+8||t===s-1&&!e.slice(s+1,Math.min(e.length,s+9)).some(me))return!0;return!1}function Tt(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Bt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(me(e[n]))return Et(e[n],t)?n:-1;return-1}function me(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return ge(t,n,r)}function Et(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),s=String(i.mimeType||"").toLowerCase(),a=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||s.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(a):t==="image"?/image|photo|picture/.test(n)||o==="image"||s.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(a):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!s.startsWith("image/")&&!s.startsWith("video/")}function Pt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
3
- `)}function be(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Wt(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
4
- `)||null}function Rt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!ge(i,o,s))return null;const l=A(e.bbox)??A(o.bbox)??A(o.downloadActionBbox),w=A(o.downloadActionBbox),u=ye(l,t,n),d=Dt(l,t,n),f=ye(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:At(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Ft(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function At(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ge(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function A(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function ye(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Dt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Dn(e,t){await ve(e,t)}async function Lt(e,t){await xe(e,t)}async function xe(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if($t(r.platform)){const i=await Kt(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function ve(e,t,n={}){const r=await xe(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(jt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function Kt(e,t,n){const r=Ot(t);if(!r||process.platform!=="darwin")return r;const i=zt(r);try{await e.request(i,{},n)}catch{}return r}function Ot(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function $t(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function zt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Vt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ve(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await qt(e,l,t)}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}async function qt(e,t,n){if(pe(t.title))return t;try{const r=await e.request("windows.list",{},n);m(r,"windows.list");const o=(r.result?.windows??[]).find(s=>s.windowId&&s.visible!==!1&&s.minimized!==!0&&D(s)&&pe(s.title));if(!o)return t;if(t.windowId&&t.windowId!==o.windowId)try{await e.request("windows.closeWindow",{windowId:t.windowId},n)}catch{}return o}catch{return t}}function pe(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function jt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(D),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function Ln(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Gt(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(D);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function j(e){const{capture:t,ocr:n}=e.observation??await k(e),r=E({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:on(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=pn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function G(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Ht(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Ht(e){const t=await k(e),n=E({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await j({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await ke(e);if(f.opened)return f}}const i=await Qt(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await Ut(e);if(s.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:u.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await tn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const l=await j({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await j(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Ut(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await U(n);const o=await Yt(e);t=o;const{capture:s,ocr:a}=o,c=E({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=cn(a,s,e.binding.conversationDisplayName);if(!l){if(dn(a)||ln(a,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,s,Xt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Yt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Xt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Jt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Q("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Jt(e){const t=Zt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Zt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(a?.bbox,t,n)}async function Qt(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function H(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),s=await Me(e,o,n);return{capture:o,ocr:s}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function D(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function U(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Ge(t,en(e,n))}async function S(e,t,n){const r=L(e,t,n);r>0&&await U(r)}function en(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function tn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Cn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await Q("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await H(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=E({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!nn(n)&&await rn({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function nn(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function rn(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(te({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function E(e){const n=(e.ocr.blocks??[]).map(r=>wn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function on(e,t,n=!0){const r=n?an(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function an(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!sn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function sn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||un(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function cn(e,t,n){const r=(e.blocks??[]).map(a=>Ne(a)).filter(a=>!!a),i=r.filter(a=>K(p(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>p(d.text)===a);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Te(a.text)).filter(a=>!Ee(a,r)).filter(a=>z(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Be(s,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function dn(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function ln(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function un(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function wn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const l=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return l||w}):!1}function Y(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
- `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((a,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return a+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(a=>Number(a.bbox?.y)<e.capture.height*.14).map(a=>String(a.text||"")).join(`
6
- `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&hn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?P(a.title):V(a.title),l=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):V(e.binding.conversationDisplayName);return!!(c&&l&&Re(c,l))}),!1)}function hn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
- `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function Pe(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=fn(e.ocr,e.screenshot).sort((o,s)=>v(s.text).length-v(o.text).length);for(const o of i){const s=v(o.text);if(!s)continue;const a=t.find(l=>mn(l.normalized,s));if(!a)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${a.idempotencyKey||a.replyId||a.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:s,anchorText:s,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function fn(e,t){const n=(e.blocks??[]).filter(i=>vn(i,t)&&v(i.text)).sort((i,o)=>C(i.bbox,"y")-C(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let s=i+1;s<n.length&&o.length<5;s+=1){const a=n[s];if(!bn(o[o.length-1],a,t))break;o.push(a),r.push({text:gn(o.map(c=>String(c.text||"").trim())),bbox:xn(o.map(c=>c.bbox))})}}return r}function mn(e,t){if(!e||!t)return!1;if(ee(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function bn(e,t,n){const r=C(e.bbox,"x"),i=C(e.bbox,"y"),o=C(e.bbox,"height"),s=C(t.bbox,"x"),a=C(t.bbox,"y");if(![r,i,o,s,a].every(Number.isFinite)||r<n.width*.5||s<n.width*.5)return!1;const c=a-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(s-r)<=120}function gn(e){return e.reduce((t,n)=>t?n?`${t}${yn(t,n)?" ":""}${n}`:t:n,"")}function yn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function xn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const s=Math.min(...n),a=Math.min(...r);return{x:s,y:a,width:Math.max(...i)-s,height:Math.max(...o)-a,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function We(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=v(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&ee(a,i)>=.9})||n.push(r)}return n}function vn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function pn(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(s*.35,110*l),y:o+Math.min(a-8*w,Math.max(18*w,a/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),f=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-f)/w}}function $(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+s/2,y:o+a/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),f=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-f)/u}}function Cn(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),s=i.bbox;if(!s||!o)return!1;const a=Number(s.x),c=Number(s.y);return!Number.isFinite(a)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:a>t.width*.55&&c>t.height*.45&&c<t.height*.92});return $(r?.bbox,t,n)}function Re(e,t){return y(t)?Ae(e,t):e===t||e.includes(t)||t.includes(e)}function z(e,t){if(y(t))return Ae(e,t);const n=Fe(p(e)),r=Fe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Fe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function V(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ae(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,H as captureAndRecognizeWeChatWindow,T as captureWeChatWindow,Fn as detectWeChatMessageInputPointByServerVision,rt as detectWeChatSearchInputPointByServerVision,Lt as ensureHelperPermissionPreflight,Dn as ensureHelperPreflight,Vt as ensureWeChatWindowReady,Gt as focusExistingWeChatWindow,Ie as focusKnownWeChatWindow,Ln as focusWeChatWindow,Rn as observeWeChatChannelBindingViaHelper,G as openConversationBySearch,j as openConversationInVisibleList,Me as recognizeWeChatScreenshot,tt as selectWeChatWindowForBinding};
1
+ import R from"node:fs";import Z from"node:path";import{WeChatChannelApiError as Le}from"./client.js";import{defaultWeChatChannelAttachmentDir as De,resolveVisibleWeChatChannelMedia as Ke}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as $e,loadWeChatChannelVectorStore as ze,saveWeChatChannelVectorStore as Ve,upsertWeChatChannelVectorReferences as qe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as Q,normalizeWeChatChannelMessageKind as je}from"./core/schema.js";import{waitForWeChatChannelPacing as ee,weChatChannelPacingDelayMs as Ge}from"./pacing.js";import{matchWeChatConversationFingerprints as He}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as te}from"./anchor.js";import{decideWeChatChannelActivityGate as Ue,normalizeWeChatChannelActivitySnapshot as Ye}from"./human-coordination.js";const Xe=3,Je=5,Ze="wechat_login_required",Qe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function An(e){return await ft(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>et({...e,helper:n}))}async function et(e){const t=e.foregroundMode??"required";let n=await tt(e,{foregroundMode:t});if(t!=="background"&&(n=await Ie(e.helper,n,e.traceId)),t==="background"){const{capture:a,ocr:s}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=W({capture:a,ocr:s,window:n});if(c)throw new Error(c);if(!O(s,a,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(X({capture:a,ocr:s,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:a,ocr:s}),le(e)?ue({...e,window:n,capture:a,ocr:s}):q({...e,window:n,capture:a,ocr:s})}const r=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const a=await ce({...e,fallback:n,requireTargetConversation:!1,force:!0});if(a.windowId!==n.windowId){n=await Ie(e.helper,a,e.traceId);const s=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(X({capture:i,ocr:o,binding:e.binding})){n=await Ut(e.helper,e.traceId);const a=await H({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),X({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),le(e)?ue({...e,window:n,capture:i,ocr:o}):q({...e,window:n,capture:i,ocr:o})}async function tt(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await jt(e.helper,e.traceId,{foreground:n});return n==="background"?r:ce({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const x=new Map;function nt(){return process.env.SHENNIAN_WECHAT_VISION_LAYOUT_REUSE!=="0"}function ne(e){if(!nt()||!e.classification||e.classification.windowKind!=="chat_main")return;const t=e.window.windowId;if(!t)return;const n=se(e.window),r=A(e.classification.layout?.messageInputRect,e.screenshot,e.window,"message-input"),i=A(e.classification.layout?.searchInputRect,e.screenshot,e.window,"search-input");if(!r&&!i)return;const o={windowId:t,boundsKey:n,messageInputPoint:r,searchInputPoint:i,updatedAt:new Date().toISOString()},a=`${e.runtime.runtimeId}:${e.binding.bindingId}:${t}`,s=`${e.runtime.runtimeId}:window:${t}:${n}`;x.set(a,o),x.set(s,o),F(e,a,o),F(e,s,o)}async function Ln(e){const t=await re(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function rt(e){const t=await re(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function re(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=se(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,a=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,s=x.get(o)??x.get(a)??oe(e,o)??oe(e,a);if(s&&s.windowId===r&&s.boundsKey===i&&(!t||s[t]))return x.set(o,s),x.set(a,s),s;const c=e.screenshot??await T(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=A(l.layout?.messageInputRect,c,e.window,"message-input"),u=A(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return x.set(o,d),x.set(a,d),F(e,o,d),F(e,a,d),d}function ie(e){const t=e.workDir;return t?Z.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function oe(e,t){const n=ie(e);if(!n)return null;try{const r=JSON.parse(R.readFileSync(n,"utf8"));return it(r[t])}catch{return null}}function F(e,t,n){const r=ie(e);if(r)try{let i={};try{i=JSON.parse(R.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,R.mkdirSync(Z.dirname(r),{recursive:!0}),R.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
+ `,"utf8")}catch{}}function it(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=ae(t.messageInputPoint??t.point),o=ae(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function ae(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function se(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function A(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),a=Number(e?.width),s=Number(e?.height);if(![i,o,a,s].every(Number.isFinite)||a<=0||s<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:a,height:s,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:a/999*t.width,height:s/999*t.height,coordinateSpace:"screenshotPixel"},l=ot(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function ot(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=at({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function at(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),a=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-a)/i}}async function ce(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(B).filter(a=>a.visible!==!1&&a.minimized!==!0&&!!a.windowId),r=dt(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const a of r)try{const s=await T(e.helper,a.windowId,e.traceId,a.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:s.mimeType,dataBase64:s.dataBase64,width:s.width,height:s.height,windowId:a.windowId},traceId:e.traceId});i.push({window:a,classification:c,screenshot:s,score:lt(c,e.requireTargetConversation===!0)})}catch(s){if(st(s))throw s;i.push({window:a,classification:null,errorSummary:s instanceof Error?s.message:String(s||"window classification failed"),score:-1})}const o=i.filter(a=>a.score>0).sort((a,s)=>s.score-a.score)[0];if(!o){const a=ut(i,e.fallback);if(a)return console.warn(`[wechat-channel] window classifier fallback to local Windows WeChat shell windowId=${a.window.windowId}`),a.window;const s=i.map(c=>({windowId:c.window.windowId,appName:c.window.appName,title:c.window.title,score:c.score,errorSummary:c.errorSummary,classification:c.classification?{windowKind:c.classification.windowKind,reasonCode:c.classification.reasonCode,confidence:c.classification.confidence,conversationTitle:c.classification.conversationTitle,isTargetConversation:c.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.screenshot&&ne({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:o.window,classification:o.classification,screenshot:o.screenshot}),o.window}async function Dn(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function st(e){const t=ct(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ct(e){if(e instanceof Le||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function dt(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function lt(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ut(e,t){const n=e.filter(i=>i.score<=0).filter(i=>i.classification?.reasonCode==="server_window_classified").filter(i=>wt(i.window));return n.length===1?n[0]:n.find(i=>i.window.windowId===t.windowId)??null}function wt(e){if(!B(e)||e.visible===!1||e.minimized===!0||!j(e.title))return!1;const t=String(e.className??"");if(!/^Qt\d+QWindowIcon$/i.test(t))return!1;const n=e.bounds;return n?Number(n.width)>=700&&Number(n.height)>=500:!1}function ft(e,t,n){if(!t)return{helper:e,withLease:async(s,c,l)=>await l(e)};let r=null;const i=s=>{t.onEvent?.({...s,at:new Date().toISOString()})},o=async(s,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},h=u.ok&&d.interrupted?de(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:s,action:c,ok:u.ok===!0&&!d.interrupted,...h?{reasonCode:h}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:de(d.interruptReason),stage:s,action:c})}const w=Date.now()+mt(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:s,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});const d=Ue({snapshot:Ye(u.result),stage:s,policy:t.policy});if(i({phase:"snapshot",stage:s,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const h=new N({reasonCode:d.reasonCode,stage:s,action:c});if(!l.settle)throw h;const J=w-Date.now();if(J<=0)throw h;await Y(Math.max(50,Math.min(d.waitMs,J)))}},a={request:async(s,c,l)=>{const w=ht(s);w&&await o(w.stage,`${s}:before`);const u=await e.request(s,c,l);return w&&u.ok&&await o(w.stage,`${s}:after`),u}};return{helper:a,async withLease(s,c,l){if(await Ot(e,n),await o(s,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(a);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:s,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:s,action:c});try{const u=await l(a);return await o(s,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(h=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:h instanceof Error?h.message:String(h)}));i({phase:"lease-release",stage:s,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function ht(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function de(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function mt(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Qe}function le(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function q(e){const t=await Se(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await me({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return Pe(r,We({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function ue(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Se(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(bt(c))return q(e);throw c}const r=kt(n.structuredMessages??n.observedMessages??[]),i=await gt({...e,messages:r,screenshot:t}),o=await me({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=Pe(o,We({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function bt(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function gt(e){const t=yt(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await vt({...e,seedBlocks:t}):t.filter(a=>f(a.vectorBase64)||f(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??$e(e.workDir||process.cwd(),e.runtime.runtimeId),i=ze(r,e.runtime.runtimeId),o=qe({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ve(r,i),{messages:It(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function yt(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=f(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=f(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function xt(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return Ct(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function vt(e){try{return await xt(e)}catch(t){if(pt(t))throw t;return[]}}function pt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function Ct(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:f(n.stableMessageKey)||f(i?.stableMessageKey),blockId:f(n.blockId)||f(i?.blockId),blockKind:f(n.blockKind)||f(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>f(n.stableMessageKey)&&f(n.blockId))}function It(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:_t(r,i)}:r})}function _t(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=f(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(l=>l.blockId===s);return c?we(c,o):fe(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(we(o));return r}function we(e,t){return{...t?fe(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function fe(e){const{vectorBase64:t,...n}=e;return n}function kt(e){return e.map((t,n)=>St(t,n)).filter(t=>t!=null)}function St(e,t){if(!b(e))return null;const n=je(e.kind),r=Tt(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=f(e.stableMessageKey)||Mt({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:Nt(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function Mt(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||f(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function Nt(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=Q(t.availability)),b(t.attachment)&&(t.attachment=he(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?he(n):n)),t}function he(e){const t={...e};return I(t,"availability")&&(t.availability=Q(t.availability)),t}function Tt(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function me(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>At(o,e.screenshot,e.window,Ft(e.messages,a))).filter((o,a)=>Bt(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??De(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ke({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=i.get(o.stableMessageKey);return a?{...o,mediaMetadata:Lt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Bt(e,t,n){const r=ge(n);if(!r)return!0;const i=Et(r),o=e.map((a,s)=>ge(Rt(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Wt(e,i);for(const a of o)if(t>=a&&t<=a+8||t===a-1&&!e.slice(a+1,Math.min(e.length,a+9)).some(be))return!0;return!1}function Et(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Wt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(be(e[n]))return Pt(e[n],t)?n:-1;return-1}function be(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return ye(t,n,r)}function Pt(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),a=String(i.mimeType||"").toLowerCase(),s=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function Rt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
3
+ `)}function ge(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Ft(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
4
+ `)||null}function At(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=f(a.availability)||f(o.availability),c=f(a.localPath)||f(o.localPath);if(s==="edge-local"&&c||!ye(i,o,a))return null;const l=L(e.bbox)??L(o.bbox)??L(o.downloadActionBbox),w=L(o.downloadActionBbox),u=xe(l,t,n),d=Kt(l,t,n),h=xe(w,t,n);return{messageKey:e.stableMessageKey,kind:f(a.type)||f(a.kind)||f(o.messageType)||i,fileName:f(a.name)||f(a.fileName)||f(o.fileName)||null,mimeType:f(a.mimeType)||f(o.mimeType)||null,size:_(a.size)??_(o.size),mediaStatus:Dt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...h?{downloadActionBbox:h}:{}}}function Lt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function Dt(e,t){const n=f(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="loading"||n==="downloading"||n==="in_progress")return"loading";if(n==="downloaded"||n==="available")return"available";const r=f(t.availability)||f(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function ye(e,t,n){const r=`${e} ${f(t.messageType)} ${f(n.type)} ${f(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function L(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function xe(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=$(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,a=i.height/t.height,s=r.x,c=r.y;return{x:s-e.width*o/2,y:c-e.height*a/2,width:e.width*o,height:e.height*a,coordinateSpace:"screen"}}function Kt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function f(e){return typeof e=="string"?e.trim():""}function g(e){return f(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Kn(e,t){await pe(e,t)}async function Ot(e,t){await ve(e,t)}async function ve(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{};if(Vt(r.platform)){const i=await $t(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing")}return r}async function pe(e,t,n={}){const r=await ve(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(Ht(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function $t(e,t,n){const r=zt(t);if(!r||process.platform!=="darwin")return r;const i=qt(r);try{await e.request(i,{},n)}catch{}return r}function zt(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function Vt(e){const t=String(e||"").toLowerCase();return t?t.includes("darwin")||t.includes("mac"):!0}function qt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function jt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await pe(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return await Gt(e,l,t)}const a=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(a,"windows.ensureReady");const s=a.result;if(!s?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return s}async function Gt(e,t,n){if(j(t.title))return t;try{const r=await e.request("windows.list",{},n);m(r,"windows.list");const o=(r.result?.windows??[]).find(a=>a.windowId&&a.visible!==!1&&a.minimized!==!0&&B(a)&&j(a.title));if(!o)return t;if(t.windowId&&t.windowId!==o.windowId)try{await e.request("windows.closeWindow",{windowId:t.windowId},n)}catch{}return o}catch{return t}}function j(e){const t=String(e??"").normalize("NFKC").trim().toLowerCase();return t==="\u5FAE\u4FE1"||t==="wechat"||t==="weixin"}function Ht(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function Ce(e){const t=e.filter(B),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function On(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function Ie(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function Ut(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=Ce(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await _e(e,i,t)}async function _e(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(B);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function G(e){const{capture:t,ocr:n}=e.observation??await k(e),r=W({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=He({visibleItems:sn(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=In(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(a,"mouse.click"),{opened:!0,reason:i.reason}}async function H(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Xe;r+=1){if(t=await Yt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await ke(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Yt(e){const t=await k(e),n=W({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await G({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const h=await ke(e);if(h.opened)return h}}const i=await tn(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??D(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const a=await Xt(e);if(a.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason}):{opened:!1,reason:u.reason}}let s=a.observation;const c=y(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await rn(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??D(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),s=void 0}const l=await G({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function ke(e){const t=await G(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await E({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Xt(e){let t;const n=e.searchDelayMs===0?0:D(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Je:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await Y(n);const o=await Jt(e);t=o;const{capture:a,ocr:s}=o,c=W({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=ln(s,a,e.binding.conversationDisplayName);if(!l){if(un(s)||wn(s,e.binding.conversationDisplayName))break;continue}const w=$(l.bbox,a,Zt(a,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Jt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Me(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function Zt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Qt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await ee("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Qt(e){const t=en(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function en(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-a)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return $(s?.bbox,t,n)}async function tn(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await rt({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function T(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Se(e){return e.capture.dataBase64?e.capture:T(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function U(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await T(e,t,n,r),a=await Me(e,o,n);return{capture:o,ocr:a}}async function Me(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function B(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function Y(e){return new Promise(t=>setTimeout(t,e))}function D(e,t,n){return e.searchDelayMs===0?0:Ge(t,nn(e,n))}async function S(e,t,n){const r=D(e,t,n);r>0&&await Y(r)}function nn(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function rn(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function E(e){if(!e.window.bounds)return;const t=e.observation,n=_n(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await ee("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=W({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:!on(n)&&await an({...e,capture:t})?{ok:!0,reason:"target-conversation-title-confirmed-by-vision"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function on(e){return(e.blocks??[]).length>0?!0:String(e.text??"").trim().length>0}async function an(e){if(!e.runtime||!e.api?.classifyWindow)return!1;try{const t=e.capture.dataBase64?e.capture:await T(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height,windowId:e.window.windowId},traceId:e.traceId});return n.windowKind!=="chat_main"||!(n.isTargetConversation===!0||z(n.conversationTitle,e.binding.conversationDisplayName))?!1:(ne({helper:e.helper,runtime:e.runtime,binding:e.binding,workDir:e.workDir,traceId:e.traceId,window:e.window,classification:n,screenshot:t}),!0)}catch{return!1}}function W(e){const n=(e.ocr.blocks??[]).map(r=>hn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ze:null}function sn(e,t,n=!0){const r=n?cn(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function cn(e,t){const n=e.map(i=>Ne(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Te(i.text)),r=new Set;return n.filter(i=>!dn(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function Ne(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![r,i,o,a].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:a}}function dn(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Te(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||K(t)||fn(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function ln(e,t,n){const r=(e.blocks??[]).map(s=>Ne(s)).filter(s=>!!s),i=r.filter(s=>K(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=i.findIndex(d=>p(d.text)===s);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!K(p(d.text))).filter(d=>!Ee(d,r)).filter(d=>z(d.text,n)).sort((d,h)=>d.y-h.y||d.x-h.x)[0];if(u)return{bbox:Be(u,t),reason:"search-result-title"}}const a=r.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!Te(s.text)).filter(s=>!Ee(s,r)).filter(s=>z(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:Be(a,t),reason:"search-result-title"}:null}function Be(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function K(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function un(e){return(e.blocks??[]).some(t=>K(p(t.text)))}function wn(e,t){return(e.visibleConversationFingerprints??[]).some(n=>z(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ee(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function fn(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function hn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):V(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):V(i.text);if(!o||!Re(o,r))return!1;const a=i.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const l=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,w=s<t.width*.18&&c<t.height*.12;return l||w}):!1}function X(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
+ `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((s,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return s+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(s=>Number(s.bbox?.y)<e.capture.height*.14).map(s=>String(s.text||"")).join(`
6
+ `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&mn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?P(s.title):V(s.title),l=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):V(e.binding.conversationDisplayName);return!!(c&&l&&Re(c,l))}),!1)}function mn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
+ `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function We(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=bn(e.ocr,e.screenshot).sort((o,a)=>v(a.text).length-v(o.text).length);for(const o of i){const a=v(o.text);if(!a)continue;const s=t.find(l=>gn(l.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:a,anchorText:a,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function bn(e,t){const n=(e.blocks??[]).filter(i=>Cn(i,t)&&v(i.text)).sort((i,o)=>C(i.bbox,"y")-C(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let a=i+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!yn(o[o.length-1],s,t))break;o.push(s),r.push({text:xn(o.map(c=>String(c.text||"").trim())),bbox:pn(o.map(c=>c.bbox))})}}return r}function gn(e,t){if(!e||!t)return!1;if(te(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function yn(e,t,n){const r=C(e.bbox,"x"),i=C(e.bbox,"y"),o=C(e.bbox,"height"),a=C(t.bbox,"x"),s=C(t.bbox,"y");if(![r,i,o,a,s].every(Number.isFinite)||r<n.width*.5||a<n.width*.5)return!1;const c=s-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-r)<=120}function xn(e){return e.reduce((t,n)=>t?n?`${t}${vn(t,n)?" ":""}${n}`:t:n,"")}function vn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function pn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...r);return{x:a,y:s,width:Math.max(...i)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Pe(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=v(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&te(s,i)>=.9})||n.push(r)}return n}function Cn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function In(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(a*.35,110*l),y:o+Math.min(s-8*w,Math.max(18*w,s/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),h=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-h)/w}}function $(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),h=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-h)/u}}function _n(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),a=i.bbox;if(!a||!o)return!1;const s=Number(a.x),c=Number(a.y);return!Number.isFinite(s)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:s>t.width*.55&&c>t.height*.45&&c<t.height*.92});return $(r?.bbox,t,n)}function Re(e,t){return y(t)?Ae(e,t):e===t||e.includes(t)||t.includes(e)}function z(e,t){if(y(t))return Ae(e,t);const n=Fe(p(e)),r=Fe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Fe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function V(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ae(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,U as captureAndRecognizeWeChatWindow,T as captureWeChatWindow,Ln as detectWeChatMessageInputPointByServerVision,rt as detectWeChatSearchInputPointByServerVision,Ot as ensureHelperPermissionPreflight,Kn as ensureHelperPreflight,jt as ensureWeChatWindowReady,Ut as focusExistingWeChatWindow,Ie as focusKnownWeChatWindow,On as focusWeChatWindow,An as observeWeChatChannelBindingViaHelper,H as openConversationBySearch,G as openConversationInVisibleList,Me as recognizeWeChatScreenshot,tt as selectWeChatWindowForBinding};
@@ -73,3 +73,4 @@ export declare function weChatChannelOutboundLedgerPath(workDir: string, runtime
73
73
  export declare function weChatChannelHelperTracePath(workDir: string, runtimeId: string): string;
74
74
  export declare function safeWeChatChannelRuntimePathSegment(runtimeId: string): string;
75
75
  export declare function observedMessageToExternalEvent(config: ExternalChannelConfig, binding: WeChatChannelBindingConfig, message: WeChatChannelObservedMessage): ExternalMessageEvent | null;
76
+ export declare function observedMessagesToExternalEvent(config: ExternalChannelConfig, binding: WeChatChannelBindingConfig, messages: WeChatChannelObservedMessage[]): ExternalMessageEvent | null;
@@ -1,9 +1,16 @@
1
- import w from"node:crypto";import m from"node:fs";import f from"node:path";import{loadConfig as N}from"../../config/index.js";import{createWeChatChannelProductRunner as O}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as k}from"../wechat-channel/client.js";import{createWeChatAutomationLane as $}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as h,saveWeChatChannelOutboundLedger as I}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as S}from"../wechat-channel/ledger.js";const z="wechat-channel";function de(e){const t=D({runtimeId:e.config.id,machineId:Q(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:j(e.secret.forceForeground),bindings:B(e.config,e.secret)}),n=P(e.config.workDir,t.runtimeId),a=u(e.config.workDir,t.runtimeId),r=F(e.config.workDir,t.runtimeId),o=[];m.mkdirSync(f.dirname(r),{recursive:!0}),m.writeFileSync(r,"");const s=e.createRunner??O,c=L(),d=s({runtime:t,workDir:e.config.workDir,api:c,ledgerPath:n,outboundLedgerPath:a,lane:$(),helperRequestLogger:H(r),onInboundMessages:(C,A)=>{const M=t.bindings.find(p=>p.bindingId===C.bindingId)??C;for(const p of A){const y=x(e.config,M,p);if(!y)continue;const W=e.onMessage?.({...y,managerSessionId:e.config.managerSessionId})??y;o.push(W)}}});return{runtime:t,runner:d,emitted:o,ledgerPath:n,outboundLedgerPath:a,helperTracePath:r}}function L(){let e=null;const t=()=>(e??=k(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function j(e){return e===!1?"polite":"work"}function ue(e){const t=e.config.id,n=R(e.config,e.conversationName),a=S(P(e.config.workDir,t),t),r=u(e.config.workDir,t),o=h(r,t),s=e.reply.attachment?[J(e.reply.attachment)]:[],c=K({ledger:o,replyId:e.reply.messageId||e.reply.idempotencyKey||l("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||l("wechat-channel-send",`${e.config.id}
1
+ import C from"node:crypto";import p from"node:fs";import y from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as $}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as O}from"../wechat-channel/client.js";import{createWeChatAutomationLane as k}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as b,saveWeChatChannelOutboundLedger as S}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as P}from"../wechat-channel/ledger.js";const j="wechat-channel";function fe(e){const t=D({runtimeId:e.config.id,machineId:ee(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:E(e.secret.forceForeground),bindings:q(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),o=f(e.config.workDir,t.runtimeId),r=F(e.config.workDir,t.runtimeId),a=[];p.mkdirSync(y.dirname(r),{recursive:!0}),p.writeFileSync(r,"");const c=e.createRunner??$,l=z(),u=c({runtime:t,workDir:e.config.workDir,api:l,ledgerPath:n,outboundLedgerPath:o,lane:k(),helperRequestLogger:B(r),onInboundMessages:(h,s)=>{const m=t.bindings.find(N=>N.bindingId===h.bindingId)??h,g=v(e.config,m,s);if(!g)return;const R=e.onMessage?.({...g,managerSessionId:e.config.managerSessionId})??g;a.push(R)}});return{runtime:t,runner:u,emitted:a,ledgerPath:n,outboundLedgerPath:o,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=O(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function E(e){return e===!1?"polite":"work"}function he(e){const t=e.config.id,n=A(e.config,e.conversationName),o=P(x(e.config.workDir,t),t),r=f(e.config.workDir,t),a=b(r,t),c=e.reply.attachment?[X(e.reply.attachment)]:[],l=K({ledger:a,replyId:e.reply.messageId||e.reply.idempotencyKey||d("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||d("wechat-channel-send",`${e.config.id}
2
2
  ${e.reply.conversationId}
3
3
  ${e.reply.text}
4
- ${s.join(`
5
- `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:a.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return I(r,o),c}function me(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function fe(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function he(e){const t=Y(e.limit,20,1,100),n=S(e.product.ledgerPath,e.product.runtime.runtimeId),a=[];for(const r of e.product.runtime.bindings){const o=n.bindings[r.bindingId]?.recent??[];for(const s of o.slice(-t)){const c=x(e.config,r,s);c&&a.push(c)}}return a}function ge(e){const t=u(e.config.workDir,e.config.id),n=h(t,e.config.id),a=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!a&&!r)return{cancelled:!1};const o=n.records.find(c=>a&&c.idempotencyKey===a||r&&c.replyId===r);if(!o)return{cancelled:!1};const s=o.sendStatus;return T(o,e.reason||"user_cancelled"),o.sendStatus!==s?(I(t,n),{cancelled:!0,status:o.sendStatus,record:o}):{cancelled:!1,status:o.sendStatus,record:o}}function pe(e){return e===z}function E(e){return l("wechat-conversation",i(e))}function _(e,t){return l("wechat-channel-binding",`${e}
6
- ${i(t)}`)}function P(e,t){return f.join(e,"wechat-channel",`${b(t)}.ledger.json`)}function u(e,t){return f.join(e,"wechat-channel",`${b(t)}.outbound-ledger.json`)}function F(e,t){return f.join(e,"wechat-channel",`${b(t)}.helper-trace.jsonl`)}function b(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function H(e){return t=>{m.appendFileSync(e,`${JSON.stringify(q(t))}
7
- `)}}function q(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=g(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=g(e.result)),t}function g(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:w.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const o=e.slice(0,10).map(s=>g(s,t,n+1));return e.length>o.length?{type:"array",length:e.length,items:o}:o}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const a=e,r={};for(const[o,s]of Object.entries(a))r[o]=g(s,o,n+1);return r}function B(e,t){return Z(t).map(n=>({bindingId:R(e,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function R(e,t){return e.managedBy==="session-sync"||e.id.startsWith("wechat-binding:")?e.id:_(e.id,t)}function x(e,t,n){const a=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=V(n);if(!a&&r.length===0)return null;const o=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:E(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:l("wechat-message",`${t.bindingId}
8
- ${n.stableMessageKey}`),sender:{id:l("wechat-sender",`${t.bindingId}
9
- ${o||n.senderRole}`),name:o||null},text:a,attachments:r,receivedAt:X(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function V(e){const t=e.mediaMetadata;return v(t).map(a=>{const r=a,o=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",c=typeof r.url=="string"?i(r.url):"",d=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...c?{url:c}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(d)&&d>0?{size:d}:{},...o?{localPath:o}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:U(r.availability,{localPath:o,url:c}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...G(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function v(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function U(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function G(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function J(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!m.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function Z(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function Q(){return process.env.SHENNIAN_MACHINE_ID?.trim()||N().machineId||"local"}function X(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function Y(e,t,n,a){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(a,Math.floor(r))):t}function l(e,t){return`${e}:${w.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{z as WECHAT_RPA_PRODUCT_SOURCE,ge as cancelWeChatRpaProductOutboundReply,me as countPendingWeChatRpaProductOutbound,de as createWeChatRpaProductSourceConnection,ue as enqueueWeChatRpaProductOutboundReply,pe as isWeChatRpaProductSource,fe as listPendingWeChatRpaProductOutbound,he as listWeChatRpaProductRecentMessages,x as observedMessageToExternalEvent,b as safeWeChatChannelRuntimePathSegment,_ as weChatChannelBindingId,E as weChatChannelConversationId,F as weChatChannelHelperTracePath,P as weChatChannelLedgerPath,u as weChatChannelOutboundLedgerPath};
4
+ ${c.join(`
5
+ `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:o.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:c});return S(r,a),l}function ge(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function pe(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ye(e){const t=ne(e.limit,20,1,100),n=P(e.product.ledgerPath,e.product.runtime.runtimeId),o=[];for(const r of e.product.runtime.bindings){const a=n.bindings[r.bindingId]?.recent??[];for(const c of a.slice(-t)){const l=M(e.config,r,c);l&&o.push(l)}}return o}function be(e){const t=f(e.config.workDir,e.config.id),n=b(t,e.config.id),o=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!o&&!r)return{cancelled:!1};const a=n.records.find(l=>o&&l.idempotencyKey===o||r&&l.replyId===r);if(!a)return{cancelled:!1};const c=a.sendStatus;return T(a,e.reason||"user_cancelled"),a.sendStatus!==c?(S(t,n),{cancelled:!0,status:a.sendStatus,record:a}):{cancelled:!1,status:a.sendStatus,record:a}}function we(e){return e===j}function L(e){return d("wechat-conversation",i(e))}function _(e,t){return d("wechat-channel-binding",`${e}
6
+ ${i(t)}`)}function x(e,t){return y.join(e,"wechat-channel",`${I(t)}.ledger.json`)}function f(e,t){return y.join(e,"wechat-channel",`${I(t)}.outbound-ledger.json`)}function F(e,t){return y.join(e,"wechat-channel",`${I(t)}.helper-trace.jsonl`)}function I(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function B(e){return t=>{p.appendFileSync(e,`${JSON.stringify(H(t))}
7
+ `)}}function H(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=w(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=w(e.result)),t}function w(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:C.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const a=e.slice(0,10).map(c=>w(c,t,n+1));return e.length>a.length?{type:"array",length:e.length,items:a}:a}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[a,c]of Object.entries(o))r[a]=w(c,a,n+1);return r}function q(e,t){return Y(t).map(n=>({bindingId:A(e,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function A(e,t){return e.managedBy==="session-sync"||e.id.startsWith("wechat-binding:")?e.id:_(e.id,t)}function M(e,t,n){const o=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=V(n);if(!o&&r.length===0)return null;const a=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:L(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:d("wechat-message",`${t.bindingId}
8
+ ${n.stableMessageKey}`),sender:{id:d("wechat-sender",`${t.bindingId}
9
+ ${a||n.senderRole}`),name:a||null},text:o,attachments:r,receivedAt:te(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function v(e,t,n){const o=n.map(s=>M(e,t,s)).filter(s=>s!=null);if(o.length<=1)return o[0]??null;const r=o[0],a=n.map(s=>i(s.stableMessageKey||"")).filter(Boolean),l=new Set(o.map(s=>i(s.sender.name||""))).size===1?r.sender.name??null:null,u=new Set(o.map(s=>s.sender.id)).size===1?r.sender.id:d("wechat-sender-batch",`${t.bindingId}
10
+ ${a.join(`
11
+ `)}`),h=o.map(s=>{const m=i(s.text);return m?l!==null?m:`${i(s.sender.name||"")||"\u5BF9\u65B9"}: ${m}`:""}).filter(Boolean).join(`
12
+ `);return{...r,messageId:d("wechat-message-batch",`${t.bindingId}
13
+ ${a.join(`
14
+ `)}`),sender:{id:u,name:l},text:h,attachments:G(o.flatMap(s=>s.attachments)),receivedAt:J(o.map(s=>s.receivedAt)),rawRef:a.join(`
15
+ `)}}function V(e){const t=e.mediaMetadata;return U(t).map(o=>{const r=o,a=typeof r.localPath=="string"?i(r.localPath):"",c=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",l=typeof r.url=="string"?i(r.url):"",u=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...l?{url:l}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(u)&&u>0?{size:u}:{},...a?{localPath:a}:{},...c?{thumbnailPath:c}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:Z(r.availability,{localPath:a,url:l}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...Q(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function U(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function G(e){const t=new Set,n=[];for(const o of e){const r=[o.localPath||"",o.url||"",o.thumbnailPath||"",o.hash||"",o.name||"",o.mimeType||"",o.type||""].join(`
16
+ `);t.has(r)||(t.add(r),n.push(o))}return n}function J(e){let t=0;for(const n of e){const o=Date.parse(n);Number.isFinite(o)&&(t=Math.max(t,o))}return t?new Date(t).toISOString():new Date().toISOString()}function Z(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function Q(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function X(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!p.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function Y(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function ee(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function te(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function ne(e,t,n,o){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(o,Math.floor(r))):t}function d(e,t){return`${e}:${C.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{j as WECHAT_RPA_PRODUCT_SOURCE,be as cancelWeChatRpaProductOutboundReply,ge as countPendingWeChatRpaProductOutbound,fe as createWeChatRpaProductSourceConnection,he as enqueueWeChatRpaProductOutboundReply,we as isWeChatRpaProductSource,pe as listPendingWeChatRpaProductOutbound,ye as listWeChatRpaProductRecentMessages,M as observedMessageToExternalEvent,v as observedMessagesToExternalEvent,I as safeWeChatChannelRuntimePathSegment,_ as weChatChannelBindingId,L as weChatChannelConversationId,F as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,f as weChatChannelOutboundLedgerPath};
@@ -1,24 +1,24 @@
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
1
+ import J from"node:http";import{randomBytes as Y,randomUUID as p}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 me}from"../channels/wechat-channel/client.js";import{createWeChatAutomationLane as pe}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),m=ye(d.id);n&&n.role===d.role&&s===h&&h&&m!==null&&o!==null&&m===o+1?(i+=l,n.ts=d.ts,o=m):(c(),n=d,s=h,o=m,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??pe(),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 me({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-${p()}`,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-${p()}`,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-${p()}`,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||p()),h=String(n.channelId||""),m=String(n.conversationId||""),C=!i&&(!h||!m)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,I=i?.channelId||h||C?.channelId||"",E=i?.conversationId||m||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(m){u(r,400,{ok:!1,...j(m,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-${p()}`,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-${p()}`,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-${p()}`,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-${p()}`,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
4
  Worker: ${r.sessionId}
5
5
  \u72B6\u6001\uFF1A${t}
6
6
  \u6700\u7EC8\u7ED3\u679C\uFF1A
7
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,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
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,messageId:r.messageId,attachments:l,externalChannel:Ee(n),replyTarget:r.replyTarget})}async dispatchExternalMessage(e){const r=typeof e.messageId=="string"&&e.messageId.trim()?e.messageId.trim():void 0;await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",queueMessageId:r,clientMessageId:r,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,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}
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-${p()}`,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
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(`
21
+ ${h}`;return s.has(C)||n.includes(h)?"":(s.add(C),`${r}: ${l}`)}const m=l;return s.has(m)?"":(s.add(m),`${r}: ${l}`)}).filter(i=>!n.includes(i)).join(`
22
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
23
  `);return[X({conversationName:a.conversationName||a.conversationId,messages:[]}),s].filter(Boolean).join(`
24
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};
@@ -18,5 +18,6 @@ export declare class ChatQueueManager {
18
18
  private drainNext;
19
19
  private mergeExternalMessages;
20
20
  private dispatchQueuedMessage;
21
+ private isSessionBusy;
21
22
  private broadcast;
22
23
  }
@@ -1,4 +1,4 @@
1
- import l from"node:fs";import{randomUUID as m}from"node:crypto";import{resolveShennianPath as f}from"../config/index.js";import{mergeProjectedSessions as I}from"./projection.js";import{materializeRemoteChatAttachments as x}from"./remote-attachments.js";const y=f("chat-queue.json");function w(){return{sessions:{}}}function h(){return new Date().toISOString()}function S(i){return`enqueue-send-${i}-${m()}`}function r(){try{const i=JSON.parse(l.readFileSync(y,"utf-8"));return{sessions:i.sessions&&typeof i.sessions=="object"?i.sessions:{}}}catch{return w()}}function u(i){l.mkdirSync(f(""),{recursive:!0}),l.writeFileSync(y,JSON.stringify(i,null,2))}function p(i){if(!Array.isArray(i))return;const e=i.map(s=>{if(!s||typeof s!="object")return null;const t=s,n={path:typeof t.path=="string"?t.path:"",name:typeof t.name=="string"?t.name:"",mimeType:typeof t.mimeType=="string"?t.mimeType:"",...typeof t.previewData=="string"&&t.previewData.trim()?{previewData:t.previewData.trim()}:{}};return n.path&&n.name&&n.mimeType?n:null}).filter(s=>s!=null);return e.length?e:void 0}function M(i){const e=h();return{id:i.queueMessageId||i.clientMessageId||`queue-${m()}`,sessionId:i.sessionId,text:i.text,agentType:i.agentType,workDir:i.workDir,agentSessionId:i.agentSessionId??null,modelId:i.modelId??null,reasoningEffort:i.reasoningEffort??null,clientMessageId:i.clientMessageId??null,attachments:p(i.attachments),externalChannel:i.externalChannel??null,replyTarget:i.replyTarget??null,origin:i.origin,createdAt:e,updatedAt:e}}class Q{opts;draining=new Set;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:r().sessions[e]??[]}}async handleEnqueue(e){const s=this.opts.getRuntime(),t=e.params;if(I(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const n=p(t.attachments),o=n?.length?await x({text:t.text,attachments:n,workDir:t.workDir}):{text:t.text,attachments:n,localized:!1},d=!!s.sessions.get(t.sessionId)?.currentRunId;if(!d&&!r().sessions[t.sessionId]?.length){await this.opts.dispatchReq({...e,id:S(e.id),method:"chat.send",params:{...t,text:o.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:o.attachments}});return}const c=r(),g=M({...t,text:o.text,attachments:o.attachments});c.sessions[t.sessionId]=[...c.sessions[t.sessionId]??[],g],u(c),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:g.id,queue:this.getSnapshot(t.sessionId),...o.localized?{localizedAttachments:!0}:{}}}),d||this.drainNext(t.sessionId)}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}}),this.getSnapshot(t.sessionId).busy||this.drainNext(t.sessionId)}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const n=r(),o=n.sessions[t.sessionId]??[],a=o.findIndex(d=>d.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o[a]={...o[a],text:t.text,attachments:p(t.attachments),updatedAt:h()},n.sessions[t.sessionId]=o,u(n),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const n=r(),o=n.sessions[t.sessionId]??[],a=o.filter(d=>d.id!==t.queueMessageId);if(a.length===o.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}a.length?n.sessions[t.sessionId]=a:delete n.sessions[t.sessionId],u(n),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}noteTerminal(e){this.drainNext(e)}drainIdleQueues(){const e=r();for(const s of Object.keys(e.sessions))this.getSnapshot(s).busy||this.drainNext(s)}async drainNext(e){if(this.draining.has(e)||this.opts.getRuntime().sessions.get(e)?.currentRunId)return;const t=r(),n=t.sessions[e]??[],o=n.shift();if(!o){this.broadcast(e);return}const a=o.origin==="external"?this.mergeExternalMessages(o,n):o;n.length?t.sessions[e]=n:delete t.sessions[e],u(t),this.broadcast(e),this.draining.add(e);try{await this.dispatchQueuedMessage(a)}finally{this.draining.delete(e)}}mergeExternalMessages(e,s){const t=[e];for(;s[0]?.origin==="external";)t.push(s.shift());const n=R(t);return n.length===1?n[0]:{...e,id:`external-batch-${e.id}`,text:n.map(o=>o.text).filter(Boolean).join(`
1
+ import h from"node:fs";import{randomUUID as y}from"node:crypto";import{resolveShennianPath as I}from"../config/index.js";import{mergeProjectedSessions as w}from"./projection.js";import{materializeRemoteChatAttachments as S}from"./remote-attachments.js";const x=I("chat-queue.json");function M(){return{sessions:{}}}function p(){return new Date().toISOString()}function R(n){return`enqueue-send-${n}-${y()}`}function u(){try{const n=JSON.parse(h.readFileSync(x,"utf-8"));return{sessions:n.sessions&&typeof n.sessions=="object"?n.sessions:{}}}catch{return M()}}function d(n){h.mkdirSync(I(""),{recursive:!0}),h.writeFileSync(x,JSON.stringify(n,null,2))}function f(n){if(!Array.isArray(n))return;const e=n.map(s=>{if(!s||typeof s!="object")return null;const t=s,i={path:typeof t.path=="string"?t.path:"",name:typeof t.name=="string"?t.name:"",mimeType:typeof t.mimeType=="string"?t.mimeType:"",...typeof t.previewData=="string"&&t.previewData.trim()?{previewData:t.previewData.trim()}:{}};return i.path&&i.name&&i.mimeType?i:null}).filter(s=>s!=null);return e.length?e:void 0}function q(n){const e=p();return{id:n.queueMessageId||n.clientMessageId||`queue-${y()}`,sessionId:n.sessionId,text:n.text,agentType:n.agentType,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,reasoningEffort:n.reasoningEffort??null,clientMessageId:n.clientMessageId??null,attachments:f(n.attachments),externalChannel:n.externalChannel??null,replyTarget:n.replyTarget??null,origin:n.origin,createdAt:e,updatedAt:e}}class B{opts;draining=new Set;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:u().sessions[e]??[]}}async handleEnqueue(e){const s=this.opts.getRuntime(),t=e.params;if(w(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=f(t.attachments),r=i?.length?await S({text:t.text,attachments:i,workDir:t.workDir}):{text:t.text,attachments:i,localized:!1},a=await this.isSessionBusy(t.sessionId),o=u(),c=o.sessions[t.sessionId]??[];if(!a&&!c.length){await this.opts.dispatchReq({...e,id:R(e.id),method:"chat.send",params:{...t,text:r.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:r.attachments}});return}const l=q({...t,text:r.text,attachments:r.attachments}),m=t.origin==="external"?D(c,l):null;m||(o.sessions[t.sessionId]=[...c,l],d(o)),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:m?.id??l.id,queue:this.getSnapshot(t.sessionId),...r.localized?{localizedAttachments:!0}:{}}}),a||this.drainNext(t.sessionId)}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}}),this.getSnapshot(t.sessionId).busy||this.drainNext(t.sessionId)}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=u(),r=i.sessions[t.sessionId]??[],a=r.findIndex(o=>o.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}r[a]={...r[a],text:t.text,attachments:f(t.attachments),updatedAt:p()},i.sessions[t.sessionId]=r,d(i),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=u(),r=i.sessions[t.sessionId]??[],a=r.filter(o=>o.id!==t.queueMessageId);if(a.length===r.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}a.length?i.sessions[t.sessionId]=a:delete i.sessions[t.sessionId],d(i),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}noteTerminal(e){this.drainNext(e)}drainIdleQueues(){const e=u();for(const s of Object.keys(e.sessions))this.drainNext(s)}async drainNext(e){if(this.draining.has(e))return;const s=this.opts.getRuntime();if(await this.isSessionBusy(e))return;const t=u(),i=t.sessions[e]??[],r=i.shift();if(!r){this.broadcast(e);return}const a=r.origin==="external"?this.mergeExternalMessages(r,i):r;i.length?t.sessions[e]=i:delete t.sessions[e],d(t),this.broadcast(e),this.draining.add(e);try{await this.dispatchQueuedMessage(a)}finally{this.draining.delete(e)}}mergeExternalMessages(e,s){const t=[e];for(;s[0]?.origin==="external";)t.push(s.shift());const i=k(t);return i.length===1?i[0]:{...e,id:`external-batch-${e.id}`,text:i.map(r=>r.text).filter(Boolean).join(`
2
2
 
3
- `),attachments:q(n.flatMap(o=>o.attachments??[])),updatedAt:h()}}async dispatchQueuedMessage(e){await this.opts.dispatchReq({type:"req",id:`queue-send-${e.id}-${Date.now()}`,method:"chat.send",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??void 0,reasoningEffort:e.reasoningEffort??void 0,clientMessageId:e.clientMessageId??e.id,attachments:e.attachments,externalChannel:e.externalChannel,replyTarget:e.replyTarget,waitForDispatch:!0}})}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function R(i){const e=new Set,s=[];for(const t of i){const n=[k(t.text),...(t.attachments??[]).map(o=>o.path||`${o.name}:${o.mimeType}`)].join(`
4
- `);e.has(n)||(e.add(n),s.push(t))}return s}function q(i){const e=new Set,s=[];for(const t of i){const n=t.path||`${t.name}:${t.mimeType}`;e.has(n)||(e.add(n),s.push(t))}return s.length?s:void 0}function k(i){return String(i||"").replace(/\s+/g," ").trim()}export{Q as ChatQueueManager};
3
+ `),attachments:b(i.flatMap(r=>r.attachments??[])),updatedAt:p()}}async dispatchQueuedMessage(e){await this.opts.dispatchReq({type:"req",id:`queue-send-${e.id}-${Date.now()}`,method:"chat.send",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??void 0,reasoningEffort:e.reasoningEffort??void 0,clientMessageId:e.clientMessageId??e.id,attachments:e.attachments,externalChannel:e.externalChannel,replyTarget:e.replyTarget,waitForDispatch:!0}})}async isSessionBusy(e){const s=this.opts.getRuntime().sessions.get(e);if(!s)return!1;if(s.currentRunId)return!0;if(!s.adapter.getStatus)return!1;const t=await s.adapter.getStatus().catch(()=>null);return t?.active?(t.runId&&(s.currentRunId=t.runId,s.currentRunPhase=t.runPhase??s.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function k(n){const e=new Set,s=[];for(const t of n){const i=g(t);e.has(i)||(e.add(i),s.push(t))}return s}function D(n,e){const s=g(e);return n.find(t=>g(t)===s)??null}function g(n){const e=T(n);return e?`id:${e}`:["body",E(n.text),...(n.attachments??[]).map(s=>s.path||`${s.name}:${s.mimeType}`)].join(`
4
+ `)}function T(n){const e=typeof n.clientMessageId=="string"?n.clientMessageId.trim():"";if(e)return e;const s=typeof n.id=="string"?n.id.trim():"";return s&&!s.startsWith("queue-")?s:""}function b(n){const e=new Set,s=[];for(const t of n){const i=t.path||`${t.name}:${t.mimeType}`;e.has(i)||(e.add(i),s.push(t))}return s.length?s:void 0}function E(n){return String(n||"").replace(/\s+/g," ").trim()}export{B as ChatQueueManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.131",
3
+ "version": "0.2.133",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {