shennian 0.2.98 → 0.2.100

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 122,
5
+ "fileCount": 123,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -221,8 +221,8 @@
221
221
  },
222
222
  {
223
223
  "file": "src/channels/wechat-channel/helper-client.js",
224
- "beforeBytes": 15977,
225
- "afterBytes": 8671
224
+ "beforeBytes": 18296,
225
+ "afterBytes": 9771
226
226
  },
227
227
  {
228
228
  "file": "src/channels/wechat-channel/helper-protocol.js",
@@ -266,8 +266,8 @@
266
266
  },
267
267
  {
268
268
  "file": "src/channels/wechat-channel/observer.js",
269
- "beforeBytes": 80918,
270
- "afterBytes": 37541
269
+ "beforeBytes": 82424,
270
+ "afterBytes": 38164
271
271
  },
272
272
  {
273
273
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -276,8 +276,13 @@
276
276
  },
277
277
  {
278
278
  "file": "src/channels/wechat-channel/outbound-sender.js",
279
- "beforeBytes": 16842,
280
- "afterBytes": 8541
279
+ "beforeBytes": 17329,
280
+ "afterBytes": 8820
281
+ },
282
+ {
283
+ "file": "src/channels/wechat-channel/pacing.js",
284
+ "beforeBytes": 1673,
285
+ "afterBytes": 941
281
286
  },
282
287
  {
283
288
  "file": "src/channels/wechat-channel/preflight.js",
@@ -386,8 +391,8 @@
386
391
  },
387
392
  {
388
393
  "file": "src/commands/wechat.js",
389
- "beforeBytes": 32343,
390
- "afterBytes": 17929
394
+ "beforeBytes": 38003,
395
+ "afterBytes": 20530
391
396
  },
392
397
  {
393
398
  "file": "src/config/index.js",
@@ -401,8 +406,8 @@
401
406
  },
402
407
  {
403
408
  "file": "src/devtools/wechat-channel-action-smoke.js",
404
- "beforeBytes": 67091,
405
- "afterBytes": 35476
409
+ "beforeBytes": 68351,
410
+ "afterBytes": 36066
406
411
  },
407
412
  {
408
413
  "file": "src/devtools/wechat-channel-download-report.js",
@@ -466,8 +471,8 @@
466
471
  },
467
472
  {
468
473
  "file": "src/manager/runtime.js",
469
- "beforeBytes": 54318,
470
- "afterBytes": 27932
474
+ "beforeBytes": 57340,
475
+ "afterBytes": 29445
471
476
  },
472
477
  {
473
478
  "file": "src/native-fusion/config.js",
@@ -1,3 +1,3 @@
1
- import{spawn as d}from"node:child_process";import{randomUUID as w}from"node:crypto";import{createInterface as f}from"node:readline";import{decideWeChatChannelActivityGate as y,normalizeWeChatChannelActivitySnapshot as m}from"./human-coordination.js";import{createWeChatChannelHelperHello as g,extractWeChatChannelHelperWarmupSnapshot as S,timeoutForWeChatChannelHelperCommand as C,validateWeChatChannelHelperReady as W}from"./helper-protocol.js";class ${options;child=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.child&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.child&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=v(this.options.helperPath,this.options.args??[]),t=d(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=t,this.stderrTail="",this.lines=f({input:t.stdout}),this.lines.on("line",r=>this.handleLine(r)),t.stderr.on("data",r=>{this.captureStderr(r)}),t.stdin.on("error",r=>{this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),t.once("error",r=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),t.once("exit",(r,a)=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(r,a)))});const s=new Promise((r,a)=>{const o=setTimeout(()=>a(new Error("WeChat channel helper handshake timed out")),1e4),c=u=>{clearTimeout(o),this.lines?.off("line",h),t.off("exit",n),a(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,l)=>{clearTimeout(o),t.off("error",c),this.lines?.off("line",h),a(new Error(this.formatHelperExitMessage(u,l)))},h=u=>{let l;try{l=JSON.parse(u)}catch{clearTimeout(o),t.off("error",c),this.lines?.off("line",h),a(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!E(l))return;clearTimeout(o),t.off("error",c),t.off("exit",n),this.lines?.off("line",h);const p=W(l,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=l,this.captureWarmupSnapshot(l),r(l)):a(new Error(`${p.errorCode}: ${p.errorSummary}`))};t.once("error",c),t.once("exit",n),this.lines?.on("line",h)});try{t.stdin.write(`${JSON.stringify(g(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
- `)}catch(r){throw await this.stop().catch(()=>{}),r}return s}async request(e,t,s){const r=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,t,s));return this.requestQueueTail=r.then(()=>{},()=>{}),r}async sendRequest(e,t,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,t,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,t,s){if((!this.child||!this.readyState)&&await this.start(),!this.child)throw new Error("WeChat channel helper is not started");const r=w(),a=C(e),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:r,command:e,traceId:s,params:t,timeoutMs:a});const c=new Promise((n,h)=>{const u=setTimeout(()=>{this.pending.delete(r),h(new Error(`helper_command_timeout: ${e}`))},a);this.pending.set(r,{resolve:n,reject:h,timer:u})});try{this.child.stdin.write(`${JSON.stringify({id:r,command:e,params:t,traceId:s})}
3
- `)}catch(n){const h=this.pending.get(r);throw h&&clearTimeout(h.timer),this.pending.delete(r),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}try{const n=await c;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,ok:n.ok,errorCode:n.errorCode,errorSummary:n.errorSummary,latencyMs:n.latencyMs,result:n.result}),this.captureWarmupSnapshot(n),this.captureWarmupSnapshot(n.result),n}catch(n){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}}async guardUnsafeWindowsCommand(e,t){if(!this.options.guardUnsafeWindowsCommands||!_(e))return;const s=await this.sendRawRequest("permissions.check",{},t);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const r=s.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(r.duplicateInstanceBlocked===!0||Number(r.duplicateMainWindowCount??0)>0||Number(r.unsafeAuxiliaryWindowCount??0)>0)throw new Error("wechat_duplicate_instance");if(r.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&R(e)){const a=await this.sendRawRequest("activity.snapshot",{},t);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const o=y({snapshot:m(a.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,t)}async cleanupWindowsOverlaysAfterCommand(e,t){!this.options.guardUnsafeWindowsCommands||!b(e)||await this.cleanupWindowsOverlays("after",e,t)}async cleanupWindowsOverlays(e,t,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const r=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:t,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!r.ok)throw new Error(`${r.errorCode??"overlay_cleanup_failed"}: ${r.errorSummary??t}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child;this.child=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),!(!e||e.killed)&&e.kill("SIGTERM")}handleLine(e){let t;try{t=JSON.parse(e)}catch{return}if(!T(t))return;const s=this.pending.get(t.id);s&&(clearTimeout(s.timer),this.pending.delete(t.id),s.resolve(t))}captureWarmupSnapshot(e){const t=S(e);t&&(this.warmupState=t)}captureStderr(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");t&&(this.stderrTail=(this.stderrTail+t).slice(-2e3))}formatHelperExitMessage(e,t){const s=[e===null?null:`code=${e}`,t?`signal=${t}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[t,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(t)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}}function v(i,e){return{command:i,args:e}}function E(i){if(!i||typeof i!="object")return!1;const e=i;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function T(i){if(!i||typeof i!="object")return!1;const e=i;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function _(i){return i==="windows.focus"||i==="windows.capture"||i==="screen.capture"||i==="ocr.recognize"||i==="image.cropHash"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function b(i){return i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function R(i){return i==="windows.focus"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.set")||i==="clipboard.restore"||i==="menu.pickItem"||i==="savePanel.saveToPath"}export{$ as WeChatChannelHelperClient};
1
+ import{spawn as f}from"node:child_process";import{randomUUID as w}from"node:crypto";import{createInterface as m}from"node:readline";import{decideWeChatChannelActivityGate as y,normalizeWeChatChannelActivitySnapshot as g}from"./human-coordination.js";import{createWeChatChannelHelperHello as C,extractWeChatChannelHelperWarmupSnapshot as S,timeoutForWeChatChannelHelperCommand as W,validateWeChatChannelHelperReady as b}from"./helper-protocol.js";class H{options;child=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.child&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.child&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=_(this.options.helperPath,this.options.args??[]),i=f(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=i,this.stderrTail="",this.lines=m({input:i.stdout}),this.lines.on("line",r=>this.handleLine(r)),i.stderr.on("data",r=>{this.captureStderr(r)}),i.stdin.on("error",r=>{this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("error",r=>{this.child===i&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("exit",(r,a)=>{this.child===i&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(r,a)))});const s=new Promise((r,a)=>{const o=setTimeout(()=>a(new Error("WeChat channel helper handshake timed out")),1e4),c=u=>{clearTimeout(o),this.lines?.off("line",l),i.off("exit",n),a(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,h)=>{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error(this.formatHelperExitMessage(u,h)))},l=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!R(h))return;clearTimeout(o),i.off("error",c),i.off("exit",n),this.lines?.off("line",l);const d=b(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);d.ok?(this.readyState=h,this.captureWarmupSnapshot(h),r(h)):a(new Error(`${d.errorCode}: ${d.errorSummary}`))};i.once("error",c),i.once("exit",n),this.lines?.on("line",l)});try{i.stdin.write(`${JSON.stringify(C(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
+ `)}catch(r){throw await this.stop().catch(()=>{}),r}return s}async request(e,i,s){const r=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,i,s));return this.requestQueueTail=r.then(()=>{},()=>{}),r}async sendRequest(e,i,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,i,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,i,s){if((!this.child||!this.readyState)&&await this.start(),!this.child)throw new Error("WeChat channel helper is not started");const r=w(),a=W(e),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:r,command:e,traceId:s,params:i,timeoutMs:a});const c=new Promise((n,l)=>{const u=setTimeout(()=>{this.pending.delete(r),l(new Error(`helper_command_timeout: ${e}`))},a);this.pending.set(r,{resolve:n,reject:l,timer:u})});try{this.child.stdin.write(`${JSON.stringify({id:r,command:e,params:i,traceId:s})}
3
+ `)}catch(n){const l=this.pending.get(r);throw l&&clearTimeout(l.timer),this.pending.delete(r),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}try{const n=await c;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,ok:n.ok,errorCode:n.errorCode,errorSummary:n.errorSummary,latencyMs:n.latencyMs,result:n.result}),this.captureWarmupSnapshot(n),this.captureWarmupSnapshot(n.result),n}catch(n){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}}async guardUnsafeWindowsCommand(e,i){if(!this.options.guardUnsafeWindowsCommands||!A(e))return;const s=await this.sendRawRequest("permissions.check",{},i);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const r=s.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(v(r))throw new Error("wechat_login_required");if(r.duplicateInstanceBlocked===!0||Number(r.duplicateMainWindowCount??0)>0||Number(r.unsafeAuxiliaryWindowCount??0)>0)throw new Error("wechat_duplicate_instance");if(r.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&M(e)){const a=await this.sendRawRequest("activity.snapshot",{},i);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const o=y({snapshot:g(a.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,i)}async cleanupWindowsOverlaysAfterCommand(e,i){!this.options.guardUnsafeWindowsCommands||!q(e)||await this.cleanupWindowsOverlays("after",e,i)}async cleanupWindowsOverlays(e,i,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const r=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:i,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!r.ok)throw new Error(`${r.errorCode??"overlay_cleanup_failed"}: ${r.errorSummary??i}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child;this.child=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),!(!e||e.killed)&&e.kill("SIGTERM")}handleLine(e){let i;try{i=JSON.parse(e)}catch{return}if(!k(i))return;const s=this.pending.get(i.id);s&&(clearTimeout(s.timer),this.pending.delete(i.id),s.resolve(i))}captureWarmupSnapshot(e){const i=S(e);i&&(this.warmupState=i)}captureStderr(e){const i=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");i&&(this.stderrTail=(this.stderrTail+i).slice(-2e3))}formatHelperExitMessage(e,i){const s=[e===null?null:`code=${e}`,i?`signal=${i}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[i,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(i)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}}function v(t){if(Number(t.duplicateMainWindowCount??0)>0||(t.duplicateMainWindows??[]).some(p))return!1;const e=t.unsafeAuxiliaryWindows??[];return!(Number(t.unsafeAuxiliaryWindowCount??0)>0||e.some(T))&&!(t.duplicateInstanceBlocked===!0&&t.wechatWindowAvailable===!1)?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(E)||t.wechatWindowAvailable===!1}function p(t){const e=`${t.appName||""} ${t.title||""} ${t.className||""}`.toLowerCase();return e.includes("wechat")||e.includes("weixin")||e.includes("\u5FAE\u4FE1")||e.includes("qwindowicon")}function E(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);return!Number.isFinite(e)||!Number.isFinite(i)?!1:e>=760&&i>=520}function T(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);if(!Number.isFinite(e)||!Number.isFinite(i))return!1;const s=e>=360&&e<=720&&i>=420&&i<=840&&i>e,r=e>=420&&e<=680&&i>=260&&i<=460;return s||r}function _(t,e){return{command:t,args:e}}function R(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function k(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function A(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function q(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function M(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{H as WeChatChannelHelperClient};
@@ -104,6 +104,7 @@ export declare function openConversationInVisibleList(input: {
104
104
  binding: WeChatChannelBindingConfig;
105
105
  traceId?: string;
106
106
  observation?: WeChatChannelWindowObservation;
107
+ allowOcrTitleRows?: boolean;
107
108
  }): Promise<{
108
109
  opened: boolean;
109
110
  reason: string;
@@ -1,6 +1,6 @@
1
- import{defaultWeChatChannelAttachmentDir as pe,resolveVisibleWeChatChannelMedia as Ce}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Ne}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Ie,loadWeChatChannelVectorStore as Se,saveWeChatChannelVectorStore as Me,upsertWeChatChannelVectorReferences as ke}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as $,normalizeWeChatChannelMessageKind as Te}from"./core/schema.js";import{matchWeChatConversationFingerprints as _e}from"./fingerprint.js";import{normalizeWeChatAnchorText as p,weChatTextSimilarity as V}from"./anchor.js";const Be=3,We=5,Fe=420,q="wechat_login_required",F="wechat_duplicate_instance";async function jt(e){let t=await ot(e.helper,e.traceId);t=await lt(e.helper,t,e.traceId);const n=await ae({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!n.opened)throw new Error(n.reason||"conversation_not_opened");let{capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId});if(be({capture:r,ocr:i,binding:e.binding})){t=await ie(e.helper,e.traceId);const o=await ae({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!o.opened)throw new Error(o.reason||"wechat_window_obstructed");if({capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId}),be({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),Ee(e)?Ae({...e,window:t,capture:r,ocr:i}):j({...e,window:t,capture:r,ocr:i})}function Ee(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function j(e){const t=await de(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),r=await G({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return ge(r,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Ae(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await de(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]})}catch(c){if(Re(c))return j(e);throw c}const r=qe(n.structuredMessages??n.observedMessages??[]),i=await Oe({...e,messages:r,screenshot:t}),o=await G({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=ge(o,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Ne({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function Re(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function Oe(e){const t=Le(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Pe({...e,seedBlocks:t}):t.filter(a=>h(a.vectorBase64)||h(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Ie(e.workDir||process.cwd(),e.runtime.runtimeId),i=Se(r,e.runtime.runtimeId),o=ke({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Me(r,i),{messages:$e(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Le(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function ze(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return De(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Pe(e){try{return await ze(e)}catch(t){if(Ke(t))throw t;return[]}}function Ke(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function De(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey: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 $e(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:Ve(r,i)}:r})}function Ve(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=h(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?Y(c,o):H(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(Y(o));return r}function Y(e,t){return{...t?H(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function H(e){const{vectorBase64:t,...n}=e;return n}function qe(e){return e.map((t,n)=>je(t,n)).filter(t=>t!=null)}function je(e,t){if(!b(e))return null;const n=Te(e.kind),r=Ue(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=h(e.stableMessageKey)||Ye({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:He(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 Ye(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 He(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),b(t.attachment)&&(t.attachment=U(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?U(n):n)),t}function U(e){const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),t}function Ue(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function G(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>tt(o,e.screenshot,e.window,et(e.messages,a))).filter((o,a)=>Ge(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??pe(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ce({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await k({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=i.get(o.stableMessageKey);return a?{...o,mediaMetadata:nt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Ge(e,t,n){const r=Z(n);if(!r)return!0;const i=Xe(r),o=e.map((a,s)=>Z(Qe(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Ze(e,i);for(const a of o)if(t>=a&&t<=a+8||t===a-1&&!e.slice(a+1,Math.min(e.length,a+9)).some(X))return!0;return!1}function Xe(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Ze(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(X(e[n]))return Je(e[n],t)?n:-1;return-1}function X(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return J(t,n,r)}function Je(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),a=String(i.mimeType||"").toLowerCase(),s=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function Qe(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
2
- `)}function Z(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function et(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
3
- `)||null}function tt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=h(a.availability)||h(o.availability),c=h(a.localPath)||h(o.localPath);if(s==="edge-local"&&c||!J(i,o,a))return null;const d=E(e.bbox)??E(o.bbox)??E(o.downloadActionBbox),l=E(o.downloadActionBbox),f=Q(d,t,n),u=it(d,t,n),x=Q(l,t,n);return{messageKey:e.stableMessageKey,kind:h(a.type)||h(a.kind)||h(o.messageType)||i,fileName:h(a.name)||h(a.fileName)||h(o.fileName)||null,mimeType:h(a.mimeType)||h(o.mimeType)||null,size:S(a.size)??S(o.size),mediaStatus:rt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...f?{bbox:f}:{},...u?{screenshotBbox:u}:{},...x?{downloadActionBbox:x}:{}}}function nt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function rt(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function J(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 E(e){if(!b(e))return null;const t=S(e.x),n=S(e.y),r=S(e.width),i=S(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function Q(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=B(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,a=i.height/t.height,s=r.x,c=r.y;return{x:s-e.width*o/2,y:c-e.height*a/2,width:e.width*o,height:e.height*a,coordinateSpace:"screen"}}function it(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function S(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Yt(e,t){await ee(e,t)}async function ee(e,t){let n=await e.request("permissions.check",{},t);w(n,"permissions.check");const r=n.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(at(r))throw new Error(F);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(te([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(F);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32")throw new Error("wechat_window_unavailable");return r}async function ot(e,t){const n=await ee(e,t);if(String(n.platform??"").toLowerCase()==="win32"){const i=await e.request("windows.ensureReady",{activate:!0,allowRecovery:!1,allowLaunch:!1},t);w(i,"windows.ensureReady");const o=i.result;if(!o?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return o}const r=await ie(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.list missing WeChat window data");return r}function te(e,t){const n=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>M(o)-M(i))[0];return n.find(i=>st(i,r))??null}function at(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(v)||(e.unsafeAuxiliaryWindows??[]).some(v)}function L(e){const t=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter(re);if(n.length>1)throw new Error(F);const r=n.slice().sort((i,o)=>M(o)-M(i))[0];if(te(t,r))throw new Error(F)}function ne(e){const t=e.filter(v),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),r=n.filter(re);return(r.length?r:n.length?n:t).slice().sort((o,a)=>M(a)-M(o))[0]??null}function st(e,t){if(!v(e)||t&&dt(e,t)||e.visible===!1||e.minimized===!0)return!1;const n=Number(e.bounds?.width),r=Number(e.bounds?.height);if(!Number.isFinite(n)||!Number.isFinite(r))return!1;const i=n>=360&&n<=720&&r>=420&&r<=840&&r>n,o=n>=420&&n<=680&&r>=260&&r<=460;return!(!i&&!o||t&&ct(e,t))}function ct(e,t){const n=Number(e.rootProcessId),r=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(r)||n!==r)return!1;const i=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(i)||!Number.isFinite(o)?!1:i>o}function re(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function M(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function dt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Ht(e,t){let n=await e.request("windows.list",{},t);w(n,"windows.list");let r=n.result?.windows??[];L(r);let i=ne(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await oe(e,i,t)}async function lt(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return w(r,"windows.focus"),t}async function ie(e,t){const n=await e.request("windows.list",{},t);w(n,"windows.list");const r=n.result?.windows??[];L(r);const i=ne(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await oe(e,i,t)}async function oe(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(v);return L(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function z(e){const{capture:t,ocr:n}=e.observation??await T(e),r=A({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=_e({visibleItems:yt(n,t),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=Ot(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(a,"mouse.click"),{opened:!0,reason:i.reason}}async function ae(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Be;r+=1){if(t=await ut(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await m(350)}const n=await se(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function ut(e){const t=await T(e),n=A({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(K(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const l=await z({...e,observation:t});if(l.opened){if(await m(900),(await k(e)).ok)return{opened:!0,reason:l.reason};const u=await se(e);if(u.opened)return u}}const i=gt(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(o,"wechat.searchConversation");const a=await ht(e);if(a.opened){if(await m(900),(await k(e)).ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const f=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(f,"wechat.searchConversation")}let s=a.observation;if(!y(e.binding.conversationDisplayName)){await xt(e.helper,e.traceId),await m(900);const l=await k(e);if(l.ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason};const f=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(f,"wechat.searchConversation"),s=void 0}const c=await z({...e,...s?{observation:s}:{}});if(!c.opened)return c;await m(900);const d=await k(e);return d.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:c.reason}):{opened:!1,reason:d.reason}}async function se(e){const t=await z(e);if(!t.opened)return t;await m(900);const n=await k(e);return n.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function Ut(e){const t=e.bounds,n=Number(t?.x),r=Number(t?.y),i=Number(t?.width),o=Number(t?.height);if(![n,r,i,o].every(Number.isFinite)||i<=0||o<=0)return null;const a=Math.max(520,Math.min(i-160,i*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(r+o-s),coordinateSpace:"screen"}}async function ht(e){let t;const n=e.searchDelayMs===0?0:Fe,r=y(e.binding.conversationDisplayName)?We:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await m(n);const o=await ft(e);t=o;const{capture:a,ocr:s}=o,c=A({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Ct(s,a,e.binding.conversationDisplayName);if(!d){if(Nt(s)||It(s,e.binding.conversationDisplayName))break;continue}const l=B(d.bbox,a,wt(a,e.window));if(!l)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const f=await e.helper.request("mouse.click",{x:l.x,y:l.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(f,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function ft(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await le(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&w(t,"screen.capture")}return T(e)}function wt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function T(e){let{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await bt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await m(350),{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function bt(e){const t=mt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function mt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const d=String(c.text||""),l=c.bbox;if(!l)return!1;const f=Number(l.x),u=Number(l.y);return Number.isFinite(f)&&Number.isFinite(u)&&u>t.height*.06&&u<t.height*.22&&f>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const d=String(c.text||"").trim(),l=c.bbox;if(!l||d!=="\xD7")return!1;const f=Number(l.x),u=Number(l.y);return Number.isFinite(f)&&Number.isFinite(u)&&f>t.width*.55&&f<t.width*.9&&Math.abs(u-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return B(s?.bbox,t,n)}function gt(e,t){const{capture:n,ocr:r}=t,i=(r.blocks??[]).find(o=>{const a=String(o.text||"").trim().toLowerCase(),s=o.bbox;if(!s||!/^(搜索|search)$/.test(a))return!1;const c=Number(s.x),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return B(i?.bbox,n,e.window)}async function ce(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);w(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function de(e){return e.capture.dataBase64?e.capture:ce(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function P(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&w(i,"windows.captureAndOcr");const o=await ce(e,t,n,r),a=await le(e,o,n);return{capture:o,ocr:a}}async function le(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return w(r,"ocr.recognize"),r.result??{}}function w(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function v(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function m(e){return new Promise(t=>setTimeout(t,e))}async function xt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);w(n,"keyboard.shortcut")}async function _(e){if(!e.window.bounds)return;const t=e.observation,n=Lt(t?.ocr,t?.capture,e.window);if(n){const i=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(i,"mouse.click"),await m(700)}const r=zt(e.window);if(r){const i=await e.helper.request("mouse.click",{x:r.x,y:r.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(i,"mouse.click"),await m(150)}for(let i=0;i<6;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"pagedown",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(260)}for(let i=0;i<2;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"end",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(180)}for(let i=0;i<3;i+=1){const o=await e.helper.request("mouse.scroll",{deltaY:-2400,windowId:e.window.windowId,...r?{point:{...r,coordinateSpace:"screen"}}:{}},e.traceId);w(o,"mouse.scroll"),await m(180)}await m(220)}async function k(e){const{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=A({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:K(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function A(e){const t=(e.ocr.blocks??[]).map(r=>kt(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Mt(e,t)?q:null}function yt(e,t){const n=vt(e.blocks??[],t);return n.length?n:e.visibleConversationFingerprints??[]}function vt(e,t){const n=e.map(i=>ue(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!he(i.text)),r=new Set;return n.filter(i=>!pt(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=W(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function ue(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![r,i,o,a].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:a}}function pt(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function he(e){const t=C(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||R(t)||St(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Ct(e,t,n){const r=(e.blocks??[]).map(s=>ue(s)).filter(s=>!!s),i=r.filter(s=>R(C(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=i.findIndex(u=>C(u.text)===s);if(c<0)continue;const d=i[c],l=i[c+1]?.y??Number.POSITIVE_INFINITY,f=r.filter(u=>u.y>d.y&&u.y<l).filter(u=>u.x<t.width*.7).filter(u=>u.text.length>=2).filter(u=>!R(C(u.text))).filter(u=>!we(u,r)).filter(u=>D(u.text,n)).sort((u,x)=>u.y-x.y||u.x-x.x)[0];if(f)return{bbox:fe(f,t),reason:"search-result-title"}}const a=r.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!he(s.text)).filter(s=>!we(s,r)).filter(s=>D(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return a?{bbox:fe(a,t),reason:"search-result-title"}:null}function fe(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function R(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function Nt(e){return(e.blocks??[]).some(t=>R(C(t.text)))}function It(e,t){return(e.visibleConversationFingerprints??[]).some(n=>D(n.title,t))}function C(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function we(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function St(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function Mt(e,t){if(!v(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),r=Number(e.window.bounds?.height||e.capture.height);if(![n,r].every(Number.isFinite)||!(n>=360&&n<=720&&r>=420&&r<=840&&r>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function kt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function K(e,t,n){const r=y(n)?W(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?W(i.text):O(i.text);if(!o||!xe(o,r))return!1;const a=i.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,l=s<t.width*.18&&c<t.height*.12;return d||l}):!1}function be(e){if(K(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
4
- `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((s,c)=>{const d=Array.isArray(c.source)?c.source.some(l=>c.pattern.test(l)):c.pattern.test(c.source);return s+(d?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(s=>Number(s.bbox?.y)<e.capture.height*.14).map(s=>String(s.text||"")).join(`
5
- `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&Tt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?W(s.title):O(s.title),d=y(e.binding.conversationDisplayName)?W(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&d&&xe(c,d))}),!1)}function Tt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
6
- `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function me(e){const t=e.anchors.map(o=>({...o,normalized:p(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=_t(e.ocr,e.screenshot).sort((o,a)=>p(a.text).length-p(o.text).length);for(const o of i){const a=p(o.text);if(!a)continue;const s=t.find(d=>Bt(d.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:a,anchorText:a,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function _t(e,t){const n=(e.blocks??[]).filter(i=>Rt(i,t)&&p(i.text)).sort((i,o)=>N(i.bbox,"y")-N(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let a=i+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!Wt(o[o.length-1],s,t))break;o.push(s),r.push({text:Ft(o.map(c=>String(c.text||"").trim())),bbox:At(o.map(c=>c.bbox))})}}return r}function Bt(e,t){if(!e||!t)return!1;if(V(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function Wt(e,t,n){const r=N(e.bbox,"x"),i=N(e.bbox,"y"),o=N(e.bbox,"height"),a=N(t.bbox,"x"),s=N(t.bbox,"y");if(![r,i,o,a,s].every(Number.isFinite)||r<n.width*.5||a<n.width*.5)return!1;const c=s-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-r)<=120}function Ft(e){return e.reduce((t,n)=>t?n?`${t}${Et(t,n)?" ":""}${n}`:t:n,"")}function Et(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function At(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...r);return{x:a,y:s,width:Math.max(...i)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function N(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function ge(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=p(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=p(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&V(s,i)>=.9})||n.push(r)}return n}function Rt(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function Ot(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,d=t.width&&n.bounds?.width?t.width/n.bounds.width:1,l=t.height&&n.bounds?.height?t.height/n.bounds.height:d,f={x:i+Math.min(a*.35,110*d),y:o+Math.min(s-8*l,Math.max(18*l,s/2))};if(c==="screen"||!n.bounds)return f;const u=Math.max(0,(t.width-n.bounds.width*d)/2),x=Math.max(0,(t.height-n.bounds.height*l)/2);return{x:n.bounds.x+(f.x-u)/d,y:n.bounds.y+(f.y-x)/l}}function B(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,d={x:i+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return d;const l=t.width&&n.bounds.width?t.width/n.bounds.width:1,f=t.height&&n.bounds.height?t.height/n.bounds.height:l,u=Math.max(0,(t.width-n.bounds.width*l)/2),x=Math.max(0,(t.height-n.bounds.height*f)/2);return{x:n.bounds.x+(d.x-u)/l,y:n.bounds.y+(d.y-x)/f}}function Lt(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),a=i.bbox;if(!a||!o)return!1;const s=Number(a.x),c=Number(a.y);return!Number.isFinite(s)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:s>t.width*.55&&c>t.height*.45&&c<t.height*.92});return B(r?.bbox,t,n)}function zt(e){const t=e.bounds;return!t?.width||!t?.height?null:{x:t.x+t.width*.54,y:t.y+t.height*.7}}function xe(e,t){return y(t)?ve(e,t):e===t||e.includes(t)||t.includes(e)}function D(e,t){if(y(t))return ve(e,t);const n=ye(C(e)),r=ye(C(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function ye(e){return e.toLowerCase().replace(/[l1]/g,"i")}function O(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function W(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function ve(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{P as captureAndRecognizeWeChatWindow,ce as captureWeChatWindow,Yt as ensureHelperPreflight,ot as ensureWeChatWindowReady,Ut as fallbackWeChatMessageInputPointForWindow,ie as focusExistingWeChatWindow,lt as focusKnownWeChatWindow,Ht as focusWeChatWindow,jt as observeWeChatChannelBindingViaHelper,ae as openConversationBySearch,z as openConversationInVisibleList,le as recognizeWeChatScreenshot};
1
+ import{defaultWeChatChannelAttachmentDir as Me,resolveVisibleWeChatChannelMedia as Se}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as ke}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Te,loadWeChatChannelVectorStore as _e,saveWeChatChannelVectorStore as Be,upsertWeChatChannelVectorReferences as We}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as j,normalizeWeChatChannelMessageKind as Fe}from"./core/schema.js";import{waitForWeChatChannelPacing as U,weChatChannelPacingDelayMs as Ae}from"./pacing.js";import{matchWeChatConversationFingerprints as Ee}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as Y}from"./anchor.js";const Re=3,Oe=5,P="wechat_login_required",A="wechat_duplicate_instance";async function Zt(e){let t=await dt(e.helper,e.traceId);t=await bt(e.helper,t,e.traceId);const n=await de({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!n.opened)throw new Error(n.reason||"conversation_not_opened");let{capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId});if(ye({capture:r,ocr:i,binding:e.binding})){t=await se(e.helper,e.traceId);const o=await de({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!o.opened)throw new Error(o.reason||"wechat_window_obstructed");if({capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId}),ye({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),Le(e)?Pe({...e,window:t,capture:r,ocr:i}):H({...e,window:t,capture:r,ocr:i})}function Le(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function H(e){const t=await he(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),r=await J({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return pe(r,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Pe(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await he(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]})}catch(c){if(ze(c))return H(e);throw c}const r=He(n.structuredMessages??n.observedMessages??[]),i=await De({...e,messages:r,screenshot:t}),o=await J({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=pe(o,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?ke({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function ze(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function De(e){const t=Ke(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Ve({...e,seedBlocks:t}):t.filter(a=>u(a.vectorBase64)||u(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Te(e.workDir||process.cwd(),e.runtime.runtimeId),i=_e(r,e.runtime.runtimeId),o=We({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Be(r,i),{messages:Ue(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Ke(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=u(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=u(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function $e(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 je(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Ve(e){try{return await $e(e)}catch(t){if(qe(t))throw t;return[]}}function qe(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function je(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:u(n.stableMessageKey)||u(i?.stableMessageKey),blockId:u(n.blockId)||u(i?.blockId),blockKind:u(n.blockKind)||u(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>u(n.stableMessageKey)&&u(n.blockId))}function Ue(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:Ye(r,i)}:r})}function Ye(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=u(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?G(c,o):X(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(G(o));return r}function G(e,t){return{...t?X(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 X(e){const{vectorBase64:t,...n}=e;return n}function He(e){return e.map((t,n)=>Ge(t,n)).filter(t=>t!=null)}function Ge(e,t){if(!b(e))return null;const n=Fe(e.kind),r=Je(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=u(e.stableMessageKey)||Xe({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}:{},...N(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...N(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...N(e,"bbox")?{bbox:e.bbox}:{},...N(e,"mediaMetadata")?{mediaMetadata:Ze(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 Xe(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||u(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function Ze(e){if(!b(e))return e;const t={...e};return N(t,"availability")&&(t.availability=j(t.availability)),b(t.attachment)&&(t.attachment=Z(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?Z(n):n)),t}function Z(e){const t={...e};return N(t,"availability")&&(t.availability=j(t.availability)),t}function Je(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function J(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>ot(o,e.screenshot,e.window,it(e.messages,a))).filter((o,a)=>Qe(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Me(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Se({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await k({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=i.get(o.stableMessageKey);return a?{...o,mediaMetadata:at(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Qe(e,t,n){const r=ee(n);if(!r)return!0;const i=et(r),o=e.map((a,s)=>ee(rt(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===tt(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(Q))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 tt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(Q(e[n]))return nt(e[n],t)?n:-1;return-1}function Q(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return te(t,n,r)}function nt(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(`
2
+ `)}function ee(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function it(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
3
+ `)||null}function ot(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=u(a.availability)||u(o.availability),c=u(a.localPath)||u(o.localPath);if(s==="edge-local"&&c||!te(i,o,a))return null;const d=E(e.bbox)??E(o.bbox)??E(o.downloadActionBbox),h=E(o.downloadActionBbox),f=ne(d,t,n),l=ct(d,t,n),m=ne(h,t,n);return{messageKey:e.stableMessageKey,kind:u(a.type)||u(a.kind)||u(o.messageType)||i,fileName:u(a.name)||u(a.fileName)||u(o.fileName)||null,mimeType:u(a.mimeType)||u(o.mimeType)||null,size:I(a.size)??I(o.size),mediaStatus:st(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...f?{bbox:f}:{},...l?{screenshotBbox:l}:{},...m?{downloadActionBbox:m}:{}}}function at(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 st(e,t){const n=u(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=u(t.availability)||u(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function te(e,t,n){const r=`${e} ${u(t.messageType)} ${u(n.type)} ${u(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function E(e){if(!b(e))return null;const t=I(e.x),n=I(e.y),r=I(e.width),i=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 ne(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=W(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 ct(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 N(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function u(e){return typeof e=="string"?e.trim():""}function g(e){return u(e)||null}function I(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Jt(e,t){await re(e,t)}async function re(e,t){let n=await e.request("permissions.check",{},t);w(n,"permissions.check");const r=n.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(ut(r))throw new Error(P);if(lt(r))throw new Error(A);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(ie([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(A);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32")throw new Error("wechat_window_unavailable");return r}async function dt(e,t){const n=await re(e,t);if(String(n.platform??"").toLowerCase()==="win32"){const i=await e.request("windows.ensureReady",{activate:!0,allowRecovery:!1,allowLaunch:!1},t);w(i,"windows.ensureReady");const o=i.result;if(!o?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return o}const r=await se(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.list missing WeChat window data");return r}function ie(e,t){const n=e.filter(y).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>M(o)-M(i))[0];return n.find(i=>ht(i,r))??null}function lt(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(y)||(e.unsafeAuxiliaryWindows??[]).some(y)}function ut(e){if(Number(e.duplicateMainWindowCount??0)>0||(e.duplicateMainWindows??[]).some(y))return!1;const t=e.unsafeAuxiliaryWindows??[];return Number(e.unsafeAuxiliaryWindowCount??0)>0||t.some(ae)?![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(D)||e.wechatWindowAvailable===!1:!1}function z(e){const t=e.filter(y).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter(D);if(n.length>1)throw new Error(A);const r=n.slice().sort((i,o)=>M(o)-M(i))[0];if(ie(t,r))throw new Error(A)}function oe(e){const t=e.filter(y),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),r=n.filter(D);return(r.length?r:n.length?n:t).slice().sort((o,a)=>M(a)-M(o))[0]??null}function ht(e,t){return!(!y(e)||t&&wt(e,t)||e.visible===!1||e.minimized===!0||!ae(e)||t&&ft(e,t))}function ae(e){if(!y(e)||e.visible===!1||e.minimized===!0)return!1;const t=Number(e.bounds?.width),n=Number(e.bounds?.height);if(!Number.isFinite(t)||!Number.isFinite(n))return!1;const r=t>=360&&t<=720&&n>=420&&n<=840&&n>t,i=t>=420&&t<=680&&n>=260&&n<=460;return r||i}function ft(e,t){const n=Number(e.rootProcessId),r=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(r)||n!==r)return!1;const i=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(i)||!Number.isFinite(o)?!1:i>o}function D(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function M(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function wt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Qt(e,t){let n=await e.request("windows.list",{},t);w(n,"windows.list");let r=n.result?.windows??[];z(r);let i=oe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await ce(e,i,t)}async function bt(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return w(r,"windows.focus"),t}async function se(e,t){const n=await e.request("windows.list",{},t);w(n,"windows.list");const r=n.result?.windows??[];z(r);const i=oe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await ce(e,i,t)}async function ce(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(y);return z(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function K(e){const{capture:t,ocr:n}=e.observation??await T(e),r=R({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=Ee({visibleItems:Mt(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=$t(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(a,"mouse.click"),{opened:!0,reason:i.reason}}async function de(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Re;r+=1){if(t=await mt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await le(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function mt(e){const t=await T(e),n=R({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(V(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 f=await K({...e,observation:t});if(f.opened){if(await S(e,"open-click-settle","visible-initial"),(await k(e)).ok)return{opened:!0,reason:f.reason};const m=await le(e);if(m.opened)return m}}const i=Ct(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input"),...i?{searchPoint:i}:{}},e.traceId);w(o,"wechat.searchConversation");const a=await gt(e);if(a.opened){if(await S(e,"open-click-settle","search-result-click"),(await k(e)).ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input-retry-after-click"),...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation")}let s=a.observation;const c=x(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!x(e.binding.conversationDisplayName)){await It(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const f=await k(e);if(f.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:f.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input-retry-after-return"),...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation"),s=void 0}const d=await K({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!d.opened)return d;await S(e,"open-click-settle","visible-final");const h=await k(e);return h.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:d.reason}):{opened:!1,reason:h.reason}}async function le(e){const t=await K(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await k(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}}function en(e){const t=e.bounds,n=Number(t?.x),r=Number(t?.y),i=Number(t?.width),o=Number(t?.height);if(![n,r,i,o].every(Number.isFinite)||i<=0||o<=0)return null;const a=Math.max(520,Math.min(i-160,i*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(r+o-s),coordinateSpace:"screen"}}async function gt(e){let t;const n=e.searchDelayMs===0?0:_(e,"search-result-probe","search-result-probe"),r=x(e.binding.conversationDisplayName)?Oe:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await we(n);const o=await xt(e);t=o;const{capture:a,ocr:s}=o,c=R({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Tt(s,a,e.binding.conversationDisplayName);if(!d){if(_t(s)||Bt(s,e.binding.conversationDisplayName))break;continue}const h=W(d.bbox,a,yt(a,e.window));if(!h)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const f=await e.helper.request("mouse.click",{x:h.x,y:h.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(f,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function xt(e){if(e.window.bounds&&x(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 fe(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&w(t,"screen.capture")}return T(e)}function yt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function T(e){let{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await vt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await U("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function vt(e){const t=pt(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 pt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const d=String(c.text||""),h=c.bbox;if(!h)return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&l>t.height*.06&&l<t.height*.22&&f>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const d=String(c.text||"").trim(),h=c.bbox;if(!h||d!=="\xD7")return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&f>t.width*.55&&f<t.width*.9&&Math.abs(l-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return W(s?.bbox,t,n)}function Ct(e,t){const{capture:n,ocr:r}=t,i=(r.blocks??[]).find(o=>{const a=String(o.text||"").trim().toLowerCase(),s=o.bbox;if(!s||!/^(搜索|search)$/.test(a))return!1;const c=Number(s.x),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return W(i?.bbox,n,e.window)}async function ue(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);w(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function he(e){return e.capture.dataBase64?e.capture:ue(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function $(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&w(i,"windows.captureAndOcr");const o=await ue(e,t,n,r),a=await fe(e,o,n);return{capture:o,ocr:a}}async function fe(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return w(r,"ocr.recognize"),r.result??{}}function w(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function y(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function we(e){return new Promise(t=>setTimeout(t,e))}function _(e,t,n){return e.searchDelayMs===0?0:Ae(t,Nt(e,n))}async function S(e,t,n){const r=_(e,t,n);r>0&&await we(r)}function Nt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function It(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);w(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Vt(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);w(r,"mouse.click"),await U("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function k(e){const{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=R({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:V(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function R(e){const t=(e.ocr.blocks??[]).map(r=>At(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Ft(e,t)?P:null}function Mt(e,t,n=!0){const r=n?St(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function St(e,t){const n=e.map(i=>be(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=>!me(i.text)),r=new Set;return n.filter(i=>!kt(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=F(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function be(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 kt(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 me(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||O(t)||Wt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Tt(e,t,n){const r=(e.blocks??[]).map(s=>be(s)).filter(s=>!!s),i=r.filter(s=>O(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=i.findIndex(l=>p(l.text)===s);if(c<0)continue;const d=i[c],h=i[c+1]?.y??Number.POSITIVE_INFINITY,f=r.filter(l=>l.y>d.y&&l.y<h).filter(l=>l.x<t.width*.7).filter(l=>l.text.length>=2).filter(l=>!O(p(l.text))).filter(l=>!xe(l,r)).filter(l=>q(l.text,n)).sort((l,m)=>l.y-m.y||l.x-m.x)[0];if(f)return{bbox:ge(f,t),reason:"search-result-title"}}const a=r.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!me(s.text)).filter(s=>!xe(s,r)).filter(s=>q(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return x(n)?null:a?{bbox:ge(a,t),reason:"search-result-title"}:null}function ge(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function O(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function _t(e){return(e.blocks??[]).some(t=>O(p(t.text)))}function Bt(e,t){return(e.visibleConversationFingerprints??[]).some(n=>q(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function xe(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function Wt(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 Ft(e,t){if(!y(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),r=Number(e.window.bounds?.height||e.capture.height);if(![n,r].every(Number.isFinite)||!(n>=360&&n<=720&&r>=420&&r<=840&&r>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function At(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function V(e,t,n){const r=x(n)?F(n):L(n);return r?(e.blocks??[]).some(i=>{const o=x(n)?F(i.text):L(i.text);if(!o||!Ce(o,r))return!1;const a=i.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,h=s<t.width*.18&&c<t.height*.12;return d||h}):!1}function ye(e){if(V(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
4
+ `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((s,c)=>{const d=Array.isArray(c.source)?c.source.some(h=>c.pattern.test(h)):c.pattern.test(c.source);return s+(d?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(s=>Number(s.bbox?.y)<e.capture.height*.14).map(s=>String(s.text||"")).join(`
5
+ `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&Et(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=x(e.binding.conversationDisplayName)?F(s.title):L(s.title),d=x(e.binding.conversationDisplayName)?F(e.binding.conversationDisplayName):L(e.binding.conversationDisplayName);return!!(c&&d&&Ce(c,d))}),!1)}function Et(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
6
+ `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function ve(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=Rt(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(d=>Ot(d.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:a,anchorText:a,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function Rt(e,t){const n=(e.blocks??[]).filter(i=>Kt(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(!Lt(o[o.length-1],s,t))break;o.push(s),r.push({text:Pt(o.map(c=>String(c.text||"").trim())),bbox:Dt(o.map(c=>c.bbox))})}}return r}function Ot(e,t){if(!e||!t)return!1;if(Y(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function Lt(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 Pt(e){return e.reduce((t,n)=>t?n?`${t}${zt(t,n)?" ":""}${n}`:t:n,"")}function zt(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Dt(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&&Y(s,i)>=.9})||n.push(r)}return n}function Kt(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 $t(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,d=t.width&&n.bounds?.width?t.width/n.bounds.width:1,h=t.height&&n.bounds?.height?t.height/n.bounds.height:d,f={x:i+Math.min(a*.35,110*d),y:o+Math.min(s-8*h,Math.max(18*h,s/2))};if(c==="screen"||!n.bounds)return f;const l=Math.max(0,(t.width-n.bounds.width*d)/2),m=Math.max(0,(t.height-n.bounds.height*h)/2);return{x:n.bounds.x+(f.x-l)/d,y:n.bounds.y+(f.y-m)/h}}function W(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,d={x:i+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return d;const h=t.width&&n.bounds.width?t.width/n.bounds.width:1,f=t.height&&n.bounds.height?t.height/n.bounds.height:h,l=Math.max(0,(t.width-n.bounds.width*h)/2),m=Math.max(0,(t.height-n.bounds.height*f)/2);return{x:n.bounds.x+(d.x-l)/h,y:n.bounds.y+(d.y-m)/f}}function Vt(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 W(r?.bbox,t,n)}function Ce(e,t){return x(t)?Ie(e,t):e===t||e.includes(t)||t.includes(e)}function q(e,t){if(x(t))return Ie(e,t);const n=Ne(p(e)),r=Ne(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Ne(e){return e.toLowerCase().replace(/[l1]/g,"i")}function L(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function F(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function x(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Ie(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{$ as captureAndRecognizeWeChatWindow,ue as captureWeChatWindow,Jt as ensureHelperPreflight,dt as ensureWeChatWindowReady,en as fallbackWeChatMessageInputPointForWindow,se as focusExistingWeChatWindow,bt as focusKnownWeChatWindow,Qt as focusWeChatWindow,Zt as observeWeChatChannelBindingViaHelper,de as openConversationBySearch,K as openConversationInVisibleList,fe as recognizeWeChatScreenshot};
@@ -45,6 +45,8 @@ export declare class WeChatChannelOutboundSender {
45
45
  private sendTextIfPresent;
46
46
  private sendAttachmentsIfPresent;
47
47
  private withAutomationLease;
48
+ private releaseAutomationLease;
49
+ private waitAfterPaste;
48
50
  private ensureStageIdle;
49
51
  private activitySnapshot;
50
52
  private automationLeaseDecision;
@@ -1 +1 @@
1
- import v from"node:fs";import A from"node:path";import{guardWeChatOutboundRevision as C,markWeChatOutboundFailed as S,markWeChatOutboundManualReview as k,markWeChatOutboundCommitStage as r,markWeChatOutboundSending as x,markWeChatOutboundSentUnconfirmed as T,markWeChatOutboundUserActiveTimeout as h,markWeChatOutboundWaitingUserIdle as w,shouldFailWeChatOutboundForUserActiveTimeout as d}from"./outbound-ledger.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as I,normalizeWeChatChannelActivitySnapshot as R}from"./human-coordination.js";const W=600*1e3,b=450,y=520;async function j(e){const t={sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]},s=e.now??new Date,n=e.maxUserActivityWaitMs??W,i=e.ledger.records.filter(a=>a.bindingId===e.bindingId&&a.sendStatus==="queued");for(const a of i){if(d({record:a,now:s,maxWaitMs:n})){h(a,s),t.failedRecords.push(a);continue}if(!C({record:a,currentLastInboundRevision:e.currentLastInboundRevision,now:s}).ok){t.staleRecords.push(a);continue}try{const o=await e.sender.canStart(a,s);if(o.ok===!1){w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}x(a),await e.sender.send(a),T(a,s),t.sentRecords.push(a)}catch(o){if(o instanceof f){if(D(o)){k(a,"user_takeover_before_return","User activity was detected after paste and before Return; manual WeChat inspection is required",o.stage),t.manualReviewRecords.push(a);continue}w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}S(a,q(o),o instanceof Error?o.message:String(o)),t.failedRecords.push(a)}}return t}class B{options;activeAutomationLeaseId=null;constructor(t){this.options=t}async canStart(t,s=new Date){const n=await this.activitySnapshot();U(n,this.options.platform);const i=_({snapshot:n,stage:"send_start",policy:this.options.activityGatePolicy});return i.ok===!1?{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:I(s,i.waitMs)}:{ok:!0}}async send(t){await this.withAutomationLease(t,()=>this.sendWithLease(t))}async sendWithLease(t){await this.ensureStageIdle(t,"open_conversation"),r(t,"open_conversation");const s=await this.options.openConversation(t.conversationName);if(!s.opened)throw new Error(s.reason||"conversation_not_opened");r(t,"focus_input"),await L(this.options.helper,this.options.traceId,s.inputPoint??void 0),await this.ensureStageIdle(t,"focus_input"),await this.ensureStageIdle(t,"clipboard_snapshot"),r(t,"clipboard_snapshot");const n=await this.options.helper.request("clipboard.snapshot",{},this.options.traceId);c(n,"clipboard.snapshot");let i=!1;try{await this.sendTextIfPresent(t,()=>{i=!0}),await this.sendAttachmentsIfPresent(t,()=>{i=!0})}finally{const a=n.result&&typeof n.result=="object"?n.result:{},p=await this.options.helper.request("clipboard.restore",a,this.options.traceId);i||c(p,"clipboard.restore")}}async sendTextIfPresent(t,s){if(!t.text)return;await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set"),await u(this.options.helper,"a",m(this.options.platform),this.options.traceId),await u(this.options.helper,"backspace",[],this.options.traceId);const n=await this.options.helper.request("clipboard.setText",{text:t.text},this.options.traceId);c(n,"clipboard.setText"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}async sendAttachmentsIfPresent(t,s){for(const n of t.attachmentLocalRefs??[]){O(n),await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set");const i=E(n),a=i==="image"&&g(this.options.platform)?await this.options.helper.request("clipboard.setImage",{filePath:n},this.options.traceId):await this.options.helper.request("clipboard.setFiles",{filePaths:[n]},this.options.traceId);c(a,i==="image"&&g(this.options.platform)?"clipboard.setImage":"clipboard.setFiles"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}}async withAutomationLease(t,s){const n=await this.options.helper.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`send:${t.bindingId}`,ttlMs:6e4},this.options.traceId);c(n,"automation.lease.acquire");const i=typeof n.result?.leaseId=="string"?n.result.leaseId:"";try{this.activeAutomationLeaseId=i||null,await s()}finally{this.activeAutomationLeaseId=null,i&&await this.options.helper.request("automation.lease.release",{leaseId:i},this.options.traceId)}}async ensureStageIdle(t,s){if(this.options.takeoverCheck===!1)return;if(this.activeAutomationLeaseId){const a=await this.automationLeaseDecision();if(a.ok===!1)throw new f({reasonCode:a.reasonCode,nextAttemptAt:a.nextAttemptAt,stage:s,replyId:t.replyId});return}const n=await this.activitySnapshot(),i=_({snapshot:n,stage:M(s),policy:this.options.activityGatePolicy});if(!i.ok)throw new f({reasonCode:i.reasonCode,nextAttemptAt:I(new Date,i.waitMs),stage:s,replyId:t.replyId})}async activitySnapshot(){const t=await this.options.helper.request("activity.snapshot",{},this.options.traceId);return c(t,"activity.snapshot"),R(t.result)}async automationLeaseDecision(){if(!this.activeAutomationLeaseId)return{ok:!0};const t=await this.options.helper.request("automation.lease.status",{},this.options.traceId);c(t,"automation.lease.status");const s=t.result??{};return s.active===!1||s.leaseId!==this.activeAutomationLeaseId||!s.interrupted?{ok:!0}:{ok:!1,reasonCode:P(s.interruptReason),nextAttemptAt:new Date(Date.now()+1e3)}}}class f extends Error{reasonCode;nextAttemptAt;stage;replyId;constructor(t){super(`user_takeover_aborted:${t.reasonCode}:${t.stage}`),this.name="WeChatChannelUserTakeoverAbort",this.reasonCode=t.reasonCode,this.nextAttemptAt=t.nextAttemptAt,this.stage=t.stage,this.replyId=t.replyId}}function M(e){return e==="open_conversation"?"open_conversation":e==="queued"?"send_start":"dangerous_action"}async function L(e,t,s){const n={waitMs:y,...s?{inputPoint:s}:{}};if((await e.request("wechat.focusMessageInput",n,t)).ok){await l(120);const p=await e.request("wechat.focusMessageInput",n,t);c(p,"wechat.focusMessageInput");return}await l(350);const a=await e.request("wechat.focusMessageInput",{...n,waitMs:Math.max(y,700)},t);c(a,"wechat.focusMessageInput")}async function u(e,t,s,n){const i=await e.request("keyboard.shortcut",{key:t,modifiers:s},n);c(i,"keyboard.shortcut")}function O(e){if(!v.statSync(e).isFile())throw new Error(`wechat_channel_attachment_not_file:${e}`)}function E(e){const t=A.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic",".tiff",".bmp"].includes(t)?"image":[".mp4",".mov",".m4v",".avi",".mkv",".webm"].includes(t)?"video":"file"}function g(e){return(e??process.platform)==="win32"}function m(e){return(e??process.platform)==="win32"?["control"]:["command"]}function U(e,t){if((t??process.platform)!=="darwin")return;const s=e?.permissions;if(!s)return;const n=[s.accessibilityTrusted===!1?"accessibility":null,s.iohidPostGranted===!1?"input-monitoring":null].filter(Boolean);if(n.length)throw new Error(`permission_missing:mac_input:${n.join(",")}`)}function l(e){return new Promise(t=>setTimeout(t,e))}function q(e){const t=e instanceof Error?e.message:String(e);return/conversation_not|conversation.*visible|conversation.*open/i.test(t)?"conversation_not_opened":/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/clipboard/i.test(t)?"clipboard_failed":/attachment|not_file|ENOENT|no such file/i.test(t)?"attachment_unavailable":"send_failed"}function P(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function D(e){return e.stage==="before_return"}function c(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}export{B as WeChatChannelOutboundSender,f as WeChatChannelUserTakeoverAbort,j as sendQueuedWeChatOutboundRecords};
1
+ import g from"node:fs";import A from"node:path";import{waitForWeChatChannelPacing as l}from"./pacing.js";import{guardWeChatOutboundRevision as v,markWeChatOutboundFailed as C,markWeChatOutboundManualReview as k,markWeChatOutboundCommitStage as r,markWeChatOutboundSending as S,markWeChatOutboundSentUnconfirmed as x,markWeChatOutboundUserActiveTimeout as h,markWeChatOutboundWaitingUserIdle as w,shouldFailWeChatOutboundForUserActiveTimeout as d}from"./outbound-ledger.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as y,normalizeWeChatChannelActivitySnapshot as R}from"./human-coordination.js";const W=600*1e3,b=520;async function j(e){const t={sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]},s=e.now??new Date,i=e.maxUserActivityWaitMs??W,n=e.ledger.records.filter(a=>a.bindingId===e.bindingId&&a.sendStatus==="queued");for(const a of n){if(d({record:a,now:s,maxWaitMs:i})){h(a,s),t.failedRecords.push(a);continue}if(!v({record:a,currentLastInboundRevision:e.currentLastInboundRevision,now:s}).ok){t.staleRecords.push(a);continue}try{const o=await e.sender.canStart(a,s);if(o.ok===!1){w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:i})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}S(a),await e.sender.send(a),x(a,s),t.sentRecords.push(a)}catch(o){if(o instanceof f){if(E(o)){k(a,"user_takeover_before_return","User activity was detected after paste and before Return; manual WeChat inspection is required",o.stage),t.manualReviewRecords.push(a);continue}w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:i})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}C(a,U(o),o instanceof Error?o.message:String(o)),t.failedRecords.push(a)}}return t}class z{options;activeAutomationLeaseId=null;constructor(t){this.options=t}async canStart(t,s=new Date){const i=await this.activitySnapshot();O(i,this.options.platform);const n=_({snapshot:i,stage:"send_start",policy:this.options.activityGatePolicy});return n.ok===!1?{ok:!1,reasonCode:n.reasonCode,nextAttemptAt:y(s,n.waitMs)}:{ok:!0}}async send(t){await this.withAutomationLease(t,()=>this.sendWithLease(t))}async sendWithLease(t){await this.ensureStageIdle(t,"open_conversation"),r(t,"open_conversation");const s=await this.options.openConversation(t.conversationName);if(!s.opened)throw new Error(s.reason||"conversation_not_opened");r(t,"focus_input"),await M(this.options.helper,this.options.traceId,s.inputPoint??void 0),await this.ensureStageIdle(t,"focus_input"),await this.ensureStageIdle(t,"clipboard_snapshot"),r(t,"clipboard_snapshot");const i=await this.options.helper.request("clipboard.snapshot",{},this.options.traceId);c(i,"clipboard.snapshot");let n=!1;try{await this.sendTextIfPresent(t,()=>{n=!0}),await this.sendAttachmentsIfPresent(t,()=>{n=!0})}finally{const a=i.result&&typeof i.result=="object"?i.result:{},p=await this.options.helper.request("clipboard.restore",a,this.options.traceId);n||c(p,"clipboard.restore")}}async sendTextIfPresent(t,s){if(!t.text)return;await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set"),await u(this.options.helper,"a",m(this.options.platform),this.options.traceId),await u(this.options.helper,"backspace",[],this.options.traceId);const i=await this.options.helper.request("clipboard.setText",{text:t.text},this.options.traceId);c(i,"clipboard.setText"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await this.waitAfterPaste(t),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}async sendAttachmentsIfPresent(t,s){for(const i of t.attachmentLocalRefs??[]){L(i),await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set");const n=q(i),a=n==="image"&&I(this.options.platform)?await this.options.helper.request("clipboard.setImage",{filePath:i},this.options.traceId):await this.options.helper.request("clipboard.setFiles",{filePaths:[i]},this.options.traceId);c(a,n==="image"&&I(this.options.platform)?"clipboard.setImage":"clipboard.setFiles"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await this.waitAfterPaste(t),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}}async withAutomationLease(t,s){const i=await this.options.helper.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`send:${t.bindingId}`,ttlMs:6e4},this.options.traceId);c(i,"automation.lease.acquire");const n=typeof i.result?.leaseId=="string"?i.result.leaseId:"";try{this.activeAutomationLeaseId=n||null,await s()}finally{this.activeAutomationLeaseId=null,n&&await this.releaseAutomationLease(n)}}async releaseAutomationLease(t){try{await this.options.helper.request("automation.lease.release",{leaseId:t},this.options.traceId)}catch{}}async waitAfterPaste(t){await l("send-post-paste",`${this.options.traceId||""}:${t.replyId}:post-paste`,this.options.postPasteSettleMs??void 0)}async ensureStageIdle(t,s){if(this.options.takeoverCheck===!1)return;if(this.activeAutomationLeaseId){const a=await this.automationLeaseDecision();if(a.ok===!1)throw new f({reasonCode:a.reasonCode,nextAttemptAt:a.nextAttemptAt,stage:s,replyId:t.replyId});return}const i=await this.activitySnapshot(),n=_({snapshot:i,stage:T(s),policy:this.options.activityGatePolicy});if(!n.ok)throw new f({reasonCode:n.reasonCode,nextAttemptAt:y(new Date,n.waitMs),stage:s,replyId:t.replyId})}async activitySnapshot(){const t=await this.options.helper.request("activity.snapshot",{},this.options.traceId);return c(t,"activity.snapshot"),R(t.result)}async automationLeaseDecision(){if(!this.activeAutomationLeaseId)return{ok:!0};const t=await this.options.helper.request("automation.lease.status",{},this.options.traceId);c(t,"automation.lease.status");const s=t.result??{};return s.active===!1||s.leaseId!==this.activeAutomationLeaseId||!s.interrupted?{ok:!0}:{ok:!1,reasonCode:P(s.interruptReason),nextAttemptAt:new Date(Date.now()+1e3)}}}class f extends Error{reasonCode;nextAttemptAt;stage;replyId;constructor(t){super(`user_takeover_aborted:${t.reasonCode}:${t.stage}`),this.name="WeChatChannelUserTakeoverAbort",this.reasonCode=t.reasonCode,this.nextAttemptAt=t.nextAttemptAt,this.stage=t.stage,this.replyId=t.replyId}}function T(e){return e==="open_conversation"?"open_conversation":e==="queued"?"send_start":"dangerous_action"}async function M(e,t,s){const i={waitMs:b,...s?{inputPoint:s}:{}};if((await e.request("wechat.focusMessageInput",i,t)).ok){await l("send-focus-stabilize",`${t||""}:focus-stabilize`);const p=await e.request("wechat.focusMessageInput",i,t);c(p,"wechat.focusMessageInput");return}await l("send-focus-retry",`${t||""}:focus-retry`);const a=await e.request("wechat.focusMessageInput",{...i,waitMs:Math.max(b,700)},t);c(a,"wechat.focusMessageInput")}async function u(e,t,s,i){const n=await e.request("keyboard.shortcut",{key:t,modifiers:s},i);c(n,"keyboard.shortcut")}function L(e){if(!g.statSync(e).isFile())throw new Error(`wechat_channel_attachment_not_file:${e}`)}function q(e){const t=A.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic",".tiff",".bmp"].includes(t)?"image":[".mp4",".mov",".m4v",".avi",".mkv",".webm"].includes(t)?"video":"file"}function I(e){return(e??process.platform)==="win32"}function m(e){return(e??process.platform)==="win32"?["control"]:["command"]}function O(e,t){if((t??process.platform)!=="darwin")return;const s=e?.permissions;if(!s)return;const i=[s.accessibilityTrusted===!1?"accessibility":null,s.iohidPostGranted===!1?"input-monitoring":null].filter(Boolean);if(i.length)throw new Error(`permission_missing:mac_input:${i.join(",")}`)}function U(e){const t=e instanceof Error?e.message:String(e);return/conversation_not|conversation.*visible|conversation.*open/i.test(t)?"conversation_not_opened":/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/clipboard/i.test(t)?"clipboard_failed":/attachment|not_file|ENOENT|no such file/i.test(t)?"attachment_unavailable":"send_failed"}function P(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function E(e){return e.stage==="before_return"}function c(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}export{z as WeChatChannelOutboundSender,f as WeChatChannelUserTakeoverAbort,j as sendQueuedWeChatOutboundRecords};
@@ -0,0 +1,3 @@
1
+ export type WeChatChannelPacingProfile = 'open-retry' | 'open-click-settle' | 'search-input-settle' | 'search-result-probe' | 'preview-dismiss-settle' | 'jump-latest-settle' | 'send-focus-stabilize' | 'send-focus-retry' | 'send-post-paste' | 'send-after-submit' | 'observe-retry';
2
+ export declare function weChatChannelPacingDelayMs(profile: WeChatChannelPacingProfile, seed?: string): number;
3
+ export declare function waitForWeChatChannelPacing(profile: WeChatChannelPacingProfile, seed?: string, overrideMs?: number): Promise<number>;
@@ -0,0 +1 @@
1
+ import M from"node:crypto";const o={"open-retry":{minMs:320,maxMs:560},"open-click-settle":{minMs:760,maxMs:1180},"search-input-settle":{minMs:620,maxMs:980},"search-result-probe":{minMs:360,maxMs:640},"preview-dismiss-settle":{minMs:260,maxMs:480},"jump-latest-settle":{minMs:620,maxMs:920},"send-focus-stabilize":{minMs:90,maxMs:190},"send-focus-retry":{minMs:280,maxMs:540},"send-post-paste":{minMs:420,maxMs:760},"send-after-submit":{minMs:820,maxMs:1280},"observe-retry":{minMs:1100,maxMs:2600}};function r(e,a){const t=o[e],s=Math.max(0,Math.floor(t.minMs)),m=Math.max(s,Math.floor(t.maxMs))-s+1;if(m<=1)return s;const i=M.createHash("sha256").update(`${e}:${a||""}`).digest().readUInt32BE(0);return s+i%m}async function u(e,a,t){const s=typeof t=="number"&&Number.isFinite(t)?Math.max(0,Math.floor(t)):r(e,a);return s>0&&await new Promise(n=>setTimeout(n,s)),s}export{u as waitForWeChatChannelPacing,r as weChatChannelPacingDelayMs};
@@ -23,7 +23,7 @@ type IpcContext = {
23
23
  url: string;
24
24
  token: string;
25
25
  };
26
- type WeChatToolBinding = {
26
+ export type WeChatToolBinding = {
27
27
  sessionId: string;
28
28
  channelId: string;
29
29
  conversationId: string;
@@ -100,9 +100,28 @@ export type WeChatReadLatestResult = {
100
100
  reasonCode?: null;
101
101
  message?: string;
102
102
  };
103
+ export type WeChatToolErrorResult = {
104
+ ok: false;
105
+ operation: 'read' | 'write';
106
+ conversation?: string;
107
+ reasonCode: string;
108
+ message: string;
109
+ };
103
110
  export declare function runWeChatDoctor(options?: WeChatDoctorOptions): Promise<WeChatDoctorResult>;
104
111
  export declare function runWeChatSend(options: WeChatSendOptions): Promise<WeChatSendResult>;
105
112
  export declare function runWeChatReadLatest(options: WeChatReadLatestOptions): Promise<WeChatReadLatestResult>;
113
+ export declare function sendDirectWeChatMessageOnce(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
114
+ pending?: boolean;
115
+ sendStatus?: WeChatSendResult['sendStatus'];
116
+ outDir?: string;
117
+ helperTracePath?: string | null;
118
+ }>;
119
+ export declare function readDirectWeChatLatestOnce(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<{
120
+ messages: ExternalMessageEvent[];
121
+ outDir: string;
122
+ helperTracePath: string | null;
123
+ }>;
106
124
  export declare function registerWeChatCommand(program: Command): void;
125
+ export declare function buildWeChatToolErrorResult(operation: 'read' | 'write', conversation: string | undefined, error: unknown): WeChatToolErrorResult;
107
126
  export declare function buildWeChatReadMarkdown(result: WeChatReadLatestResult): string;
108
127
  export {};
@@ -1,7 +1,7 @@
1
- import L from"node:crypto";import S from"node:fs";import g from"node:path";import{SERVERS as O}from"../region.js";import{loadConfig as k,resolveShennianPath as _}from"../config/index.js";import{observedMessageToExternalEvent as U,weChatChannelConversationId as q,weChatChannelBindingId as z}from"../channels/wechat-rpa/product-channel.js";import{readExternalAttachment as B}from"./external-attachments.js";import{runWeChatChannelActionSmoke as $}from"../devtools/wechat-channel-action-smoke.js";async function F(e={}){const n=k(),t=Q(e.serverUrl||n.serverUrl||O.cn.url),a=e.machineToken||n.machineToken,r=e.machineId||n.machineId,o=e.fetchImpl||fetch,s=[m("cli_installed","Shennian CLI is installed."),a?m("machine_paired","Machine token is configured."):C("machine_paired","machine_not_paired","Machine is not paired. Run shennian pair first.")];if(!a)return v({checks:s,serverUrl:t,machineId:r});try{const d=await o(`${t}/api/channels/wechat/runtime-policy`,{headers:{authorization:`Bearer ${a}`}}),i=await d.json().catch(()=>null);if(!d.ok||i?.ok===!1){const l=y(i?.reasonCode)||d.statusText||"request_failed";return s.push(C("server_policy",l,`WeChat runtime policy is blocked: ${l}`)),v({checks:s,serverUrl:t,machineId:r})}const c=u(i?.usagePolicy)?i.usagePolicy:{};s.push(m("server_policy","Server WeChat policy is reachable.")),s.push(m("entitlement","Account has Pro / Team external channel entitlement.")),s.push(m("credits",c.billingMode==="credits"?"Credit billing is enabled; balance is checked by server calls.":"Credits are not metered in current billing mode."));const f=u(i?.runtime)&&u(i.runtime.current)?i.runtime.current:null;s.push(f?m("runtime_registered","Local WeChat runtime is registered on the server."):G("runtime_registered","runtime_not_registered","Local WeChat runtime is not registered yet; open Shennian Desktop/daemon and enable Use WeChat."))}catch(d){s.push(C("server_policy","network_unavailable",d instanceof Error?d.message:"Unable to reach Shennian server."))}return v({checks:s,serverUrl:t,machineId:r})}async function J(e){const n=e.text.trim();if(!n&&!e.attachment)throw new Error("Message text or attachment is required");const t=x(e),a=e.attachment?fe(e.attachment):void 0;if(e.dryRun)return{ok:!0,operation:"write",conversation:t.conversationName,conversationName:t.conversationName,conversationId:t.conversationId,sessionId:t.sessionId,channelId:t.channelId,traceId:e.traceId,outDir:t.workDir,helperTracePath:null,sendStatus:"dry_run",pending:!1,attachment:a,attachments:a?[a]:[],dryRun:!0,reasonCode:null,message:"Dry run completed. No WeChat message was sent."};const r=D(e);if(!r){const c=e.directRuntime?await e.directRuntime.send(t,e):await H(t,e);return{ok:!0,operation:"write",conversation:t.conversationName,conversationName:t.conversationName,conversationId:t.conversationId,sessionId:t.sessionId,channelId:t.channelId,traceId:e.traceId,outDir:c.outDir??t.workDir,helperTracePath:c.helperTracePath??null,sendStatus:c.sendStatus??(c.pending?"pending":"sent"),pending:c.pending===!0,attachment:a,attachments:a?[a]:[],reasonCode:null,message:c.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}await P(r,t,e);const o=e.idempotencyKey||T("wechat-cli-send",`${t.channelId}
2
- ${t.conversationId}
3
- ${n}
4
- ${Date.now()}`),s=await w(r,"/external/reply",{managerSessionId:t.sessionId,channelId:t.channelId,conversationId:t.conversationId,text:n,attachment:e.attachment,idempotencyKey:o},e.fetchImpl,e.timeoutMs),d=await Y(r,t,e),i=d?ce(d,o,s.pending===!0):s.pending===!0?"queued":"ok";return{ok:!0,operation:"write",conversation:t.conversationName,conversationName:t.conversationName,conversationId:t.conversationId,sessionId:t.sessionId,channelId:t.channelId,traceId:e.traceId,outDir:t.workDir,helperTracePath:null,sendStatus:i,pending:s.pending===!0&&(i==="queued"||i==="pending"),attachment:a,attachments:a?[a]:[],reasonCode:null,message:d?i==="confirmed_echo"?"WeChat message sent and confirmed.":i==="manual_review"?"WeChat message was submitted but needs manual review.":"WeChat message submitted to local daemon.":"WeChat message submitted to local daemon; confirmation sync timed out."}}async function K(e){const n=h(e.limit,10),t=x(e),a=D(e);if(!a){const i=e.directRuntime?{messages:await e.directRuntime.readLatest(t,{...e,limit:n,recentLimit:e.recentLimit??n}),outDir:t.workDir,helperTracePath:null}:await Z(t,{...e,limit:n,recentLimit:e.recentLimit??n}),c=i.messages.filter(l=>l.conversationId===t.conversationId||l.conversationName===t.conversationName).slice(-n),f=E(c);return{ok:!0,operation:"read",conversation:t.conversationName,conversationName:t.conversationName,conversationId:t.conversationId,sessionId:t.sessionId,channelId:t.channelId,traceId:e.traceId,outDir:i.outDir,helperTracePath:i.helperTracePath,messages:c,attachments:f,count:c.length,reasonCode:null,message:`Read ${c.length} WeChat message(s).`}}await P(a,t,{...e,recentLimit:e.recentLimit??n});const r=await w(a,"/wechat-rpa/channel/sync",{managerSessionId:t.sessionId},e.fetchImpl,e.timeoutMs),s=(Array.isArray(r.messages)?r.messages.filter(ye):[]).filter(i=>i.conversationId===t.conversationId||i.conversationName===t.conversationName).slice(-n),d=E(s);return{ok:!0,operation:"read",conversation:t.conversationName,conversationName:t.conversationName,conversationId:t.conversationId,sessionId:t.sessionId,channelId:t.channelId,traceId:e.traceId,outDir:t.workDir,helperTracePath:null,messages:s,attachments:d,count:s.length,reasonCode:null,message:`Read ${s.length} WeChat message(s).`}}async function H(e,n){const t=V(n.attachment),a=R(e,n,{kind:"send",steps:["open-read",t],text:n.text,attachment:n.attachment}),r=await $(a),o=r.steps.find(d=>d.step===t);if(!r.ok||!o?.ok){const d=o?.reasonCode||r.steps.find(c=>!c.ok&&!c.skipped)?.reasonCode||"wechat_direct_send_failed",i=o?.errorSummary||r.steps.find(c=>!c.ok&&!c.skipped)?.errorSummary||d;throw new Error(`${d}: ${i}`)}const s=re(o.details?.sendStatus)??"sent_unconfirmed";return{pending:s==="queued"||s==="pending"||s==="sent_unconfirmed",sendStatus:s,outDir:r.outDir,helperTracePath:r.helperTracePath}}function V(e){return e?e.kind==="image"?"send-image":e.kind==="video"?"send-video":"send-file":"send-text"}async function Z(e,n){const t=n.download!=="never",a=t?"download-visible-media":"structure-window",r=R(e,n,{kind:"read",steps:[a],allowEmptyMedia:t}),o=await $(r),s=o.steps.find(l=>l.step===a);if(!o.ok&&s?.reasonCode!=="no_observed_messages"){const l=s?.reasonCode||o.steps.find(p=>!p.ok&&!p.skipped)?.reasonCode||"wechat_direct_read_failed",j=s?.errorSummary||o.steps.find(p=>!p.ok&&!p.skipped)?.errorSummary||l;throw new Error(`${l}: ${j}`)}const d=g.join(o.outDir,t?"download-visible-messages.json":"structure-window-messages.json"),i=te(d),c=ee(e,n),f=ne(e);return{messages:i.map(l=>U(c,f,l)).filter(l=>l!=null),outDir:o.outDir,helperTracePath:o.helperTracePath}}function Pe(e){const n=e.command("wechat").description("Use WeChat through Shennian local runtime");n.option("--conversation <name>","WeChat conversation/group name"),n.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async a=>{const r=await F();if(a.json)console.log(JSON.stringify(r,null,2));else for(const o of r.checks){const s=o.ok?"\u2713":o.status==="warning"?"!":"\u2717";console.log(`${s} ${o.id}: ${o.message}`)}r.ok||(process.exitCode=1)}),n.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(a,r,o)=>{const s=W(o,r.conversation),d=r.text??a.join(" "),i=ue(r),c=await J({conversation:s,text:d,attachment:i,sessionId:r.sessionId,workDir:r.workDir,idempotencyKey:r.idempotencyKey,dryRun:r.dryRun,traceId:r.traceId,timeoutMs:r.timeout?h(r.timeout,24e4):void 0,transport:M(r.transport)});oe(c,r.json||r.format==="json")}),n.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(a,r,o)=>{const s=W(o,r.conversation),d=me(r.json?"json":r.format),i=await K({conversation:s,limit:h(r.limit??a,10),download:he(r.download),traceId:r.traceId,timeoutMs:r.timeout?h(r.timeout,24e4):void 0,sessionId:r.sessionId,workDir:r.workDir,transport:M(r.transport)});se(i,d)})}function v(e){return{ok:e.checks.every(n=>n.ok||n.status==="warning"),checks:e.checks,serverUrl:e.serverUrl,machineId:e.machineId}}function m(e,n){return{id:e,ok:!0,status:"ready",message:n}}function G(e,n,t){return{id:e,ok:!1,status:"warning",reasonCode:n,message:t}}function C(e,n,t){return{id:e,ok:!1,status:"blocked",reasonCode:n,message:t}}function Q(e){return e.replace(/\/+$/,"")}function N(){const e=_("runtime","manager-ipc.json");let n;try{n=JSON.parse(S.readFileSync(e,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const t=Number(n.pid);if(Number.isInteger(t)&&t>0&&!we(t))throw new Error(`Manager IPC runtime file is stale for PID ${t}. Restart Shennian Desktop or run \`shennian restart\`.`);const a=typeof n.url=="string"?n.url.replace(/\/+$/,""):"",r=typeof n.token=="string"?n.token:"";if(!a||!r)throw new Error(`Manager IPC runtime file is incomplete: ${e}`);return{url:a,token:r}}function X(){try{return N()}catch{return null}}function D(e){if(e.directRuntime)return null;const n=e.transport??"auto";return n==="direct"?null:e.ipc?e.ipc:n==="daemon"||e.sessionId?.trim()?N():X()}async function P(e,n,t){return w(e,"/wechat-rpa/channel/upsert",{managerSessionId:n.sessionId,id:n.channelId,name:`WeChat ${n.conversationName}`,workDir:n.workDir,enabled:!0,groups:[{name:n.conversationName}],canReply:!0,source:t.source||"wechat-channel",recentLimit:h(t.recentLimit,20),pollIntervalMs:h(t.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:t.download!=="never",privacyConsentAccepted:!0},t.fetchImpl,t.timeoutMs)}async function w(e,n,t,a=fetch,r){const o=String(t.managerSessionId||"");if(!o)throw new Error("managerSessionId is required");const s=r?new AbortController:null,d=s?setTimeout(()=>s.abort(),r):null;try{const i=await a(`${e.url}${n}`,{method:"POST",headers:{authorization:`Bearer ${e.token}`,"content-type":"application/json","x-shennian-manager-session-id":o},body:JSON.stringify(t),signal:s?.signal}),c=await i.json().catch(()=>({ok:!1,error:i.statusText}));if(!i.ok||!c.ok)throw new Error(c.error||`Manager IPC failed: ${i.status}`);return c}finally{d&&clearTimeout(d)}}function x(e){const n=e.conversation.trim();if(!n)throw new Error("--conversation is required");const t=T("wechat-cli-conversation",n),a=e.sessionId?.trim()||`wechat-cli-${t.slice(-24)}`,r=`wechat-rpa:${a}`;return{sessionId:a,channelId:r,conversationId:q(n),conversationName:n,workDir:g.resolve(e.workDir||_("wechat-cli",A(n)))}}async function Y(e,n,t){try{return await w(e,"/wechat-rpa/channel/sync",{managerSessionId:n.sessionId},t.fetchImpl,t.timeoutMs)}catch(a){if(Ie(a))return null;throw a}}function R(e,n,t){const a=n.traceId||`wechat-cli-${t.kind}-${Date.now().toString(36)}`,r=g.join(e.workDir,"runs",A(a)),o=t.attachment;return{conversation:e.conversationName,mode:"custom",steps:t.steps,outDir:r,workDir:e.workDir,traceId:a,runtimeId:e.channelId,machineId:process.env.SHENNIAN_MACHINE_ID||k().machineId||"local",sessionId:e.sessionId,serverUrl:k().serverUrl,platform:ae(),text:t.text,...o?.localPath&&o.kind==="image"?{imagePath:o.localPath}:{},...o?.localPath&&o.kind==="video"?{videoPath:o.localPath}:{},...o?.localPath&&o.kind!=="image"&&o.kind!=="video"?{filePath:o.localPath}:{},allowEmptyMedia:t.allowEmptyMedia,requireServer:!1}}function ee(e,n){return{id:e.channelId,type:"wechat-rpa",name:`WeChat ${e.conversationName}`,sessionId:e.sessionId,managerSessionId:e.sessionId,workDir:e.workDir,enabled:!0,secretRef:`wechat-cli:${e.channelId}`}}function ne(e){return{bindingId:z(e.channelId,e.conversationName),sessionId:e.sessionId,conversationDisplayName:e.conversationName,enabled:!0,allowReply:!0,downloadMedia:!0}}function te(e){try{const n=JSON.parse(S.readFileSync(e,"utf-8"));return Array.isArray(n)?n.filter(u):[]}catch{return[]}}function re(e){return e==="dry_run"||e==="queued"||e==="sent"||e==="sent_unconfirmed"||e==="confirmed_echo"||e==="manual_review"||e==="pending"||e==="ok"?e:null}function ae(){if(process.platform==="darwin")return"darwin";if(process.platform==="win32")return"win32"}function W(e,n){const t=e.parent?.opts()??{},a=n||t.conversation||"";if(!a.trim())throw new Error("--conversation is required");return a}function oe(e,n){if(n){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function se(e,n){if(n==="json"){console.log(JSON.stringify(e,null,2));return}console.log(ie(e).trimEnd())}function ie(e){const n=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
5
- `;for(const t of e.messages)n.push(`${le(t)}: ${de(t)}`);return`${n.join(`
1
+ import F from"node:crypto";import x from"node:fs";import y from"node:path";import{SERVERS as J}from"../region.js";import{loadConfig as k,resolveShennianPath as $}from"../config/index.js";import{observedMessageToExternalEvent as K,weChatChannelConversationId as H,weChatChannelBindingId as V}from"../channels/wechat-rpa/product-channel.js";import{readExternalAttachment as Z}from"./external-attachments.js";import{runWeChatChannelActionSmoke as D}from"../devtools/wechat-channel-action-smoke.js";async function G(e={}){const t=k(),n=ne(e.serverUrl||t.serverUrl||J.cn.url),a=e.machineToken||t.machineToken,r=e.machineId||t.machineId,o=e.fetchImpl||fetch,s=[g("cli_installed","Shennian CLI is installed."),a?g("machine_paired","Machine token is configured."):S("machine_paired","machine_not_paired","Machine is not paired. Run shennian pair first.")];if(!a)return C({checks:s,serverUrl:n,machineId:r});try{const i=await o(`${n}/api/channels/wechat/runtime-policy`,{headers:{authorization:`Bearer ${a}`}}),c=await i.json().catch(()=>null);if(!i.ok||c?.ok===!1){const u=f(c?.reasonCode)||i.statusText||"request_failed";return s.push(S("server_policy",u,`WeChat runtime policy is blocked: ${u}`)),C({checks:s,serverUrl:n,machineId:r})}const d=p(c?.usagePolicy)?c.usagePolicy:{};s.push(g("server_policy","Server WeChat policy is reachable.")),s.push(g("entitlement","Account has Pro / Team external channel entitlement.")),s.push(g("credits",d.billingMode==="credits"?"Credit billing is enabled; balance is checked by server calls.":"Credits are not metered in current billing mode."));const l=p(c?.runtime)&&p(c.runtime.current)?c.runtime.current:null;s.push(l?g("runtime_registered","Local WeChat runtime is registered on the server."):ee("runtime_registered","runtime_not_registered","Local WeChat runtime is not registered yet; open Shennian Desktop/daemon and enable Use WeChat."))}catch(i){s.push(S("server_policy","network_unavailable",i instanceof Error?i.message:"Unable to reach Shennian server."))}return C({checks:s,serverUrl:n,machineId:r})}async function P(e){const t=e.text.trim();if(!t&&!e.attachment)throw new Error("Message text or attachment is required");const n=b(e),a=e.attachment?we(e.attachment):void 0;if(e.dryRun)return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,sendStatus:"dry_run",pending:!1,attachment:a,attachments:a?[a]:[],dryRun:!0,reasonCode:null,message:"Dry run completed. No WeChat message was sent."};const r=M(e);if(!r){const l=e.directRuntime?await e.directRuntime.send(n,e):await Q(n,e);return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:l.outDir??n.workDir,helperTracePath:l.helperTracePath??null,sendStatus:l.sendStatus??(l.pending?"pending":"sent"),pending:l.pending===!0,attachment:a,attachments:a?[a]:[],reasonCode:null,message:l.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}if(r.mode==="tool"){let l;try{l=await w(r.ctx,"/wechat-rpa/tool/send",{managerSessionId:n.sessionId,binding:n,text:t,attachment:e.attachment,traceId:e.traceId,timeoutMs:e.timeoutMs},e.fetchImpl,e.timeoutMs)}catch(h){if(R(e,h))return P({...e,transport:"direct"});throw h}const u=j(l.sendStatus)??(l.pending===!0?"pending":"sent");return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:f(l.outDir)||n.workDir,helperTracePath:f(l.helperTracePath)||null,sendStatus:u,pending:l.pending===!0,attachment:a,attachments:a?[a]:[],reasonCode:null,message:l.pending?"WeChat message is queued or waiting for confirmation.":"WeChat message sent."}}const o=r.ctx;await T(o,n,e);const s=e.idempotencyKey||q("wechat-cli-send",`${n.channelId}
2
+ ${n.conversationId}
3
+ ${t}
4
+ ${Date.now()}`),i=await w(o,"/external/reply",{managerSessionId:n.sessionId,channelId:n.channelId,conversationId:n.conversationId,text:t,attachment:e.attachment,idempotencyKey:s},e.fetchImpl,e.timeoutMs),c=await te(o,n,e),d=c?ue(c,s,i.pending===!0):i.pending===!0?"queued":"ok";return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,sendStatus:d,pending:i.pending===!0&&(d==="queued"||d==="pending"),attachment:a,attachments:a?[a]:[],reasonCode:null,message:c?d==="confirmed_echo"?"WeChat message sent and confirmed.":d==="manual_review"?"WeChat message was submitted but needs manual review.":"WeChat message submitted to local daemon.":"WeChat message submitted to local daemon; confirmation sync timed out."}}async function W(e){const t=v(e.limit,10),n=b(e),a=M(e);if(!a){const d=e.directRuntime?{messages:await e.directRuntime.readLatest(n,{...e,limit:t,recentLimit:e.recentLimit??t}),outDir:n.workDir,helperTracePath:null}:await Y(n,{...e,limit:t,recentLimit:e.recentLimit??t}),l=d.messages.filter(h=>h.conversationId===n.conversationId||h.conversationName===n.conversationName).slice(-t),u=N(l);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:d.outDir,helperTracePath:d.helperTracePath,messages:l,attachments:u,count:l.length,reasonCode:null,message:`Read ${l.length} WeChat message(s).`}}if(a.mode==="tool"){let d;try{d=await w(a.ctx,"/wechat-rpa/tool/read",{managerSessionId:n.sessionId,binding:n,limit:t,download:e.download,traceId:e.traceId,timeoutMs:e.timeoutMs},e.fetchImpl,e.timeoutMs)}catch(m){if(R(e,m))return W({...e,transport:"direct"});throw m}const u=(Array.isArray(d.messages)?d.messages.filter(B):[]).filter(m=>m.conversationId===n.conversationId||m.conversationName===n.conversationName).slice(-t),h=N(u);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:f(d.outDir)||n.workDir,helperTracePath:f(d.helperTracePath)||null,messages:u,attachments:h,count:u.length,reasonCode:null,message:`Read ${u.length} WeChat message(s).`}}const r=a.ctx;await T(r,n,{...e,recentLimit:e.recentLimit??t});const o=await w(r,"/wechat-rpa/channel/sync",{managerSessionId:n.sessionId},e.fetchImpl,e.timeoutMs),i=(Array.isArray(o.messages)?o.messages.filter(B):[]).filter(d=>d.conversationId===n.conversationId||d.conversationName===n.conversationName).slice(-t),c=N(i);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,messages:i,attachments:c,count:i.length,reasonCode:null,message:`Read ${i.length} WeChat message(s).`}}async function Q(e,t){const n=X(t.attachment),a=E(e,t,{kind:"send",steps:["open-read",n],text:t.text,attachment:t.attachment}),r=await D(a),o=r.steps.find(i=>i.step===n);if(!r.ok||!o?.ok){const i=r.steps.find(u=>!u.ok&&!u.skipped),c=o?.reasonCode==="open_read_required"?i??o:o??i,d=c?.reasonCode||"wechat_direct_send_failed",l=c?.errorSummary||d;throw new Error(`${d}: ${l}`)}const s=j(o.details?.sendStatus)??"sent_unconfirmed";return{pending:s==="queued"||s==="pending"||s==="sent_unconfirmed",sendStatus:s,outDir:r.outDir,helperTracePath:r.helperTracePath}}function X(e){return e?e.kind==="image"?"send-image":e.kind==="video"?"send-video":"send-file":"send-text"}async function Y(e,t){const n=t.download!=="never",a=n?"download-visible-media":"structure-window",r=E(e,t,{kind:"read",steps:[a],allowEmptyMedia:n}),o=await D(r),s=o.steps.find(u=>u.step===a);if(!o.ok&&s?.reasonCode!=="no_observed_messages"){const u=s?.reasonCode||o.steps.find(m=>!m.ok&&!m.skipped)?.reasonCode||"wechat_direct_read_failed",h=s?.errorSummary||o.steps.find(m=>!m.ok&&!m.skipped)?.errorSummary||u;throw new Error(`${u}: ${h}`)}const i=y.join(o.outDir,n?"download-visible-messages.json":"structure-window-messages.json"),c=oe(i),d=re(e,t),l=ae(e);return{messages:c.map(u=>K(d,l,u)).filter(u=>u!=null),outDir:o.outDir,helperTracePath:o.helperTracePath}}function We(e){const t=e.command("wechat").description("Use WeChat through Shennian local runtime");t.option("--conversation <name>","WeChat conversation/group name"),t.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async a=>{const r=await G();if(a.json)console.log(JSON.stringify(r,null,2));else for(const o of r.checks){const s=o.ok?"\u2713":o.status==="warning"?"!":"\u2717";console.log(`${s} ${o.id}: ${o.message}`)}r.ok||(process.exitCode=1)}),t.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s;try{s=A(o,r.conversation);const i=r.text??a.join(" "),c=fe(r),d=await P({conversation:s,text:i,attachment:c,sessionId:r.sessionId,workDir:r.workDir,idempotencyKey:r.idempotencyKey,dryRun:r.dryRun,traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,transport:U(r.transport)});ie(d,r.json||r.format==="json")}catch(i){O("write",s||r.conversation,i,r.json||r.format==="json")}}),t.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s,i="markdown";try{s=A(o,r.conversation),i=pe(r.json?"json":r.format);const c=await W({conversation:s,limit:v(r.limit??a,10),download:ge(r.download),traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,sessionId:r.sessionId,workDir:r.workDir,transport:U(r.transport)});ce(c,i)}catch(c){O("read",s||r.conversation,c,r.json||i==="json")}})}function C(e){return{ok:e.checks.every(t=>t.ok||t.status==="warning"),checks:e.checks,serverUrl:e.serverUrl,machineId:e.machineId}}function g(e,t){return{id:e,ok:!0,status:"ready",message:t}}function ee(e,t,n){return{id:e,ok:!1,status:"warning",reasonCode:t,message:n}}function S(e,t,n){return{id:e,ok:!1,status:"blocked",reasonCode:t,message:n}}function ne(e){return e.replace(/\/+$/,"")}function _(){const e=$("runtime","manager-ipc.json");let t;try{t=JSON.parse(x.readFileSync(e,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const n=Number(t.pid);if(Number.isInteger(n)&&n>0&&!ye(n))throw new Error(`Manager IPC runtime file is stale for PID ${n}. Restart Shennian Desktop or run \`shennian restart\`.`);const a=typeof t.url=="string"?t.url.replace(/\/+$/,""):"",r=typeof t.token=="string"?t.token:"";if(!a||!r)throw new Error(`Manager IPC runtime file is incomplete: ${e}`);return{url:a,token:r}}function Me(){try{return _()}catch{return null}}function M(e){if(e.directRuntime)return null;const t=e.transport??"auto";return t==="direct"?null:e.ipc?{ctx:e.ipc,mode:"session"}:e.sessionId?.trim()?{ctx:_(),mode:"session"}:t==="daemon"?{ctx:_(),mode:"tool"}:null}function R(e,t){if((e.transport??"auto")!=="auto"||e.sessionId?.trim()||e.ipc)return!1;const a=t instanceof Error?t.message:String(t||"");return/Unknown manager IPC path:\s*\/wechat-rpa\/tool\//i.test(a)}async function T(e,t,n){return w(e,"/wechat-rpa/channel/upsert",{managerSessionId:t.sessionId,id:t.channelId,name:`WeChat ${t.conversationName}`,workDir:t.workDir,enabled:!0,groups:[{name:t.conversationName}],canReply:!0,source:n.source||"wechat-channel",recentLimit:v(n.recentLimit,20),pollIntervalMs:v(n.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:n.download!=="never",privacyConsentAccepted:!0},n.fetchImpl,n.timeoutMs)}async function w(e,t,n,a=fetch,r){const o=String(n.managerSessionId||"");if(!o)throw new Error("managerSessionId is required");const s=r?new AbortController:null,i=s?setTimeout(()=>s.abort(),r):null;try{const c=await a(`${e.url}${t}`,{method:"POST",headers:{authorization:`Bearer ${e.token}`,"content-type":"application/json","x-shennian-manager-session-id":o},body:JSON.stringify(n),signal:s?.signal}),d=await c.json().catch(()=>({ok:!1,error:c.statusText}));if(!c.ok||!d.ok)throw new Error(d.error||`Manager IPC failed: ${c.status}`);return d}finally{i&&clearTimeout(i)}}function b(e){const t=e.conversation.trim();if(!t)throw new Error("--conversation is required");const n=q("wechat-cli-conversation",t),a=e.sessionId?.trim()||`wechat-cli-${n.slice(-24)}`,r=`wechat-rpa:${a}`;return{sessionId:a,channelId:r,conversationId:H(t),conversationName:t,workDir:y.resolve(e.workDir||$("wechat-cli",z(t)))}}async function te(e,t,n){try{return await w(e,"/wechat-rpa/channel/sync",{managerSessionId:t.sessionId},n.fetchImpl,n.timeoutMs)}catch(a){if(ke(a))return null;throw a}}function E(e,t,n){const a=t.traceId||`wechat-cli-${n.kind}-${Date.now().toString(36)}`,r=y.join(e.workDir,"runs",z(a)),o=n.attachment;return{conversation:e.conversationName,mode:"custom",steps:n.steps,outDir:r,workDir:e.workDir,traceId:a,runtimeId:e.channelId,machineId:process.env.SHENNIAN_MACHINE_ID||k().machineId||"local",sessionId:e.sessionId,serverUrl:k().serverUrl,platform:se(),text:n.text,...o?.localPath&&o.kind==="image"?{imagePath:o.localPath}:{},...o?.localPath&&o.kind==="video"?{videoPath:o.localPath}:{},...o?.localPath&&o.kind!=="image"&&o.kind!=="video"?{filePath:o.localPath}:{},allowEmptyMedia:n.allowEmptyMedia,skipRuntimeUpsert:!0,requireServer:!1}}function re(e,t){return{id:e.channelId,type:"wechat-rpa",name:`WeChat ${e.conversationName}`,sessionId:e.sessionId,managerSessionId:e.sessionId,workDir:e.workDir,enabled:!0,secretRef:`wechat-cli:${e.channelId}`}}function ae(e){return{bindingId:V(e.channelId,e.conversationName),sessionId:e.sessionId,conversationDisplayName:e.conversationName,enabled:!0,allowReply:!0,downloadMedia:!0}}function oe(e){try{const t=JSON.parse(x.readFileSync(e,"utf-8"));return Array.isArray(t)?t.filter(p):[]}catch{return[]}}function j(e){return e==="dry_run"||e==="queued"||e==="sent"||e==="sent_unconfirmed"||e==="confirmed_echo"||e==="manual_review"||e==="pending"||e==="ok"?e:null}function se(){if(process.platform==="darwin")return"darwin";if(process.platform==="win32")return"win32"}function A(e,t){const n=e.parent?.opts()??{},a=t||n.conversation||"";if(!a.trim())throw new Error("--conversation is required");return a}function ie(e,t){if(t){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function ce(e,t){if(t==="json"){console.log(JSON.stringify(e,null,2));return}console.log(le(e).trimEnd())}function O(e,t,n,a){const r=de(e,t,n);a?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function de(e,t,n){const a=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=I(a)||"wechat_tool_failed",o=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o?.[1]||"wechat_tool_failed",i=I(o?.[2]||r),c=i&&i!==s?i:s;return{ok:!1,operation:e,...t?.trim()?{conversation:t.trim()}:{},reasonCode:s,message:c}}function le(e){const t=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
5
+ `;for(const n of e.messages)t.push(`${he(n)}: ${me(n)}`);return`${t.join(`
6
6
  `)}
7
- `}function ce(e,n,t){const a=u(e.channel)?e.channel:{},o=(Array.isArray(a.wechatRpaPendingReplies)?a.wechatRpaPendingReplies:[]).filter(u).find(c=>y(c.idempotencyKey)===n),s=y(o?.status);if(s==="confirmed_echo"||s==="sent_unconfirmed"||s==="manual_review"||s==="queued")return s;const i=(Array.isArray(a.wechatRpaRecentTaskSummaries)?a.wechatRpaRecentTaskSummaries:[]).filter(u).map(c=>y(c.status)).find(Boolean);return i==="sent"?"sent":i==="confirmed_echo"?"confirmed_echo":i==="manual_review"?"manual_review":t?"pending":"ok"}function de(e){const n=e.attachments||[],t=I(e.text);return t&&n.length===0?t:t&&n.length>0?`${t} ${n.map(b).join("\u3001")}`:n.length>0?n.map(b).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function le(e){return I(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function b(e){const n=I(e.name||g.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),t=e.localPath||e.url||"",a=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return t?`[${ge(n)}](${pe(t)})${a}`:`${n}${a}`}function ue(e){const n=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(t=>!!t);if(n.length>1)throw new Error("Pass only one of --file, --image, or --video");if(n.length!==0)return B(n[0].path,n[0].kind)}function me(e){const n=String(e||"markdown").trim().toLowerCase();if(n==="json")return"json";if(n==="markdown"||n==="md"||n==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function he(e){const n=String(e||"auto").trim().toLowerCase();if(n==="auto"||n==="never")return n;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function M(e){const n=String(e||"auto").trim().toLowerCase();if(n==="auto"||n==="daemon"||n==="direct")return n;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function fe(e){return{kind:e.kind,name:e.name,mimeType:e.mimeType,size:e.size,localPath:e.localPath}}function E(e){return e.flatMap(n=>n.attachments??[])}function pe(e){const n=e.replace(/\\/g,"/");return/[\s()<>]/.test(n)?`<${n.replace(/[<>]/g,"")}>`:n}function ge(e){return I(e).replace(/([\\\]])/g,"\\$1")}function I(e){return String(e||"").replace(/\s+/g," ").trim()}function we(e){try{return process.kill(e,0),!0}catch{return!1}}function Ie(e){if(!e||typeof e!="object")return!1;const n=e,t=typeof n.name=="string"?n.name:"",a=typeof n.message=="string"?n.message:"";return t==="AbortError"||n.code==="ABORT_ERR"||/aborted|abort/i.test(a)}function T(e,n){return`${e}:${L.createHash("sha256").update(n).digest("hex").slice(0,32)}`}function A(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"conversation"}function h(e,n){const t=Number(e);return!Number.isFinite(t)||t<=0?n:Math.floor(t)}function ye(e){return!!e&&typeof e=="object"&&!Array.isArray(e)&&e.type==="external.message"&&typeof e.channelId=="string"&&typeof e.conversationId=="string"&&typeof e.messageId=="string"&&typeof e.text=="string"}function u(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function y(e){return typeof e=="string"&&e?e:void 0}export{ie as buildWeChatReadMarkdown,Pe as registerWeChatCommand,F as runWeChatDoctor,K as runWeChatReadLatest,J as runWeChatSend};
7
+ `}function ue(e,t,n){const a=p(e.channel)?e.channel:{},o=(Array.isArray(a.wechatRpaPendingReplies)?a.wechatRpaPendingReplies:[]).filter(p).find(d=>f(d.idempotencyKey)===t),s=f(o?.status);if(s==="confirmed_echo"||s==="sent_unconfirmed"||s==="manual_review"||s==="queued")return s;const c=(Array.isArray(a.wechatRpaRecentTaskSummaries)?a.wechatRpaRecentTaskSummaries:[]).filter(p).map(d=>f(d.status)).find(Boolean);return c==="sent"?"sent":c==="confirmed_echo"?"confirmed_echo":c==="manual_review"?"manual_review":n?"pending":"ok"}function me(e){const t=e.attachments||[],n=I(e.text);return n&&t.length===0?n:n&&t.length>0?`${n} ${t.map(L).join("\u3001")}`:t.length>0?t.map(L).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function he(e){return I(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function L(e){const t=I(e.name||y.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",a=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${ve(t)}](${Ie(n)})${a}`:`${t}${a}`}function fe(e){const t=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(n=>!!n);if(t.length>1)throw new Error("Pass only one of --file, --image, or --video");if(t.length!==0)return Z(t[0].path,t[0].kind)}function pe(e){const t=String(e||"markdown").trim().toLowerCase();if(t==="json")return"json";if(t==="markdown"||t==="md"||t==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function ge(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="never")return t;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function U(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="daemon"||t==="direct")return t;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function we(e){return{kind:e.kind,name:e.name,mimeType:e.mimeType,size:e.size,localPath:e.localPath}}function N(e){return e.flatMap(t=>t.attachments??[])}function Ie(e){const t=e.replace(/\\/g,"/");return/[\s()<>]/.test(t)?`<${t.replace(/[<>]/g,"")}>`:t}function ve(e){return I(e).replace(/([\\\]])/g,"\\$1")}function I(e){return String(e||"").replace(/\s+/g," ").trim()}function ye(e){try{return process.kill(e,0),!0}catch{return!1}}function ke(e){if(!e||typeof e!="object")return!1;const t=e,n=typeof t.name=="string"?t.name:"",a=typeof t.message=="string"?t.message:"";return n==="AbortError"||t.code==="ABORT_ERR"||/aborted|abort/i.test(a)}function q(e,t){return`${e}:${F.createHash("sha256").update(t).digest("hex").slice(0,32)}`}function z(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"conversation"}function v(e,t){const n=Number(e);return!Number.isFinite(n)||n<=0?t:Math.floor(n)}function B(e){return!!e&&typeof e=="object"&&!Array.isArray(e)&&e.type==="external.message"&&typeof e.channelId=="string"&&typeof e.conversationId=="string"&&typeof e.messageId=="string"&&typeof e.text=="string"}function p(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function f(e){return typeof e=="string"&&e?e:void 0}export{le as buildWeChatReadMarkdown,de as buildWeChatToolErrorResult,Y as readDirectWeChatLatestOnce,We as registerWeChatCommand,G as runWeChatDoctor,W as runWeChatReadLatest,P as runWeChatSend,Q as sendDirectWeChatMessageOnce};
@@ -19,6 +19,7 @@ export type WeChatChannelActionSmokeOptions = {
19
19
  videoPath?: string;
20
20
  downloadAnchorText?: string;
21
21
  allowEmptyMedia?: boolean;
22
+ skipRuntimeUpsert?: boolean;
22
23
  requireServer: boolean;
23
24
  };
24
25
  export type WeChatChannelActionSmokeResult = {
@@ -94,3 +95,6 @@ export declare function buildSendAndConfirmEvidence(input: WeChatChannelSendAndC
94
95
  failedOutboundCount: number;
95
96
  manualReviewCount: number;
96
97
  };
98
+ export declare function countMarkerOccurrencesInOcrBlocks(blocks: Array<{
99
+ text?: string | null;
100
+ }>, marker: string): number;
@@ -1,10 +1,10 @@
1
- import D from"node:crypto";import{spawnSync as ne}from"node:child_process";import w from"node:fs";import i from"node:path";import{fileURLToPath as oe}from"node:url";import{buildBottomPageReport as M,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as re}from"../config/index.js";import{createWeChatChannelApiClient as se}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ae,WECHAT_CHANNEL_HELPER_VERSION as ie}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as de}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as j}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as ce,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as le,saveWeChatChannelLedger as ue,updateWeChatChannelBindingLedger as me}from"../channels/wechat-channel/ledger.js";import{WeChatChannelOutboundSender as U,sendQueuedWeChatOutboundRecords as S}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as C,fallbackWeChatMessageInputPointForWindow as he,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as fe,openConversationBySearch as I,recognizeWeChatScreenshot as E}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as ge}from"../channels/wechat-channel/runtime.js";function pe(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const we="ABC",be="wechat-channel-action-smoke";function ve(e){const t=e.filter(l=>l!=="--"),n=st(t),o=it(t,"--step").map(at).filter(l=>l!=null),r=re(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?dt(o):ye(n);return{conversation:g(t,"--conversation")||g(t,"--group")||we,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||be,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:nt(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ye(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function Se(e){const t=Date.now(),n=new Date(t).toISOString();w.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");w.writeFileSync(o,"");const r=Qe(e),s=r.bindings[0],a=Ze(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ce(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await ke({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),f(i.join(e.outDir,"summary.json"),d)}return d}function Ce(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function ke(e){try{return e.step==="open-read"?await Re(e):e.step==="structure-window"?await Le(e):e.step==="server-observe"?await Oe(e):e.step==="read-bottom-page"?await Ne(e):e.step==="download-visible-media"?await Fe(e):e.step==="send-and-confirm"?await Ee(e):e.step.startsWith("human-")?await Ae(e):await V(e)}catch(t){const n=ft(t),o=await Ie(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:gt(n),...o?{details:o}:{}}}}async function Ie(e,t,n){if(!xe(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return f(o,r),{failureDiagnosticsPath:o}}function xe(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function Ae(e){if(e.step==="human-observe-deferred")return await _e(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=G(e.helper,J(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const ee=await C(s,e.options.traceId),te=await k(s,ee,e.options.traceId);return I({helper:s,window:te,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new U({helper:s,platform:e.runtime.policy.platform,openConversation:d,traceId:e.options.traceId,activityGatePolicy:e.step==="human-send-user-active-timeout"?{keyDownThresholdMs:3e4,mouseMovedThresholdMs:3e4,mouseClickThresholdMs:3e4,scrollWheelThresholdMs:3e4}:void 0});let h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ue()),h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Ve(e.step,r)?await N({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?ce({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],b=Ge(u,h,p.confirmedRecords),v={case:e.step,marker:n,runStatus:b,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return f(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Je(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function _e(e){const t=G(e.helper,J("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return f(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Re(e){const t=await C(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await I({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await De({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await W(e.helper,n.windowId,e.options.traceId),s=T(e.options.outDir,"open-read-full-window",r),a=await E(e.helper,r,e.options.traceId);return f(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function De(e){try{const t=await W(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=T(e.outDir,"open-read-failure-window",t),o=await E(e.helper,t,e.traceId);f(i.join(e.outDir,"open-read-failure-ocr.json"),o),f(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){f(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function V(e){return await Me(e,ot(e.step,e.options))}async function Me(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await N({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new U({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await C(e.helper,e.options.traceId),b=await k(e.helper,u,e.options.traceId);return{...await I({helper:e.helper,window:b,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:he(b)}}}),a=await Te({ledger:n,bindingId:e.binding.bindingId,sender:s});await R(900);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,h=c&&t.text?await ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=Pe({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:h});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:h,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function Pe(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Te(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;r.waitingRecords.length>0&&s<=t&&!(Date.now()-o>=n);s+=1){const a=je(r.waitingRecords[0]?.nextAttemptAt,o,n);await R(a),r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function je(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function We(e){const t=e.sendResults.filter(u=>!z(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
1
+ import R from"node:crypto";import{spawnSync as se}from"node:child_process";import w from"node:fs";import i from"node:path";import{fileURLToPath as ae}from"node:url";import{buildBottomPageReport as D,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as ie}from"../config/index.js";import{createWeChatChannelApiClient as de}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ce,WECHAT_CHANNEL_HELPER_VERSION as ue}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as le}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as T}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as me,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as fe,saveWeChatChannelLedger as he,updateWeChatChannelBindingLedger as ge}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as U}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as z,sendQueuedWeChatOutboundRecords as C}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,ensureWeChatWindowReady as S,fallbackWeChatMessageInputPointForWindow as pe,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as we,openConversationBySearch as x,recognizeWeChatScreenshot as W}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as be}from"../channels/wechat-channel/runtime.js";function ve(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const ye="ABC",Ce="wechat-channel-action-smoke";function Se(e){const t=e.filter(l=>l!=="--"),n=ct(t),o=lt(t,"--step").map(ut).filter(l=>l!=null),r=ie(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?mt(o):ke(n);return{conversation:g(t,"--conversation")||g(t,"--group")||ye,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||Ce,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:at(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ke(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function xe(e){const t=Date.now(),n=new Date(t).toISOString();w.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");w.writeFileSync(o,"");const r=et(e),s=r.bindings[0],a=tt(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ie(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await Ae({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),h(i.join(e.outDir,"summary.json"),d)}return d}function Ie(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function Ae(e){try{return e.step==="open-read"?await Pe(e):e.step==="structure-window"?await Ke(e):e.step==="server-observe"?await qe(e):e.step==="read-bottom-page"?await Le(e):e.step==="download-visible-media"?await He(e):e.step==="send-and-confirm"?await Fe(e):e.step.startsWith("human-")?await De(e):await V(e)}catch(t){const n=bt(t),o=await _e(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:vt(n),...o?{details:o}:{}}}}async function _e(e,t,n){if(!Re(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return h(o,r),{failureDiagnosticsPath:o}}function Re(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function De(e){if(e.step==="human-observe-deferred")return await Me(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=J(e.helper,Q(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const oe=await S(s,e.options.traceId),re=await k(s,oe,e.options.traceId);return x({helper:s,window:re,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new z({helper:s,platform:e.runtime.policy.platform,openConversation:d,traceId:e.options.traceId,activityGatePolicy:e.step==="human-send-user-active-timeout"?{keyDownThresholdMs:3e4,mouseMovedThresholdMs:3e4,mouseClickThresholdMs:3e4,scrollWheelThresholdMs:3e4}:void 0});let f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ge()),f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Je(e.step,r)?await B({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?me({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],b=Xe(u,f,p.confirmedRecords),v={case:e.step,marker:n,runStatus:b,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return h(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Ye(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function Me(e){const t=J(e.helper,Q("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return h(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Pe(e){const t=await S(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await x({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await Te({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await j(e.helper,n.windowId,e.options.traceId),s=P(e.options.outDir,"open-read-full-window",r),a=await W(e.helper,r,e.options.traceId);return h(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function Te(e){try{const t=await j(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=P(e.outDir,"open-read-failure-window",t),o=await W(e.helper,t,e.traceId);h(i.join(e.outDir,"open-read-failure-ocr.json"),o),h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function V(e){return await je(e,it(e.step,e.options))}async function je(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await B({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new z({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await S(e.helper,e.options.traceId),b=await k(e.helper,u,e.options.traceId);return{...await x({helper:e.helper,window:b,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:pe(b)}}}),a=await Ee({ledger:n,bindingId:e.binding.bindingId,sender:s});await U("send-after-submit",`${e.options.traceId}:${e.step}:after-submit`);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,f=c&&t.text?await Ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=We({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:f});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:f,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function We(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Ee(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;r.waitingRecords.length>0&&s<=t&&!(Date.now()-o>=n);s+=1){const a=$e(r.waitingRecords[0]?.nextAttemptAt,o,n);await L(a),r=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function $e(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function Be(e){const t=e.sendResults.filter(u=>!G(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
2
2
  `)).join(`
3
- `),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(z).length,d=$(t,"waitingRecords"),l=$(t,"failedRecords"),c=$(t,"manualReviewRecords"),h=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!h&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:h?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function z(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function $(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Ee(e){const t=["send-text","send-file","send-image","send-video"],n=[];for(const c of t)n.push(await V({...e,step:c}));await R(1200);const o=await $e(e,3),r=M(o.messages),s=i.join(e.options.outDir,"bottom-page.md"),a=y(o.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(o.attachmentsDir)});f(i.join(e.options.outDir,"bottom-page-summary.json"),r),f(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=We({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function $e(e,t){let n;for(let o=1;o<=t;o+=1)try{return await x(e)}catch(r){if(n=r,o>=t||!Be(r))break;try{const s=await C(e.helper,e.options.traceId),a=await k(e.helper,s,e.options.traceId);await I({helper:e.helper,window:a,binding:e.binding,traceId:e.options.traceId})}catch{}await R(1500*o)}throw n}function Be(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Ne(e){const t=await x(e),n=M(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return f(i.join(e.options.outDir,"bottom-page-summary.json"),n),f(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function Fe(e){const t=await x(e,{observePipeline:"edge-structure",persistLocalLedger:!0});B(e.options.outDir,"download-visible",t.observationEvidence);const n=M(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});f(i.join(e.options.outDir,"bottom-page-summary.json"),n),f(i.join(e.options.outDir,"download-visible-summary.json"),s),f(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function Oe(e){const t=await x(e,{observePipeline:"server-observe"});B(e.options.outDir,"server-observe",t.observationEvidence),f(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Q(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:O(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Le(e){const t=await x(e,{observePipeline:"edge-structure",downloadMedia:!1});B(e.options.outDir,"structure-window",t.observationEvidence),f(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Q(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:O(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function x(e,t={}){const n=pe({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=se({serverUrl:e.options.serverUrl});n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1});const r=i.join(e.options.outDir,"inbound-attachments"),s=t.persistLocalLedger?ct(e.options):void 0,a=s?le(s,e.runtime.runtimeId):void 0;let d,l,c;const h=await fe({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:o,workDir:e.options.workDir,attachmentsDir:r,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,observePipeline:t.observePipeline,localLedgerTailAnchors:a?lt(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:b})=>{d=qe(u),l=Ke(b),c=He(u,b,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=me({ledger:a,bindingId:e.binding.bindingId,observedMessages:h});m=u.binding,p=Y({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),ue(s,a)}return{messages:h,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function He(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function B(e,t,n){if(!n)return;const o=n.capture.dataBase64?T(e,`${t}-observation-window`,n.capture):void 0;f(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function qe(e){if(e.dataBase64)return`sha256:${D.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function Ke(e){return Y({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function G(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,h){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:h};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:h}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,h),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:h}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:h}):e.request(l,c,h)}}}function J(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ue(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ve(e,t){return t.sendStatus!=="sent_unconfirmed"?!1:e==="human-send-normal"||e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"}async function N(e){const t=await C(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await I({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await W(e.helper,n.windowId,e.traceId),s=T(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await E(e.helper,r,e.traceId);return f(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),(a.blocks||[]).map(l=>l.text||"").join(`
4
- `).split(e.marker).length-1}async function ze(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let o=0;for(let r=0;r<t;r+=1){if(o=await N({helper:e.helper,binding:e.binding,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${r+1}`,settleToBottom:e.settleToBottom}),n==null?o>0:o>n)return o;r<t-1&&await R(800)}return o}function Ge(e,t,n){return n.length>0||e.sendStatus==="confirmed_echo"?"confirmed_echo":t.waitingRecords.length>0?"send_waiting_user_idle":t.failedRecords.length>0?e.failureCode==="user_active_timeout"?"failed:user_active_timeout":"outbound_send_failed":t.manualReviewRecords.length>0||e.sendStatus==="manual_review"?"manual_review":e.sendStatus==="sent_unconfirmed"?"sent_unconfirmed":e.sendStatus}function Je(e,t){return e==="human-send-preflight-busy"?t.outboundStatus==="queued"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-user-active-timeout"?t.outboundStatus==="failed"&&t.runStatus==="failed:user_active_timeout"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-takeover-during-run"?(t.outboundStatus==="queued"||t.outboundStatus==="manual_review")&&t.observedMarkerCount===0:e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"||e==="human-send-normal"?t.outboundStatus==="confirmed_echo"||t.outboundStatus==="sent_unconfirmed"&&t.observedMarkerCount===1:t.runStatus==="observe_deferred_user_active"}function Qe(e){return ge({runtimeId:e.runtimeId,machineId:e.machineId,pollIntervalMs:6e4,platform:e.platform,foregroundPolicy:"work",bindings:[{bindingId:`action-smoke:${_(e.conversation)}`,sessionId:e.sessionId||`action-smoke:${_(e.runtimeId)}`,conversationDisplayName:e.conversation,enabled:!0,allowReply:!0,downloadMedia:!0}]})}function Ze(e,t){const n=ae({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new de({helperPath:n.helperPath,expectedHelperVersion:n.version||ie,requiredCapabilities:tt(e),guardUnsafeWindowsCommands:(e.platform??process.platform)==="win32",cleanupWindowsOverlays:(e.platform??process.platform)==="win32",requestLogger:Xe(t)})}function Xe(e){return t=>{w.appendFileSync(e,`${JSON.stringify(Ye(t))}
5
- `)}}function Ye(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=P(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=P(e.result)),t}function P(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return et(t,e)?{type:"string",length:e.length,sha256:D.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const s=e.slice(0,10).map(a=>P(a,t,n+1));return e.length>s.length?{type:"array",length:e.length,items:s}:s}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[s,a]of Object.entries(o))r[s]=P(a,s,n+1);return r}function et(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function tt(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?j("send"):e.steps.some(t=>t==="download-visible-media")?j("download"):j("observe")}function nt(e){const t=String(e||"").trim().toLowerCase();if(t){if(t==="windows"||t==="win32")return"win32";if(t==="macos"||t==="darwin")return"darwin";throw new Error(`unsupported_wechat_smoke_platform:${e}`)}}function ot(e,t){const n=i.join(t.outDir,"fixtures");w.mkdirSync(n,{recursive:!0});const o=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);if(e==="send-text")return{text:t.text||`codex-product-action text ${o}`};if(e==="send-file"){const r=t.filePath||i.join(n,`wechat-action-smoke-${o}.txt`);return t.filePath||w.writeFileSync(r,`Shennian WeChat product action smoke
3
+ `),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(G).length,d=E(t,"waitingRecords"),l=E(t,"failedRecords"),c=E(t,"manualReviewRecords"),f=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!f&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:f?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function G(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function E(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Fe(e){const t=["send-text","send-file","send-image","send-video"],n=[];for(const c of t)n.push(await V({...e,step:c}));await L(1200);const o=await Ne(e,3),r=D(o.messages),s=i.join(e.options.outDir,"bottom-page.md"),a=y(o.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(o.attachmentsDir)});h(i.join(e.options.outDir,"bottom-page-summary.json"),r),h(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=Be({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function Ne(e,t){let n;for(let o=1;o<=t;o+=1)try{return await I(e)}catch(r){if(n=r,o>=t||!Oe(r))break;try{const s=await S(e.helper,e.options.traceId),a=await k(e.helper,s,e.options.traceId);await x({helper:e.helper,window:a,binding:e.binding,traceId:e.options.traceId})}catch{}await U("observe-retry",`${e.options.traceId}:observe-retry:${o}`,1500*o)}throw n}function Oe(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Le(e){const t=await I(e),n=D(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function He(e){const t=await I(e,{observePipeline:"edge-structure",persistLocalLedger:!0});$(e.options.outDir,"download-visible",t.observationEvidence);const n=D(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"download-visible-summary.json"),s),h(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function qe(e){const t=await I(e,{observePipeline:"server-observe"});$(e.options.outDir,"server-observe",t.observationEvidence),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Ke(e){const t=await I(e,{observePipeline:"edge-structure",downloadMedia:!1});$(e.options.outDir,"structure-window",t.observationEvidence),h(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function I(e,t={}){const n=ve({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=de({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const r=i.join(e.options.outDir,"inbound-attachments"),s=t.persistLocalLedger?ft(e.options):void 0,a=s?fe(s,e.runtime.runtimeId):void 0;let d,l,c;const f=await we({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:o,workDir:e.options.workDir,attachmentsDir:r,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,observePipeline:t.observePipeline,localLedgerTailAnchors:a?ht(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:b})=>{d=ze(u),l=Ve(b),c=Ue(u,b,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=ge({ledger:a,bindingId:e.binding.bindingId,observedMessages:f});m=u.binding,p=ne({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),he(s,a)}return{messages:f,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function Ue(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function $(e,t,n){if(!n)return;const o=n.capture.dataBase64?P(e,`${t}-observation-window`,n.capture):void 0;h(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function ze(e){if(e.dataBase64)return`sha256:${R.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function Ve(e){return ne({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function J(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,f){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,f),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:f}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:f}):e.request(l,c,f)}}}function Q(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ge(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Je(e,t){return t.sendStatus!=="sent_unconfirmed"?!1:e==="human-send-normal"||e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"}async function B(e){const t=await S(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await x({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await j(e.helper,n.windowId,e.traceId),s=P(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await W(e.helper,r,e.traceId);return h(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),Qe(a.blocks||[],e.marker)}function Qe(e,t){const n=t.trim();if(!n)return 0;const o=e.map(a=>a.text||"").join(`
4
+ `),r=X(o,n);if(r>0)return r;const s=Z(n);return s.length<8?0:X(Z(o),s)}function Z(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function X(e,t){if(!t)return 0;let n=0,o=0;for(;;){const r=e.indexOf(t,o);if(r<0)break;n+=1,o=r+t.length}return n}async function Ze(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let o=0;for(let r=0;r<t;r+=1){if(o=await B({helper:e.helper,binding:e.binding,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${r+1}`,settleToBottom:e.settleToBottom}),n==null?o>0:o>n)return o;r<t-1&&await L(800)}return o}function Xe(e,t,n){return n.length>0||e.sendStatus==="confirmed_echo"?"confirmed_echo":t.waitingRecords.length>0?"send_waiting_user_idle":t.failedRecords.length>0?e.failureCode==="user_active_timeout"?"failed:user_active_timeout":"outbound_send_failed":t.manualReviewRecords.length>0||e.sendStatus==="manual_review"?"manual_review":e.sendStatus==="sent_unconfirmed"?"sent_unconfirmed":e.sendStatus}function Ye(e,t){return e==="human-send-preflight-busy"?t.outboundStatus==="queued"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-user-active-timeout"?t.outboundStatus==="failed"&&t.runStatus==="failed:user_active_timeout"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-takeover-during-run"?(t.outboundStatus==="queued"||t.outboundStatus==="manual_review")&&t.observedMarkerCount===0:e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"||e==="human-send-normal"?t.outboundStatus==="confirmed_echo"||t.outboundStatus==="sent_unconfirmed"&&t.observedMarkerCount===1:t.runStatus==="observe_deferred_user_active"}function et(e){return be({runtimeId:e.runtimeId,machineId:e.machineId,pollIntervalMs:6e4,platform:e.platform,foregroundPolicy:"work",bindings:[{bindingId:`action-smoke:${_(e.conversation)}`,sessionId:e.sessionId||`action-smoke:${_(e.runtimeId)}`,conversationDisplayName:e.conversation,enabled:!0,allowReply:!0,downloadMedia:!0}]})}function tt(e,t){const n=ce({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new le({helperPath:n.helperPath,expectedHelperVersion:n.version||ue,requiredCapabilities:st(e),guardUnsafeWindowsCommands:(e.platform??process.platform)==="win32",cleanupWindowsOverlays:(e.platform??process.platform)==="win32",requestLogger:nt(t)})}function nt(e){return t=>{w.appendFileSync(e,`${JSON.stringify(ot(t))}
5
+ `)}}function ot(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=M(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=M(e.result)),t}function M(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return rt(t,e)?{type:"string",length:e.length,sha256:R.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const s=e.slice(0,10).map(a=>M(a,t,n+1));return e.length>s.length?{type:"array",length:e.length,items:s}:s}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[s,a]of Object.entries(o))r[s]=M(a,s,n+1);return r}function rt(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function st(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?T("send"):e.steps.some(t=>t==="download-visible-media")?T("download"):T("observe")}function at(e){const t=String(e||"").trim().toLowerCase();if(t){if(t==="windows"||t==="win32")return"win32";if(t==="macos"||t==="darwin")return"darwin";throw new Error(`unsupported_wechat_smoke_platform:${e}`)}}function it(e,t){const n=i.join(t.outDir,"fixtures");w.mkdirSync(n,{recursive:!0});const o=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);if(e==="send-text")return{text:t.text||`codex-product-action text ${o}`};if(e==="send-file"){const r=t.filePath||i.join(n,`wechat-action-smoke-${o}.txt`);return t.filePath||w.writeFileSync(r,`Shennian WeChat product action smoke
6
6
  ${o}
7
- `,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||ut(r),{text:`codex-product-action image ${o}`,attachmentPath:r}}if(e==="send-video"){const r=t.videoPath||i.join(n,`wechat-action-smoke-${o}.mp4`);return!t.videoPath&&!mt(r)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${o}`,attachmentPath:r}}throw new Error(`step ${e} is not a send step`)}function Q(e){const t=e.flatMap(n=>rt(n.mediaMetadata));return{count:t.length,byAvailability:O(t.map(n=>Z(n,"availability")||"unknown")),localPaths:t.map(n=>Z(n,"localPath")).filter(n=>!!n)}}function rt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(X):X(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function st(e){const t=g(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function at(e){return e==="open-read"||e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="structure-window"||e==="server-observe"||e==="read-bottom-page"||e==="download-visible-media"||e==="send-and-confirm"||e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"||e==="human-send-normal"||e==="human-observe-deferred"?e:null}function it(e,t){const n=[];for(let o=0;o<e.length;o+=1)e[o]===t&&e[o+1]&&n.push(e[o+1]);return n}function g(e,t){const n=e.indexOf(t),o=n>=0?e[n+1]:void 0;return o&&!o.startsWith("--")?o:void 0}function F(e){return e?i.resolve(e):void 0}function dt(e){return Array.from(new Set(e))}function T(e,t,n){const o=n.mimeType.includes("jpeg")?".jpg":".png",r=i.join(e,`${t}${o}`);return w.writeFileSync(r,Buffer.from(n.dataBase64,"base64")),r}function f(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,`${JSON.stringify(t,null,2)}
8
- `,"utf8")}function A(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,t,"utf8")}function ct(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function lt(e,t=3){return(e?.recent??[]).slice(-t).map(n=>({stableMessageKey:n.stableMessageKey,senderRole:n.senderRole,kind:n.kind,anchorText:n.anchorText??n.normalizedText??n.textExcerpt??null,anchorMetadata:n.anchorMetadata??null}))}function ut(e){w.writeFileSync(e,Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAVElEQVR4nO3PMQ0AMAgAMUTof2kOKWzo8mCqkmeX9waYtwOwG4DbANwG4DYAtwG4DcBtAG4DcBuA2wDcBuA2ALcBuA3AbQBuA3AbgNsA3AbgNgC3AbgNwG0AbgNwG8BPDyCqAzmh3yHRAAAAAElFTkSuQmCC","base64"))}function mt(e){return ne("ffmpeg",["-y","-f","lavfi","-i","testsrc=size=160x120:rate=5","-t","1","-pix_fmt","yuv420p",e],{encoding:"utf8",stdio:"ignore",timeout:2e4}).status===0&&w.existsSync(e)}function O(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function Z(e,t){return typeof e[t]=="string"?e[t].trim():""}function X(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){return D.createHash("sha256").update(e).digest("hex").slice(0,16)}function Y(e){return`sha256:${D.createHash("sha256").update(ht(e)).digest("hex")}`}function ht(e){return JSON.stringify(L(e))}function L(e){if(Array.isArray(e))return e.map(L);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,L(t[n])]))}function ft(e){const t=e instanceof Error?e.message:String(e),n=t.split(":",1)[0]?.trim();return n&&/^[a-z0-9_/-]+$/i.test(n)?n.replace(/\//g,"_"):/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/wechat.*window|window.*unavailable/i.test(t)?"wechat_window_unavailable":/observe|VLM|server|api/i.test(t)?"server_observe_failed":"action_smoke_failed"}function gt(e){if(e==="wechat_duplicate_instance")return"\u8BF7\u5173\u95ED\u91CD\u590D\u767B\u5F55\u3001\u5DF2\u767B\u5F55\u63D0\u793A\u6216\u7B2C\u4E8C\u4E2A\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684 Windows \u5FAE\u4FE1\u4E3B\u7A97\u53E3\u540E\u91CD\u8BD5\u3002"}function R(e){return new Promise(t=>setTimeout(t,e))}function pt(e){process.stdout.write(`${JSON.stringify({ok:e.ok,conversation:e.conversation,outDir:e.outDir,durationMs:e.durationMs,steps:e.steps.map(t=>({step:t.step,ok:t.ok,skipped:t.skipped,reasonCode:t.reasonCode,userAction:t.userAction,details:t.details,errorSummary:t.errorSummary}))},null,2)}
9
- `)}async function wt(){const e=ve(process.argv.slice(2)),t=await Se(e);pt(t),(!t.ok||e.requireServer&&t.steps.some(n=>n.step==="server-observe"&&!n.ok))&&process.exit(1)}process.argv[1]&&i.resolve(process.argv[1])===oe(import.meta.url)&&wt().catch(e=>{process.stderr.write(`${e instanceof Error?e.stack||e.message:String(e)}
10
- `),process.exit(1)});export{M as buildBottomPageReport,q as buildDownloadVisibleMediaReport,We as buildSendAndConfirmEvidence,Pe as buildSendStepAcceptance,je as nextSmokeDrainWaitMs,ve as parseWeChatChannelActionSmokeArgs,pe as resolveWeChatSmokeObserveSessionMode,Se as runWeChatChannelActionSmoke,ye as stepsForMode};
7
+ `,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||gt(r),{text:`codex-product-action image ${o}`,attachmentPath:r}}if(e==="send-video"){const r=t.videoPath||i.join(n,`wechat-action-smoke-${o}.mp4`);return!t.videoPath&&!pt(r)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${o}`,attachmentPath:r}}throw new Error(`step ${e} is not a send step`)}function Y(e){const t=e.flatMap(n=>dt(n.mediaMetadata));return{count:t.length,byAvailability:N(t.map(n=>ee(n,"availability")||"unknown")),localPaths:t.map(n=>ee(n,"localPath")).filter(n=>!!n)}}function dt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(te):te(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function ct(e){const t=g(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function ut(e){return e==="open-read"||e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="structure-window"||e==="server-observe"||e==="read-bottom-page"||e==="download-visible-media"||e==="send-and-confirm"||e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"||e==="human-send-normal"||e==="human-observe-deferred"?e:null}function lt(e,t){const n=[];for(let o=0;o<e.length;o+=1)e[o]===t&&e[o+1]&&n.push(e[o+1]);return n}function g(e,t){const n=e.indexOf(t),o=n>=0?e[n+1]:void 0;return o&&!o.startsWith("--")?o:void 0}function F(e){return e?i.resolve(e):void 0}function mt(e){return Array.from(new Set(e))}function P(e,t,n){const o=n.mimeType.includes("jpeg")?".jpg":".png",r=i.join(e,`${t}${o}`);return w.writeFileSync(r,Buffer.from(n.dataBase64,"base64")),r}function h(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,`${JSON.stringify(t,null,2)}
8
+ `,"utf8")}function A(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,t,"utf8")}function ft(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function ht(e,t=3){return(e?.recent??[]).slice(-t).map(n=>({stableMessageKey:n.stableMessageKey,senderRole:n.senderRole,kind:n.kind,anchorText:n.anchorText??n.normalizedText??n.textExcerpt??null,anchorMetadata:n.anchorMetadata??null}))}function gt(e){w.writeFileSync(e,Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAVElEQVR4nO3PMQ0AMAgAMUTof2kOKWzo8mCqkmeX9waYtwOwG4DbANwG4DYAtwG4DcBtAG4DcBuA2wDcBuA2ALcBuA3AbQBuA3AbgNsA3AbgNgC3AbgNwG0AbgNwG8BPDyCqAzmh3yHRAAAAAElFTkSuQmCC","base64"))}function pt(e){return se("ffmpeg",["-y","-f","lavfi","-i","testsrc=size=160x120:rate=5","-t","1","-pix_fmt","yuv420p",e],{encoding:"utf8",stdio:"ignore",timeout:2e4}).status===0&&w.existsSync(e)}function N(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function ee(e,t){return typeof e[t]=="string"?e[t].trim():""}function te(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){return R.createHash("sha256").update(e).digest("hex").slice(0,16)}function ne(e){return`sha256:${R.createHash("sha256").update(wt(e)).digest("hex")}`}function wt(e){return JSON.stringify(O(e))}function O(e){if(Array.isArray(e))return e.map(O);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,O(t[n])]))}function bt(e){const t=e instanceof Error?e.message:String(e),n=t.split(":",1)[0]?.trim();return n&&/^[a-z0-9_/-]+$/i.test(n)?n.replace(/\//g,"_"):/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/wechat.*window|window.*unavailable/i.test(t)?"wechat_window_unavailable":/observe|VLM|server|api/i.test(t)?"server_observe_failed":"action_smoke_failed"}function vt(e){if(e==="wechat_duplicate_instance")return"\u8BF7\u5173\u95ED\u91CD\u590D\u767B\u5F55\u3001\u5DF2\u767B\u5F55\u63D0\u793A\u6216\u7B2C\u4E8C\u4E2A\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684 Windows \u5FAE\u4FE1\u4E3B\u7A97\u53E3\u540E\u91CD\u8BD5\u3002"}function L(e){return new Promise(t=>setTimeout(t,e))}function yt(e){process.stdout.write(`${JSON.stringify({ok:e.ok,conversation:e.conversation,outDir:e.outDir,durationMs:e.durationMs,steps:e.steps.map(t=>({step:t.step,ok:t.ok,skipped:t.skipped,reasonCode:t.reasonCode,userAction:t.userAction,details:t.details,errorSummary:t.errorSummary}))},null,2)}
9
+ `)}async function Ct(){const e=Se(process.argv.slice(2)),t=await xe(e);yt(t),(!t.ok||e.requireServer&&t.steps.some(n=>n.step==="server-observe"&&!n.ok))&&process.exit(1)}process.argv[1]&&i.resolve(process.argv[1])===ae(import.meta.url)&&Ct().catch(e=>{process.stderr.write(`${e instanceof Error?e.stack||e.message:String(e)}
10
+ `),process.exit(1)});export{D as buildBottomPageReport,q as buildDownloadVisibleMediaReport,Be as buildSendAndConfirmEvidence,We as buildSendStepAcceptance,Qe as countMarkerOccurrencesInOcrBlocks,$e as nextSmokeDrainWaitMs,Se as parseWeChatChannelActionSmokeArgs,ve as resolveWeChatSmokeObserveSessionMode,xe as runWeChatChannelActionSmoke,ke as stepsForMode};
@@ -1,24 +1,24 @@
1
- import F from"node:http";import{randomBytes as q,randomUUID as g}from"node:crypto";import k from"node:fs";import T from"node:os";import f from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as U,extractPayloadText as L,formatExternalConversationText as O,formatExternalMessageLine as K,formatExternalAttachmentReference as H,isAgentHiddenPayload as j,isToolPayload as G}from"@shennian/wire";import{ManagerRegistry as z}from"./registry.js";import{readMessages as J}from"../session/store.js";import{ChannelRuntime as Y}from"../channels/runtime.js";import{splitExternalReplyText as V}from"../channels/reply-split.js";import{resolveShennianPath as Q}from"../config/index.js";import{buildExternalChannelInstructions as X}from"../agents/external-channel-instructions.js";const A=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024);let x=null;function Te(r){x=r}function xe(){return x}function S(r){return f.resolve(r||T.homedir())}function u(r,e,a){r.writeHead(e,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(a))}function Z(r){return/^agent-(.+)-\d+$/.exec(r)?.[1]??null}function ee(r){return r==="manager"?!1:r.startsWith("custom:")?!0:U.includes(r)}function te(r){const e=/^agent-.+-(\d+)$/.exec(r);if(!e)return null;const a=Number(e[1]);return Number.isInteger(a)&&a>=0?a:null}function N(r){return r.replace(/\r\n/g,`
2
- `).trim()}function ne(r){try{const e=JSON.parse(r),a=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",n=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=n.length>220?`${n.slice(0,220)}...`:n;return s?`[${a}] ${t}: ${s}`:`[${a}] ${t}`}catch{return"[tool]"}}function re(r){if(!r||typeof r!="object")return;const e=r,a=String(e.kind||""),t=String(e.name||""),n=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),l=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(a!=="image"&&a!=="video"&&a!=="file")&&!(!t||!n||!Number.isFinite(l)||l<0)&&!(!o&&!i))return{kind:a,name:t,mimeType:n,size:l,...o?{localPath:o}:{},...i?{url:i}:{}}}function ae(r,e){const a=[...r].sort((c,d)=>c.ts-d.ts),t=[];let n=null,s=null,o=null,i="";const l=()=>{if(!n)return;const c=i.trim();c&&t.push({...n,id:`${n.id}-compact`,payload:c}),n=null,s=null,o=null,i=""};for(const c of a){if(j(c.payload)){l();continue}if(c.role==="user"){l(),t.push(c);continue}if(G(c.payload)){l(),t.push({...c,payload:ne(c.payload)});continue}const d=L(c.payload);if(!d.trim())continue;const h=Z(c.id),p=te(c.id);n&&n.role===c.role&&s===h&&h&&p!==null&&o!==null&&p===o+1?(i+=d,n.ts=c.ts,o=p):(l(),n=c,s=h,o=p,i=d)}return l(),t.slice(-e).sort((c,d)=>d.ts-c.ts)}async function se(r){const e=[];let a=0;for await(const n of r){const s=Buffer.from(n);if(a+=s.byteLength,Number.isFinite(A)&&A>0&&a>A)throw new Error(`Manager IPC request body is too large. Max: ${A} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function y(r,e,a,t){r.client.sendRes({type:"res",id:e,ok:a,...a?{payload:t}:{error:String(t.error||"unknown error")}})}function D(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function P(){return ie()?f.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):Q("runtime","manager-ipc.json")}function ie(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Ne{opts;registry=new z;channelRuntime=new Y((e,a)=>{this.handleExternalMessage(e,a)},e=>this.registry.createReplyTarget(e).replyTarget);server=null;ipcUrl=null;ipcToken=q(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=F.createServer((a,t)=>{this.handleIpc(a,t)}),await new Promise((a,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>a())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.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=P();k.mkdirSync(f.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=P(),a=JSON.parse(k.readFileSync(e,"utf-8"));(a.url===this.ipcUrl||a.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,a,t,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:a??"",SHENNIAN_MANAGER_WORKDIR:S(t),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:S(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,a,t){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:a??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const a=this.registry.getManager(e);return{agentType:a?.defaultWorkerAgentType??null,modelId:a?.defaultWorkerModelId??null}}noteManagerAgentSession(e,a,t,n){this.registry.upsertManager({sessionId:e,agentSessionId:a,workDir:S(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n})}noteAgentEvent(e,a){if(!this.findWorker(e))return;const n={lastActivityAt:new Date().toISOString(),runId:a.runId};a.agentSessionId&&(n.agentSessionId=a.agentSessionId);const s=`${e}:${a.runId}`;if(a.state==="delta"&&a.text&&!a.thinking){const i=(this.workerTextAcc.get(s)??"")+a.text;this.workerTextAcc.set(s,i);const l=N(i);l&&(n.summary=l.length>160?`${l.slice(0,160)}...`:l)}if(a.state==="final"||a.state==="error"||a.state==="aborted"){n.status=a.state;const i=N(this.workerTextAcc.get(s)??"");i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else a.state==="start"&&(n.status="running");const o=this.registry.updateWorker(e,n);o&&(a.state==="final"||a.state==="error"||a.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,a.state,a.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const a=this.opts.getRuntime(),t=e.params??{};try{const n=String(t.managerSessionId||t.sessionId||"");if(!n)throw new Error("sessionId is required");const s=this.registry.getManager(n),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",l=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",c=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){y(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=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),y(a,e.id,!0,{channel:d});return}if(o){y(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(n,"wechat-rpa",{includeSecret:!0})});return}if(i){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${n}`),managerSessionId:n,sessionId:n,workDir:S(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:W(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:$(t.source),pollIntervalMs:I(t.pollIntervalMs),recentLimit:I(t.recentLimit),idleSeconds:I(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,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(n),y(a,e.id,!0,{channel:d});return}if(l){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(n);this.broadcastManagerChannelStatus(n),y(a,e.id,!0,d);return}if(c){const d=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),y(a,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(n){y(a,e.id,!1,{error:n instanceof Error?n.message:String(n)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const a=this.opts.getRuntime();if(!a.client?.sendEvent)return;const t=this.registry.getManager(e),n=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:n}}})}async handleIpc(e,a){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(a,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),n=await 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(a,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(n.agentType||n.agent||o.defaultWorkerAgentType||"codex");if(!ee(i))throw new Error(`Unsupported manager worker agent: ${i}`);const l=S(String(n.workDir||o.workDir));if(l!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const c=String(n.message||"");if(!c)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:l,summary:c.slice(0,120)}),h=String(n.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,i,l,c,null,h),u(a,200,{ok:!0,session:d});return}if(t.pathname==="/sessions/send"){const i=String(n.sessionId||""),l=String(n.message||""),c=n.enqueue===void 0?!0:!!n.enqueue,d=this.registry.getWorkerForManager(s,i);if(!d)throw new Error("Worker not found in this manager scope");const h=String(n.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));c?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h),u(a,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const c=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(a,200,{ok:!0,queue:c});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-${g()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||""),text:String(n.message||n.text||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${g()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(n.queueMessageId||n.messageId||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(n.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${g()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(a,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(n.sessionId||""),l=Number(n.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=J(i,{limit:Math.max(l*20,l)});u(a,200,{ok:!0,messages:ae(d,l),rawMessageCount:d.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(a,200,{ok:!0,path:f.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),l=String(n.text||""),c=re(n.attachment),d=String(n.idempotencyKey||g()),h=String(n.channelId||""),p=String(n.conversationId||""),R=!i&&(!h||!p)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,M=i?.channelId||h||R?.channelId||"",C=i?.conversationId||p||R?.conversationId||"";if(M&&this.channelRuntime.getChannelById(M)){if(!C)throw new Error("No external channel target is available for this Manager");const m=await this.channelRuntime.reply({managerSessionId:s,channelId:M,conversationId:C,messageId:i?.messageId??void 0,text:l,attachment:c,idempotencyKey:d});u(a,m.ok?200:400,m);return}let w;try{w=await this.sendManagedWeComReply({managerSessionId:s,text:l,attachment:c,idempotencyKey:d})}catch(m){const E=m instanceof Error?m.message:String(m);if(!D(E))throw m;w={ok:!1,error:E}}if(w.ok){u(a,200,{ok:!0,payload:w.payload});return}if(!D(w.error||"")||!M||!C){u(a,400,{ok:!1,error:w.error||"External send failed"});return}u(a,400,{ok:!1,error:`No local external channel is configured for ${M}`});return}if(t.pathname==="/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof n.name=="string"?n.name:void 0,enabled:!!n.enabled,wsUrl:typeof n.wsUrl=="string"?n.wsUrl:void 0,token:typeof n.token=="string"?n.token:void 0,canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=S(String(n.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const l=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:W(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:$(n.source),pollIntervalMs:I(n.pollIntervalMs),recentLimit:I(n.recentLimit),idleSeconds:I(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,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:l});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:l}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,channel:i,messages:l});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:null,replyId:typeof n.replyId=="string"?n.replyId:null,reason:typeof n.reason=="string"?n.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(a,200,{ok:!0,...i});return}u(a,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(a,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,a,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${g()}`,method:"chat.send",params:{sessionId:e,text:n,agentType:a,workDir:t,agentSessionId:s,modelId:o}})}async dispatchChatEnqueue(e,a,t,n,s,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${g()}`,method:"chat.enqueue",params:{sessionId:e,text:n,agentType:a,workDir:t,agentSessionId:s,modelId:o}})}async sendManagedWeComReply(e){const a=V(e.text);if(!a.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const n=[];for(const[s,o]of a.entries()){const i=await t.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:a.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};n.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:a.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};n.push(s.payload)}return{ok:!0,payload:n.length===1?n[0]:n}}wakeManagerForWorker(e,a,t,n){const s=this.registry.getManager(e);if(!s)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
1
+ import L from"node:http";import{randomBytes as O,randomUUID as g}from"node:crypto";import k from"node:fs";import T from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as U,extractPayloadText as K,formatExternalConversationText as H,formatExternalMessageLine as j,formatExternalAttachmentReference as G,isAgentHiddenPayload as z,isToolPayload as J}from"@shennian/wire";import{ManagerRegistry as Y}from"./registry.js";import{readMessages as V}from"../session/store.js";import{ChannelRuntime as Q}from"../channels/runtime.js";import{splitExternalReplyText as X}from"../channels/reply-split.js";import{resolveShennianPath as Z}from"../config/index.js";import{buildExternalChannelInstructions as ee}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as te,sendDirectWeChatMessageOnce as ne}from"../commands/wechat.js";const A=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024);let x=null;function We(r){x=r}function Pe(){return x}function S(r){return y.resolve(r||T.homedir())}function u(r,e,a){r.writeHead(e,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(a))}function re(r){return/^agent-(.+)-\d+$/.exec(r)?.[1]??null}function ae(r){return r==="manager"?!1:r.startsWith("custom:")?!0:U.includes(r)}function se(r){const e=/^agent-.+-(\d+)$/.exec(r);if(!e)return null;const a=Number(e[1]);return Number.isInteger(a)&&a>=0?a:null}function N(r){return r.replace(/\r\n/g,`
2
+ `).trim()}function ie(r){try{const e=JSON.parse(r),a=e.type==="tool_result"||e.result?"tool_result":"tool_call",n=e.name||"tool",t=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",i=t.length>220?`${t.slice(0,220)}...`:t;return i?`[${a}] ${n}: ${i}`:`[${a}] ${n}`}catch{return"[tool]"}}function D(r){if(!r||typeof r!="object")return;const e=r,a=String(e.kind||""),n=String(e.name||""),t=String(e.mimeType||""),i=String(e.dataBase64||""),o=String(e.localPath||""),s=String(e.url||""),l=Number(e.size||0);if(i)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(a!=="image"&&a!=="video"&&a!=="file")&&!(!n||!t||!Number.isFinite(l)||l<0)&&!(!o&&!s))return{kind:a,name:n,mimeType:t,size:l,...o?{localPath:o}:{},...s?{url:s}:{}}}function W(r){const e=r.binding&&typeof r.binding=="object"&&!Array.isArray(r.binding)?r.binding:{},a=String(e.sessionId||r.managerSessionId||"").trim(),n=String(e.channelId||"").trim(),t=String(e.conversationId||"").trim(),i=String(e.conversationName||r.conversation||"").trim(),o=String(e.workDir||r.workDir||"").trim();if(!a)throw new Error("WeChat tool sessionId is required");if(!n)throw new Error("WeChat tool channelId is required");if(!t)throw new Error("WeChat tool conversationId is required");if(!i)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:a,channelId:n,conversationId:t,conversationName:i,workDir:o}}function oe(r,e){const a=[...r].sort((c,d)=>c.ts-d.ts),n=[];let t=null,i=null,o=null,s="";const l=()=>{if(!t)return;const c=s.trim();c&&n.push({...t,id:`${t.id}-compact`,payload:c}),t=null,i=null,o=null,s=""};for(const c of a){if(z(c.payload)){l();continue}if(c.role==="user"){l(),n.push(c);continue}if(J(c.payload)){l(),n.push({...c,payload:ie(c.payload)});continue}const d=K(c.payload);if(!d.trim())continue;const h=re(c.id),p=se(c.id);t&&t.role===c.role&&i===h&&h&&p!==null&&o!==null&&p===o+1?(s+=d,t.ts=c.ts,o=p):(l(),t=c,i=h,o=p,s=d)}return l(),n.slice(-e).sort((c,d)=>d.ts-c.ts)}async function le(r){const e=[];let a=0;for await(const t of r){const i=Buffer.from(t);if(a+=i.byteLength,Number.isFinite(A)&&A>0&&a>A)throw new Error(`Manager IPC request body is too large. Max: ${A} bytes.`);e.push(i)}const n=Buffer.concat(e).toString("utf-8");return n?JSON.parse(n):{}}function w(r,e,a,n){r.client.sendRes({type:"res",id:e,ok:a,...a?{payload:n}:{error:String(n.error||"unknown error")}})}function P(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function v(){return ce()?y.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):Z("runtime","manager-ipc.json")}function ce(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ve{opts;registry=new Y;channelRuntime=new Q((e,a)=>{this.handleExternalMessage(e,a)},e=>this.registry.createReplyTarget(e).replyTarget);server=null;ipcUrl=null;ipcToken=O(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=L.createServer((a,n)=>{this.handleIpc(a,n)}),await new Promise((a,n)=>{this.server.once("error",n),this.server.listen(0,"127.0.0.1",()=>a())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.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=v();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=v(),a=JSON.parse(k.readFileSync(e,"utf-8"));(a.url===this.ipcUrl||a.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,a,n,t){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:a??"",SHENNIAN_MANAGER_WORKDIR:S(n),SHENNIAN_MANAGER_MODEL:t,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:S(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,a,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,defaultWorkerAgentType:a??null,defaultWorkerModelId:n??null})}getManagerWorkerDefaults(e){const a=this.registry.getManager(e);return{agentType:a?.defaultWorkerAgentType??null,modelId:a?.defaultWorkerModelId??null}}noteManagerAgentSession(e,a,n,t){this.registry.upsertManager({sessionId:e,agentSessionId:a,workDir:S(n),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:t})}noteAgentEvent(e,a){if(!this.findWorker(e))return;const t={lastActivityAt:new Date().toISOString(),runId:a.runId};a.agentSessionId&&(t.agentSessionId=a.agentSessionId);const i=`${e}:${a.runId}`;if(a.state==="delta"&&a.text&&!a.thinking){const s=(this.workerTextAcc.get(i)??"")+a.text;this.workerTextAcc.set(i,s);const l=N(s);l&&(t.summary=l.length>160?`${l.slice(0,160)}...`:l)}if(a.state==="final"||a.state==="error"||a.state==="aborted"){t.status=a.state;const s=N(this.workerTextAcc.get(i)??"");s&&(t.summary=s.length>240?`${s.slice(0,240)}...`:s),this.workerTextAcc.delete(i)}else a.state==="start"&&(t.status="running");const o=this.registry.updateWorker(e,t);o&&(a.state==="final"||a.state==="error"||a.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,a.state,a.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const a=this.opts.getRuntime(),n=e.params??{};try{const t=String(n.managerSessionId||n.sessionId||"");if(!t)throw new Error("sessionId is required");const i=this.registry.getManager(t),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",s=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",l=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",c=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${t}`),managerSessionId:t,sessionId:t,workDir:String(n.workDir||i?.workDir||""),type:"websocket",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,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.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"wechat-rpa",{includeSecret:!0})});return}if(s){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(n.id||`wechat-rpa:${t}`),managerSessionId:t,sessionId:t,workDir:S(String(n.workDir||i?.workDir||"")),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:$(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:_(n.source),pollIntervalMs:m(n.pollIntervalMs),recentLimit:m(n.recentLimit),idleSeconds:m(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(l){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(t);this.broadcastManagerChannelStatus(t),w(a,e.id,!0,d);return}if(c){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:t,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(t),w(a,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(t){w(a,e.id,!1,{error:t instanceof Error?t.message:String(t)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const a=this.opts.getRuntime();if(!a.client?.sendEvent)return;const n=this.registry.getManager(e),t=this.channelRuntime.getManagerChannelStatus(e),i=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n?"manager":i?.agentType,agentSessionId:n?.agentSessionId??i?.agentSessionId??null,modelId:n?.modelId??i?.modelId??null,workDir:n?.workDir??i?.workDir,externalChannel:t}}})}async handleIpc(e,a){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(a,401,{ok:!1,error:"Unauthorized"});return}try{const n=new URL(e.url??"/","http://127.0.0.1"),t=await le(e),i=String(t.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!i)throw new Error("managerSessionId is required");const o=this.registry.getManager(i);if(n.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const s=new Set(this.opts.getRuntime().sessions.keys());u(a,200,{ok:!0,sessions:this.registry.listWorkers(i,{runningSessionIds:s})});return}if(n.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const s=String(t.agentType||t.agent||o.defaultWorkerAgentType||"codex");if(!ae(s))throw new Error(`Unsupported manager worker agent: ${s}`);const l=S(String(t.workDir||o.workDir));if(l!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const c=String(t.message||"");if(!c)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:i,agentType:s,workDir:l,summary:c.slice(0,120)}),h=String(t.modelId||(s===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,s,l,c,null,h),u(a,200,{ok:!0,session:d});return}if(n.pathname==="/sessions/send"){const s=String(t.sessionId||""),l=String(t.message||""),c=t.enqueue===void 0?!0:!!t.enqueue,d=this.registry.getWorkerForManager(i,s);if(!d)throw new Error("Worker not found in this manager scope");const h=String(t.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));c?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/queue"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const c=this.opts.getRuntime().chatQueue?.getSnapshot(s);u(a,200,{ok:!0,queue:c});return}if(n.pathname==="/sessions/queue/edit"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${g()}`,method:"chat.queue.edit",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||""),text:String(t.message||t.text||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/queue/delete"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${g()}`,method:"chat.queue.delete",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/stop"||n.pathname==="/sessions/terminate"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${g()}`,method:"chat.abort",params:{sessionId:s}}),this.registry.updateWorker(s,{status:"aborted"}),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/read"){const s=String(t.sessionId||""),l=Number(t.limit||200);if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const d=V(s,{limit:Math.max(l*20,l)});u(a,200,{ok:!0,messages:oe(d,l),rawMessageCount:d.length});return}if(n.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(a,200,{ok:!0,path:y.join(o.workDir,".shennian")});return}if(n.pathname==="/external/reply"){const s=typeof t.replyTarget=="string"?this.registry.getReplyTarget(t.replyTarget):this.registry.getLatestReplyTargetForManager(i),l=String(t.text||""),c=D(t.attachment),d=String(t.idempotencyKey||g()),h=String(t.channelId||""),p=String(t.conversationId||""),R=!s&&(!h||!p)?await this.channelRuntime.getDefaultReplyTarget(i).catch(()=>null):null,M=s?.channelId||h||R?.channelId||"",C=s?.conversationId||p||R?.conversationId||"";if(M&&this.channelRuntime.getChannelById(M)){if(!C)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:i,channelId:M,conversationId:C,messageId:s?.messageId??void 0,text:l,attachment:c,idempotencyKey:d});u(a,f.ok?200:400,f);return}let I;try{I=await this.sendManagedWeComReply({managerSessionId:i,text:l,attachment:c,idempotencyKey:d})}catch(f){const E=f instanceof Error?f.message:String(f);if(!P(E))throw f;I={ok:!1,error:E}}if(I.ok){u(a,200,{ok:!0,payload:I.payload});return}if(!P(I.error||"")||!M||!C){u(a,400,{ok:!1,error:I.error||"External send failed"});return}u(a,400,{ok:!1,error:`No local external channel is configured for ${M}`});return}if(n.pathname==="/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(i,"websocket")});return}if(n.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const s=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${i}`),managerSessionId:i,workDir:o.workDir,type:"websocket",name:typeof t.name=="string"?t.name:void 0,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.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:s});return}if(n.pathname==="/wechat-rpa/tool/read"){const s=W(t),l=m(t.limit)??10,c=await te(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,limit:l,recentLimit:l,download:t.download==="never"?"never":"auto",traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,messages:c.messages,outDir:c.outDir,helperTracePath:c.helperTracePath});return}if(n.pathname==="/wechat-rpa/tool/send"){const s=W(t),l=String(t.text||""),c=D(t.attachment),d=await ne(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,text:l,attachment:c,traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,...d});return}if(n.pathname==="/wechat-rpa/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(i,"wechat-rpa",{includeSecret:!0})});return}if(n.pathname==="/wechat-rpa/channel/upsert"){const s=S(String(t.workDir||o?.workDir||""));if(!s)throw new Error("workDir is required");const l=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${i}`),managerSessionId:i,sessionId:i,workDir:s,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:$(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:_(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:l});return}if(n.pathname==="/wechat-rpa/channel/sync"){const{channel:s,messages:l}=await this.channelRuntime.syncManagerWeChatRpaChannel(i);this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:s,messages:l});return}if(n.pathname==="/wechat-rpa/outbound/cancel"){const s=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:i,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(i),u(a,200,{ok:!0,...s});return}u(a,404,{ok:!1,error:`Unknown manager IPC path: ${n.pathname}`})}catch(n){u(a,400,{ok:!1,error:n instanceof Error?n.message:String(n)})}}async dispatchChatSend(e,a,n,t,i,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${g()}`,method:"chat.send",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async dispatchChatEnqueue(e,a,n,t,i,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${g()}`,method:"chat.enqueue",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async sendManagedWeComReply(e){const a=X(e.text);if(!a.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const n=this.opts.getRuntime().client;if(!n||typeof n.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const t=[];for(const[i,o]of a.entries()){const s=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:a.length>1?`${e.idempotencyKey}:${i+1}`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};t.push(s.payload)}if(e.attachment){const i=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:a.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};t.push(i.payload)}return{ok:!0,payload:t.length===1?t[0]:t}}wakeManagerForWorker(e,a,n,t){const i=this.registry.getManager(e);if(!i)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
3
3
 
4
4
  Worker: ${a.sessionId}
5
- \u72B6\u6001\uFF1A${t}
5
+ \u72B6\u6001\uFF1A${n}
6
6
  \u6700\u7EC8\u7ED3\u679C\uFF1A
7
- ${n||a.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
7
+ ${t||a.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,a){const t=this.channelRuntime.getChannelById(a.channelId)??this.channelRuntime.getManagerChannel(e,a.channelType),n=this.channelRuntime.getChannelStatusById(a.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),l=t?.agentSessionId??s?.agentSessionId??null,c=t?.modelId||s?.modelId||"",d=oe(a.attachments,a.channelType),h=de(a);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:l,modelId:c,text:h,attachments:d,externalChannel:he(n),replyTarget:a.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${g()}`,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(),a=this.registry.load();for(const t of Object.values(a.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=a.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),l=`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(i,o,n==="final"?"worker.final":`worker.${n}`)}handleExternalMessage(e,a){const n=this.channelRuntime.getChannelById(a.channelId)??this.channelRuntime.getManagerChannel(e,a.channelType),t=this.channelRuntime.getChannelStatusById(a.channelId)??this.channelRuntime.getManagerChannelStatus(e),i=this.registry.getManager(e),o=n?.agentType||(i?"manager":"codex"),s=n?.workDir||i?.workDir||process.cwd(),l=n?.agentSessionId??i?.agentSessionId??null,c=n?.modelId||i?.modelId||"",d=de(a.attachments,a.channelType),h=he(a);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:s,agentSessionId:l,modelId:c,text:h,attachments:d,externalChannel:me(t),replyTarget:a.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${g()}`,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(),a=this.registry.load();for(const n of Object.values(a.workers)){if(n.status!=="running")continue;const t=e-Date.parse(n.createdAt);if(t<10*6e4)continue;const i=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(i&&e-i<10*6e4)continue;const o=a.managers[n.managedBy];if(!o)continue;const s=Math.max(0,Math.floor((e-Date.parse(n.lastActivityAt))/6e4)),l=`Worker ${n.sessionId} \u5DF2\u8FD0\u884C ${Math.floor(t/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
10
10
 
11
11
  \u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
12
- - \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
13
- - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${t.lastActivityAt}
14
- - \u6700\u8FD1 ${i} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002
12
+ - \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
13
+ - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
14
+ - \u6700\u8FD1 ${s} \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:l}),o.status==="idle"&&this.dispatchChatSend(o.sessionId,"manager",o.workDir,l,o.agentSessionId,o.modelId)}}async interruptAndResumeManager(e,a,t){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&(this.registry.upsertManager({...e,status:"interrupting"}),await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${g()}`,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(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:l}),o.status==="idle"&&this.dispatchChatSend(o.sessionId,"manager",o.workDir,l,o.agentSessionId,o.modelId)}}async interruptAndResumeManager(e,a,n){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&(this.registry.upsertManager({...e,status:"interrupting"}),await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${g()}`,method:"chat.abort",params:{sessionId:e.sessionId}}));const o=n?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${n}
17
17
 
18
- ${a}`:a;await this.dispatchChatSend(e.sessionId,"manager",e.workDir,o,e.agentSessionId,e.modelId)}bindManagerAdapterEvents(e,a){a.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,a){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:a})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(a=>X(a,void 0,e,"manager")).join(`
18
+ ${a}`:a;await this.dispatchChatSend(e.sessionId,"manager",e.workDir,o,e.agentSessionId,e.modelId)}bindManagerAdapterEvents(e,a){a.on("agentEvent",n=>{if(n.state==="start"&&n.agentSessionId){const t=this.registry.getManager(e);t&&this.noteManagerAgentSession(e,n.agentSessionId,t.workDir,t.modelId)}n.state==="start"&&this.updateManagerStatus(e,"running"),(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.updateManagerStatus(e,"idle")})}updateManagerStatus(e,a){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,status:a})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(a=>ee(a,void 0,e,"manager")).join(`
19
19
 
20
- `).trim()}}function W(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function $(r){return r==="wechat-channel"||r==="macos-flow"||r==="macos-probe"||r==="windows-visual-flow"||r==="wechat-rpa-lab"||r==="fixture-jsonl"?r:void 0}function oe(r,e){const a=r.map(t=>v(t,e)).filter(t=>t!=null);return a.length?a:void 0}function le(r,e,a,t){const n=e||"",s=new Set;return r.map((i,l)=>{const c=v(i,t),d=H({type:i.type,name:c?.name||i.name,path:c?.path,mimeType:c?.mimeType||i.mimeType,availability:i.availability,providerError:i.providerError||ue(i)},l),h=c?.path||"";if(h){const R=`${d}
21
- ${h}`;return s.has(R)||n.includes(h)?"":(s.add(R),`${a}: ${d}`)}const p=d;return s.has(p)?"":(s.add(p),`${a}: ${d}`)}).filter(i=>!n.includes(i)).join(`
22
- `)}function de(r){const a=(r.channelType==="wechat-rpa"?ce(r.sender.name):r.sender.name||r.sender.id)||(r.channelType==="wechat-rpa"?"\u5BF9\u65B9":r.sender.id)||"\u5BF9\u65B9",t=le(r.attachments,r.text,a,r.channelType),s=[r.text?K({senderName:a,senderId:r.sender.id,text:r.text}):t?"":`${a}:`,t].filter(Boolean).join(`
23
- `);return[O({conversationName:r.conversationName||r.conversationId,messages:[]}),s].filter(Boolean).join(`
24
- `)}function ce(r){const e=r?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function ue(r){return!r.localPath||r.availability&&r.availability!=="edge-local"||b(r.localPath)?"":"edge-local-unavailable"}function v(r,e){return r.localPath&&pe(r,e)&&b(r.localPath)?{path:r.localPath,name:r.name||f.basename(r.localPath)||"attachment",mimeType:r.mimeType||B(r)}:r.url&&ge(r)?{path:r.url,name:r.name||r.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:r.mimeType||B(r)}:r.thumbnailPath&&!_(r,e)&&b(r.thumbnailPath)?{path:r.thumbnailPath,name:r.name?`${r.name}-preview.png`:f.basename(r.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function he(r){return r?r.type!=="wechat-rpa"?r:{configured:r.configured,connected:r.connected,type:r.type,channelId:r.channelId,name:r.name,canReply:r.canReply,systemPrompt:r.systemPrompt,wechatRpaSource:r.wechatRpaSource,wechatRpaGroups:r.wechatRpaGroups,pollIntervalMs:r.pollIntervalMs,recentLimit:r.recentLimit,idleSeconds:r.idleSeconds,forceForeground:r.forceForeground,noRestore:r.noRestore,downloadAttachments:r.downloadAttachments,selfNickname:r.selfNickname,wechatRpaPrivacyConsentAccepted:r.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:r.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:r.wechatRpaPreflightChecks,wechatRpaRuntimeState:r.wechatRpaRuntimeState,wechatRpaLastMessageAt:r.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:r.wechatRpaPendingReplyCount,wechatRpaLastError:r.wechatRpaLastError}:null}function pe(r,e){return r.providerError||r.availability&&r.availability!=="edge-local"?!1:_(r,e)?r.materializationKind==="original-file"&&r.isOriginal===!0&&r.mimeKindMatches===!0&&!me(r.localPath):!0}function ge(r){return!r.providerError&&(!r.availability||r.availability==="server-url")}function _(r,e){if(e!=="wechat-rpa")return!1;const a=String(r.type||"").toLowerCase();return a==="file"||a==="video"||a==="video-file"}function me(r){const e=String(r||"").replace(/\\/g,"/"),a=f.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(a)}function b(r){try{return k.statSync(r).isFile()}catch{return!1}}function B(r){return r.type==="image"?"image/*":r.type==="video"?"video/*":r.type==="audio"?"audio/*":"application/octet-stream"}function I(r){const e=Number(r);return Number.isFinite(e)?e:void 0}export{Ne as ManagerRuntimeService,xe as getManagerRuntimeService,Te as setManagerRuntimeService};
20
+ `).trim()}}function $(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function _(r){return r==="wechat-channel"||r==="macos-flow"||r==="macos-probe"||r==="windows-visual-flow"||r==="wechat-rpa-lab"||r==="fixture-jsonl"?r:void 0}function de(r,e){const a=r.map(n=>B(n,e)).filter(n=>n!=null);return a.length?a:void 0}function ue(r,e,a,n){const t=e||"",i=new Set;return r.map((s,l)=>{const c=B(s,n),d=G({type:s.type,name:c?.name||s.name,path:c?.path,mimeType:c?.mimeType||s.mimeType,availability:s.availability,providerError:s.providerError||ge(s)},l),h=c?.path||"";if(h){const R=`${d}
21
+ ${h}`;return i.has(R)||t.includes(h)?"":(i.add(R),`${a}: ${d}`)}const p=d;return i.has(p)?"":(i.add(p),`${a}: ${d}`)}).filter(s=>!t.includes(s)).join(`
22
+ `)}function he(r){const a=(r.channelType==="wechat-rpa"?pe(r.sender.name):r.sender.name||r.sender.id)||(r.channelType==="wechat-rpa"?"\u5BF9\u65B9":r.sender.id)||"\u5BF9\u65B9",n=ue(r.attachments,r.text,a,r.channelType),i=[r.text?j({senderName:a,senderId:r.sender.id,text:r.text}):n?"":`${a}:`,n].filter(Boolean).join(`
23
+ `);return[H({conversationName:r.conversationName||r.conversationId,messages:[]}),i].filter(Boolean).join(`
24
+ `)}function pe(r){const e=r?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function ge(r){return!r.localPath||r.availability&&r.availability!=="edge-local"||b(r.localPath)?"":"edge-local-unavailable"}function B(r,e){return r.localPath&&fe(r,e)&&b(r.localPath)?{path:r.localPath,name:r.name||y.basename(r.localPath)||"attachment",mimeType:r.mimeType||F(r)}:r.url&&ye(r)?{path:r.url,name:r.name||r.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:r.mimeType||F(r)}:r.thumbnailPath&&!q(r,e)&&b(r.thumbnailPath)?{path:r.thumbnailPath,name:r.name?`${r.name}-preview.png`:y.basename(r.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function me(r){return r?r.type!=="wechat-rpa"?r:{configured:r.configured,connected:r.connected,type:r.type,channelId:r.channelId,name:r.name,canReply:r.canReply,systemPrompt:r.systemPrompt,wechatRpaSource:r.wechatRpaSource,wechatRpaGroups:r.wechatRpaGroups,pollIntervalMs:r.pollIntervalMs,recentLimit:r.recentLimit,idleSeconds:r.idleSeconds,forceForeground:r.forceForeground,noRestore:r.noRestore,downloadAttachments:r.downloadAttachments,selfNickname:r.selfNickname,wechatRpaPrivacyConsentAccepted:r.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:r.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:r.wechatRpaPreflightChecks,wechatRpaRuntimeState:r.wechatRpaRuntimeState,wechatRpaLastMessageAt:r.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:r.wechatRpaPendingReplyCount,wechatRpaLastError:r.wechatRpaLastError}:null}function fe(r,e){return r.providerError||r.availability&&r.availability!=="edge-local"?!1:q(r,e)?r.materializationKind==="original-file"&&r.isOriginal===!0&&r.mimeKindMatches===!0&&!we(r.localPath):!0}function ye(r){return!r.providerError&&(!r.availability||r.availability==="server-url")}function q(r,e){if(e!=="wechat-rpa")return!1;const a=String(r.type||"").toLowerCase();return a==="file"||a==="video"||a==="video-file"}function we(r){const e=String(r||"").replace(/\\/g,"/"),a=y.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(a)}function b(r){try{return k.statSync(r).isFile()}catch{return!1}}function F(r){return r.type==="image"?"image/*":r.type==="video"?"video/*":r.type==="audio"?"audio/*":"application/octet-stream"}function m(r){const e=Number(r);return Number.isFinite(e)?e:void 0}export{ve as ManagerRuntimeService,Pe as getManagerRuntimeService,We as setManagerRuntimeService};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.98",
3
+ "version": "0.2.100",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {