shennian 0.2.108 → 0.2.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/publish-build-manifest.json +63 -58
  2. package/dist/src/agents/model-registry/discovery.js +2 -2
  3. package/dist/src/agents/pi-context.d.ts +1 -1
  4. package/dist/src/agents/pi-context.js +3 -3
  5. package/dist/src/channels/runtime.d.ts +2 -0
  6. package/dist/src/channels/runtime.js +1 -1
  7. package/dist/src/channels/secret-registry.d.ts +2 -0
  8. package/dist/src/channels/wechat-channel/automation-lane.d.ts +12 -0
  9. package/dist/src/channels/wechat-channel/automation-lane.js +2 -1
  10. package/dist/src/channels/wechat-channel/client.d.ts +25 -2
  11. package/dist/src/channels/wechat-channel/client.js +1 -1
  12. package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -1
  13. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  14. package/dist/src/channels/wechat-channel/helper-client.d.ts +2 -0
  15. package/dist/src/channels/wechat-channel/helper-client.js +3 -3
  16. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  17. package/dist/src/channels/wechat-channel/observer.d.ts +9 -1
  18. package/dist/src/channels/wechat-channel/observer.js +7 -6
  19. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +8 -0
  20. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
  21. package/dist/src/channels/wechat-channel/runner.js +1 -1
  22. package/dist/src/channels/wechat-channel/runtime.d.ts +1 -0
  23. package/dist/src/channels/wechat-channel/runtime.js +1 -1
  24. package/dist/src/channels/wechat-channel/scheduler.d.ts +1 -1
  25. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  26. package/dist/src/channels/wechat-rpa/product-channel.d.ts +1 -0
  27. package/dist/src/channels/wechat-rpa/product-channel.js +4 -4
  28. package/dist/src/channels/wechat-rpa.d.ts +2 -0
  29. package/dist/src/channels/wechat-rpa.js +2 -2
  30. package/dist/src/commands/helper-runtime-official-download.d.ts +80 -0
  31. package/dist/src/commands/helper-runtime-official-download.js +1 -0
  32. package/dist/src/commands/runtime.d.ts +24 -0
  33. package/dist/src/commands/runtime.js +1 -1
  34. package/dist/src/commands/wechat/command.d.ts +1 -0
  35. package/dist/src/commands/wechat/command.js +1 -1
  36. package/dist/src/commands/wechat/ipc.js +1 -1
  37. package/dist/src/commands/wechat/output.js +3 -3
  38. package/dist/src/commands/wechat.d.ts +1 -1
  39. package/dist/src/commands/wechat.js +1 -1
  40. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +3 -1
  41. package/dist/src/devtools/wechat-channel-action-smoke.js +9 -9
  42. package/dist/src/index.js +2 -2
  43. package/dist/src/manager/runtime.js +10 -10
  44. package/dist/src/native-fusion/service.d.ts +0 -10
  45. package/dist/src/native-fusion/service.js +2 -2
  46. package/dist/src/session/handlers/chat.js +2 -2
  47. package/dist/src/session/handlers/session-refresh.js +1 -1
  48. package/dist/src/session/handlers/wechat-helper-runtime.d.ts +31 -0
  49. package/dist/src/session/handlers/wechat-helper-runtime.js +1 -0
  50. package/dist/src/session/manager.d.ts +4 -1
  51. package/dist/src/session/manager.js +1 -1
  52. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  53. package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/dist/src/agents/codex-control.d.ts +0 -35
  56. package/dist/src/agents/codex-control.js +0 -2
@@ -1 +1 @@
1
- import I from"node:fs";import w from"node:path";import{resolveShennianPath as f}from"../../config/index.js";import{weChatChannelConversationId as g}from"../../channels/wechat-rpa/product-channel.js";import{WeChatToolOperationError as C}from"./types.js";import{clampPositiveInteger as d,isAbortError as y,processExists as v,safePathSegment as P,stableId as S,stringValue as i}from"./utils.js";function u(){const r=f("runtime","manager-ipc.json");let e;try{e=JSON.parse(I.readFileSync(r,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const t=Number(e.pid);if(Number.isInteger(t)&&t>0&&!v(t))throw new Error(`Manager IPC runtime file is stale for PID ${t}. Restart Shennian Desktop or run \`shennian restart\`.`);const n=typeof e.url=="string"?e.url.replace(/\/+$/,""):"",o=typeof e.token=="string"?e.token:"";if(!n||!o)throw new Error(`Manager IPC runtime file is incomplete: ${r}`);return{url:n,token:o}}function k(){try{return u()}catch{return null}}function b(r){if(r.directRuntime)return null;const e=r.transport??"auto";if(e==="direct")return null;if(r.ipc)return{ctx:r.ipc,mode:"session"};if(r.sessionId?.trim())return{ctx:u(),mode:"session"};if(e==="daemon")return{ctx:u(),mode:"tool"};if(e==="auto"){const t=k();if(t)return{ctx:t,mode:"tool"}}return null}function F(r,e){if((r.transport??"auto")!=="auto"||r.sessionId?.trim()||r.ipc)return!1;const n=e instanceof Error?e.message:String(e||"");return/Unknown manager IPC path:\s*\/wechat-rpa\/tool\//i.test(n)||/Manager IPC is not available/i.test(n)||/Manager IPC runtime file is stale/i.test(n)||/Manager IPC runtime file is incomplete/i.test(n)}async function N(r,e,t){return p(r,"/wechat-rpa/channel/upsert",{managerSessionId:e.sessionId,id:e.channelId,name:`WeChat ${e.conversationName}`,workDir:e.workDir,enabled:!0,groups:[{name:e.conversationName}],canReply:!0,source:t.source||"wechat-channel",recentLimit:d(t.recentLimit,20),pollIntervalMs:d(t.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:t.download!=="never",privacyConsentAccepted:!0},t.fetchImpl,t.timeoutMs)}async function p(r,e,t,n=fetch,o){const m=String(t.managerSessionId||"");if(!m)throw new Error("managerSessionId is required");const c=o?new AbortController:null,h=c?setTimeout(()=>c.abort(),o):null;try{const s=await n(`${r.url}${e}`,{method:"POST",headers:{authorization:`Bearer ${r.token}`,"content-type":"application/json","x-shennian-manager-session-id":m},body:JSON.stringify(t),signal:c?.signal}),a=await s.json().catch(()=>({ok:!1,error:s.statusText}));if(!s.ok||!a.ok){const l=i(a.reasonCode);throw l?new C({reasonCode:l,message:i(a.error)||l,outDir:i(a.outDir)||void 0,helperTracePath:i(a.helperTracePath)||null,activityGuardPath:i(a.activityGuardPath)||null,traceId:i(a.traceId)||void 0}):new Error(a.error||`Manager IPC failed: ${s.status}`)}return a}finally{h&&clearTimeout(h)}}function W(r){const e=r.conversation.trim();if(!e)throw new Error("--conversation is required");const t=S("wechat-cli-conversation",e),n=r.sessionId?.trim()||`wechat-cli-${t.slice(-24)}`,o=`wechat-rpa:${n}`;return{sessionId:n,channelId:o,conversationId:g(e),conversationName:e,workDir:w.resolve(r.workDir||f("wechat-cli",P(e)))}}async function O(r,e,t){try{return await p(r,"/wechat-rpa/channel/sync",{managerSessionId:e.sessionId},t.fetchImpl,t.timeoutMs)}catch(n){if(y(n))return null;throw n}}export{W as buildWeChatToolBinding,N as ensureWeChatToolChannel,p as ipc,u as loadManagerIpcFromRuntimeFile,b as resolveWeChatManagerIpc,F as shouldFallbackManagerToolToDirect,k as tryLoadManagerIpcFromRuntimeFile,O as trySyncWeChatToolChannel};
1
+ import v from"node:fs";import S from"node:http";import P from"node:https";import k from"node:path";import{resolveShennianPath as g}from"../../config/index.js";import{weChatChannelConversationId as x}from"../../channels/wechat-rpa/product-channel.js";import{WeChatToolOperationError as w}from"./types.js";import{clampPositiveInteger as I,isAbortError as y,processExists as M,safePathSegment as T,stableId as E,stringValue as u}from"./utils.js";function p(){const r=g("runtime","manager-ipc.json");let e;try{e=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const t=Number(e.pid);if(Number.isInteger(t)&&t>0&&!M(t))throw new Error(`Manager IPC runtime file is stale for PID ${t}. Restart Shennian Desktop or run \`shennian restart\`.`);const a=typeof e.url=="string"?e.url.replace(/\/+$/,""):"",s=typeof e.token=="string"?e.token:"";if(!a||!s)throw new Error(`Manager IPC runtime file is incomplete: ${r}`);return{url:a,token:s}}function $(){try{return p()}catch{return null}}function W(r){if(r.directRuntime)return null;const e=r.transport??"auto";if(e==="direct")return null;if(r.ipc)return{ctx:r.ipc,mode:"session"};if(r.sessionId?.trim())return{ctx:p(),mode:"session"};if(e==="daemon")return{ctx:p(),mode:"tool"};if(e==="auto"){const t=$();if(t)return{ctx:t,mode:"tool"}}return null}function _(r,e){if((r.transport??"auto")!=="auto"||r.sessionId?.trim()||r.ipc)return!1;const a=e instanceof Error?e.message:String(e||"");return/Unknown manager IPC path:\s*\/wechat-rpa\/tool\//i.test(a)||/Manager IPC is not available/i.test(a)||/Manager IPC runtime file is stale/i.test(a)||/Manager IPC runtime file is incomplete/i.test(a)}async function A(r,e,t){return C(r,"/wechat-rpa/channel/upsert",{managerSessionId:e.sessionId,id:e.channelId,name:`WeChat ${e.conversationName}`,workDir:e.workDir,enabled:!0,groups:[{name:e.conversationName}],canReply:!0,source:t.source||"wechat-channel",recentLimit:I(t.recentLimit,20),pollIntervalMs:I(t.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:t.download!=="never",privacyConsentAccepted:!0},t.fetchImpl,t.timeoutMs)}async function C(r,e,t,a=fetch,s){const m=String(t.managerSessionId||"");if(!m)throw new Error("managerSessionId is required");let o,c=0,f="";try{if(a===fetch){const n=await D(r,e,t,s);o=n.data,c=n.status,f=n.statusText}else{const n=s?new AbortController:null,i=n?setTimeout(()=>n.abort(),s):null;try{const l=await a(`${r.url}${e}`,{method:"POST",headers:{authorization:`Bearer ${r.token}`,"content-type":"application/json","x-shennian-manager-session-id":m},body:JSON.stringify(t),signal:n?.signal});c=l.status,f=l.statusText,o=await l.json().catch(()=>({ok:!1,error:l.statusText}))}finally{i&&clearTimeout(i)}}}catch(n){throw new w({reasonCode:y(n)?"manager_ipc_timeout":"manager_ipc_network_failed",message:n instanceof Error?n.message:String(n||"manager ipc request failed"),traceId:u(t.traceId)||void 0})}if(c<200||c>=300||!o.ok){const n=u(o.reasonCode);throw n?new w({reasonCode:n,message:u(o.error)||n,outDir:u(o.outDir)||void 0,helperTracePath:u(o.helperTracePath)||null,activityGuardPath:u(o.activityGuardPath)||null,traceId:u(o.traceId)||void 0}):new Error(o.error||`Manager IPC failed: ${c||f}`)}return o}function D(r,e,t,a){const s=JSON.stringify(t),m=new URL(e,`${r.url}/`),o=m.protocol==="https:"?P:S;return new Promise((c,f)=>{const n=o.request(m,{method:"POST",headers:{authorization:`Bearer ${r.token}`,"content-type":"application/json","content-length":Buffer.byteLength(s),"x-shennian-manager-session-id":String(t.managerSessionId||"")},timeout:a},i=>{const l=[];i.on("data",h=>l.push(Buffer.from(h))),i.on("end",()=>{const h=Buffer.concat(l).toString("utf8");let d;try{d=h?JSON.parse(h):{ok:!1,error:i.statusMessage||"empty response"}}catch{d={ok:!1,error:i.statusMessage||"invalid json response"}}c({status:i.statusCode||0,statusText:i.statusMessage||"",data:d})})});n.on("timeout",()=>{n.destroy(Object.assign(new Error("Manager IPC request timed out"),{name:"AbortError"}))}),n.on("error",f),n.end(s)})}function L(r){const e=r.conversation.trim();if(!e)throw new Error("--conversation is required");const t=E("wechat-cli-conversation",e),a=r.sessionId?.trim()||`wechat-cli-${t.slice(-24)}`,s=`wechat-rpa:${a}`;return{sessionId:a,channelId:s,conversationId:x(e),conversationName:e,workDir:k.resolve(r.workDir||g("wechat-cli",T(e)))}}async function J(r,e,t){try{return await C(r,"/wechat-rpa/channel/sync",{managerSessionId:e.sessionId},t.fetchImpl,t.timeoutMs)}catch(a){if(y(a))return null;throw a}}export{L as buildWeChatToolBinding,A as ensureWeChatToolChannel,C as ipc,p as loadManagerIpcFromRuntimeFile,W as resolveWeChatManagerIpc,_ as shouldFallbackManagerToolToDirect,$ as tryLoadManagerIpcFromRuntimeFile,J as trySyncWeChatToolChannel};
@@ -1,4 +1,4 @@
1
- import y from"node:path";import{cleanInline as a,stringValue as o}from"./utils.js";const b=["SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HELPER_RUNTIME_DIR","packages/helper-runtime/wechat-channel","packages\\helper-runtime\\wechat-channel","packages/helper-runtime/dist","packages\\helper-runtime\\dist","wechat-rpa-windows-product-send-runner","PowerShell one-shot runner","activity.snapshot","automation.lease."],w=new Set(["wechat_not_running","wechat_window_not_found","permission_screen_recording_missing","permission_accessibility_missing","permission_input_monitoring_missing"]);function O(e,i){if(i){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function x(e,i){if(i==="json"){console.log(JSON.stringify(e,null,2));return}console.log(k(e).trimEnd())}function T(e,i,n,t){const r=S(e,i,n);t?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function S(e,i,n){const t=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=a(t)||"wechat_tool_failed",l=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o(n?.reasonCode)||l?.[1]||"wechat_tool_failed",c=W(s,r),_=a(l?.[2]||r),v=q(c,_,s),p=o(n?.outDir),h=o(n?.helperTracePath),d=o(n?.activityGuardPath),m=o(n?.traceId),f=E(c,s);return{ok:!1,operation:e,...i?.trim()?{conversation:i.trim()}:{},reasonCode:c,...P(s,c,_)?{rawReasonCode:s}:{},message:v,...m?{traceId:m}:{},...p?{outDir:p}:{},...h?{helperTracePath:h}:{},...d?{activityGuardPath:d}:{},...f?{userAction:f}:{}}}function k(e){const i=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
2
- `;for(const n of e.messages)i.push(`${I(n)}: ${A(n)}`);return`${i.join(`
1
+ import y from"node:path";import{cleanInline as a,stringValue as o}from"./utils.js";const b=["SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HELPER_RUNTIME_DIR","packages/helper-runtime/wechat-channel","packages\\helper-runtime\\wechat-channel","packages/helper-runtime/dist","packages\\helper-runtime\\dist","wechat-rpa-windows-product-send-runner","PowerShell one-shot runner","activity.snapshot","automation.lease."],w=new Set(["wechat_not_running","wechat_window_not_found","permission_screen_recording_missing","permission_accessibility_missing","permission_input_monitoring_missing"]);function x(e,i){if(i){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function T(e,i){if(i==="json"){console.log(JSON.stringify(e,null,2));return}console.log(k(e).trimEnd())}function C(e,i,n,t){const r=S(e,i,n);t?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function S(e,i,n){const t=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=a(t)||"wechat_tool_failed",l=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o(n?.reasonCode)||l?.[1]||"wechat_tool_failed",c=W(s,r),_=a(l?.[2]||r),v=E(c,_,s),p=o(n?.outDir),d=o(n?.helperTracePath),h=o(n?.activityGuardPath),m=o(n?.traceId),f=q(c,s);return{ok:!1,operation:e,...i?.trim()?{conversation:i.trim()}:{},reasonCode:c,...$(s,c,_)?{rawReasonCode:s}:{},message:v,...m?{traceId:m}:{},...p?{outDir:p}:{},...d?{helperTracePath:d}:{},...h?{activityGuardPath:h}:{},...f?{userAction:f}:{}}}function k(e){const i=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
2
+ `;for(const n of e.messages)i.push(`${A(n)}: ${I(n)}`);return`${i.join(`
3
3
  `)}
4
- `}function W(e,i=""){const n=e.trim().toLowerCase(),t=`${n} ${i}`.toLowerCase();return n?n==="insufficient_credits"?"insufficient_credits":n==="entitlement_required"||n==="enterprise_required"||n.includes("entitlement")?"entitlement_required":n==="manual_review_required"||n.includes("manual_review")?"manual_review_required":n==="attachment_pending"||/attachment.*pending|media_original_pending/.test(t)?"attachment_pending":/helper_runtime_required|helper_runtime_source_missing|helper_runtime_package_manifest_missing|manifest_missing|helper_missing|helper_not_executable/.test(n)?"helper_missing":/helper_runtime_cli_too_old|helper_runtime_version_mismatch|helper_runtime_protocol_mismatch|helper_runtime_platform_mismatch|helper_runtime_package_manifest_invalid|integrity_mismatch/.test(n)?"helper_version_incompatible":/^user_active|recent_(keyboard|mouse|scroll)|frontmost_app_changed|user_takeover|activity/.test(n)?"user_active_retry_later":/helper_command_timeout/.test(n)?"wechat_window_unavailable":u(t)?"wechat_tool_failed":/permission|accessibility|screen_recording|automation|mac_input|input-monitoring/.test(t)?"permission_required":/wechat_login_required|login_required|安全验证|扫码登录|重新登录/.test(t)?"wechat_login_required":/wechat_single_main_window_required|wechat_duplicate_instance|duplicate_main_window/.test(n)?"wechat_window_unavailable":/visible_desktop|desktop_session|screen_locked|session_locked|rdp_disconnected|rdp.*visible|rdp.*desktop/.test(t)?"windows_visible_desktop_unavailable":/dpi_mapping_failed|display_topology|monitor_topology|multi_?monitor|multi_?screen/.test(t)?"dpi_mapping_failed":/conversation_not|conversation_title_not_confirmed|conversation_not_visible|open_read_required|search_result_not_visible/.test(n)?"conversation_not_found":/wechat_window|wechat_not_running|window_not|window_unavailable|window_unresponsive/.test(n)?"wechat_window_unavailable":n:"wechat_tool_failed"}function E(e,i=""){if(e==="wechat_login_required")return"Open WeChat on this machine, complete login or security verification, then retry.";if(e==="wechat_window_unavailable")return"Open Shennian Desktop and the main WeChat window on the active desktop, then retry. If running this command over SSH on Windows, retry with the local daemon available.";if(e==="wechat_single_main_window_required")return"Keep WeChat open and visible. If the window state is unclear, let Shennian re-check it with visual classification.";if(e==="windows_visible_desktop_unavailable")return"Unlock or reconnect the Windows visible desktop session, keep WeChat visible, then retry.";if(e==="dpi_mapping_failed")return"Move WeChat fully onto one visible display or use a supported display scaling setup, then retry.";if(e==="conversation_not_found")return"Check the WeChat conversation name and make sure the conversation can be searched.";if(e==="helper_missing")return"Open Shennian Desktop or run `shennian runtime install helper`, then retry.";if(e==="helper_version_incompatible")return"Run `shennian runtime repair helper` or upgrade Shennian CLI, then retry.";if(e==="permission_required")return H(i);if(e==="entitlement_required")return"Enable the WeChat capability for this account in Shennian.";if(e==="insufficient_credits")return"Add credits or adjust the account plan in Shennian.";if(e==="user_active_retry_later")return"Wait until the user is idle, then retry.";if(e==="attachment_pending")return"Open WeChat and let the attachment finish downloading, then retry.";if(e==="manual_review_required")return"Review the current WeChat window before retrying."}function q(e,i,n){const t=n.trim().toLowerCase();return e==="permission_required"?R(t):!i||i===e||u(i)||w.has(t)?$(e):i}function R(e){return e.includes("screen_recording")?"Shennian Helper.app needs Screen Recording permission.":e.includes("accessibility")?"Shennian Helper.app needs Accessibility permission.":e.includes("input_monitoring")||e.includes("input-monitoring")?"Shennian Helper.app needs Input Monitoring permission.":"Shennian Helper.app needs macOS privacy permissions."}function H(e){const i=e.trim().toLowerCase();return i.includes("screen_recording")?"Grant Shennian Helper.app Screen Recording in Privacy & Security, then retry.":i.includes("accessibility")?"Grant Shennian Helper.app Accessibility in Privacy & Security, then retry.":i.includes("input_monitoring")||i.includes("input-monitoring")?"Grant Shennian Helper.app Input Monitoring in Privacy & Security, then retry.":"Grant Shennian Helper.app the required macOS permissions in Privacy & Security, then retry."}function P(e,i,n){const t=e.trim().toLowerCase();return!(e===i||w.has(t)||u(`${e} ${n}`))}function u(e){const i=e.replace(/\\/g,"/").toLowerCase();return b.some(n=>{const t=n.replace(/\\/g,"/").toLowerCase();return i.includes(t)})}function $(e){return e==="wechat_login_required"?"WeChat needs login or security verification on this machine.":e==="wechat_window_unavailable"?"WeChat window is not available on the active desktop.":e==="wechat_single_main_window_required"?"WeChat window state needs visual confirmation.":e==="windows_visible_desktop_unavailable"?"Windows visible desktop session is unavailable.":e==="dpi_mapping_failed"?"Windows display scaling or monitor geometry cannot be mapped safely.":e==="conversation_not_found"?"WeChat conversation was not found.":e==="helper_missing"?"Shennian Helper runtime is not installed or cannot be found.":e==="helper_version_incompatible"?"Shennian Helper runtime is incompatible with this CLI.":e==="permission_required"?"Shennian Helper.app needs macOS privacy permissions.":e==="entitlement_required"?"WeChat capability is not enabled for this account.":e==="insufficient_credits"?"Insufficient credits.":e==="user_active_retry_later"?"User activity is in progress; retry later.":e==="attachment_pending"?"WeChat attachment is still pending.":e==="manual_review_required"?"Manual review is required before retrying.":e}function A(e){const i=e.attachments||[],n=a(e.text);return n&&i.length===0?n:n&&i.length>0?`${n} ${i.map(g).join("\u3001")}`:i.length>0?i.map(g).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function I(e){return a(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function g(e){const i=a(e.name||y.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",t=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${M(i)}](${L(n)})${t}`:`${i}${t}`}function L(e){const i=e.replace(/\\/g,"/");return/[\s()<>]/.test(i)?`<${i.replace(/[<>]/g,"")}>`:i}function M(e){return a(e).replace(/([\\\]])/g,"\\$1")}export{k as buildWeChatReadMarkdown,S as buildWeChatToolErrorResult,x as printReadLatestResult,T as printToolError,O as printToolResult};
4
+ `}function W(e,i=""){const n=e.trim().toLowerCase(),t=`${n} ${i}`.toLowerCase();return n?n==="insufficient_credits"?"insufficient_credits":n==="entitlement_required"||n==="enterprise_required"||n.includes("entitlement")?"entitlement_required":n==="manual_review_required"||n.includes("manual_review")?"manual_review_required":n==="attachment_pending"||/attachment.*pending|media_original_pending/.test(t)?"attachment_pending":/helper_runtime_required|helper_runtime_source_missing|helper_runtime_package_manifest_missing|manifest_missing|helper_missing|helper_not_executable/.test(n)?"helper_missing":/helper_runtime_cli_too_old|helper_runtime_version_mismatch|helper_runtime_protocol_mismatch|helper_runtime_platform_mismatch|helper_runtime_package_manifest_invalid|integrity_mismatch/.test(n)?"helper_version_incompatible":/^user_active|recent_(keyboard|mouse|scroll)|frontmost_app_changed|user_takeover|activity/.test(n)?"user_active_retry_later":/^manager_ipc_(timeout|network_failed)$/.test(n)?n:/helper_command_timeout/.test(n)?"wechat_window_unavailable":u(t)?"wechat_tool_failed":/permission|accessibility|screen_recording|automation|mac_input|input-monitoring/.test(t)?"permission_required":/wechat_login_required|login_required|安全验证|扫码登录|重新登录/.test(t)?"wechat_login_required":/wechat_single_main_window_required|wechat_duplicate_instance|duplicate_main_window/.test(n)?"wechat_window_unavailable":/visible_desktop|desktop_session|screen_locked|session_locked|rdp_disconnected|rdp.*visible|rdp.*desktop/.test(t)?"windows_visible_desktop_unavailable":/dpi_mapping_failed|display_topology|monitor_topology|multi_?monitor|multi_?screen/.test(t)?"dpi_mapping_failed":/conversation_not|conversation_title_not_confirmed|conversation_not_visible|open_read_required|search_result_not_visible/.test(n)?"conversation_not_found":/wechat_window|wechat_not_running|window_not|window_unavailable|window_unresponsive/.test(n)?"wechat_window_unavailable":n:"wechat_tool_failed"}function q(e,i=""){if(e==="wechat_login_required")return"Open WeChat on this machine, complete login or security verification, then retry.";if(e==="wechat_window_unavailable")return"Open Shennian Desktop and the main WeChat window on the active desktop, then retry. If running this command over SSH on Windows, retry with the local daemon available.";if(e==="wechat_single_main_window_required")return"Keep WeChat open and visible. If the window state is unclear, let Shennian re-check it with visual classification.";if(e==="windows_visible_desktop_unavailable")return"Unlock or reconnect the Windows visible desktop session, keep WeChat visible, then retry.";if(e==="dpi_mapping_failed")return"Move WeChat fully onto one visible display or use a supported display scaling setup, then retry.";if(e==="conversation_not_found")return"Check the WeChat conversation name and make sure the conversation can be searched.";if(e==="helper_missing")return"Open the \u4F7F\u7528\u5FAE\u4FE1 page in Shennian and click Install Helper, or open Shennian Desktop, then retry.";if(e==="helper_version_incompatible")return"Use Repair Helper on the \u4F7F\u7528\u5FAE\u4FE1 page, or upgrade Shennian CLI, then retry.";if(e==="permission_required")return H(i);if(e==="entitlement_required")return"Enable the WeChat capability for this account in Shennian.";if(e==="insufficient_credits")return"Add credits or adjust the account plan in Shennian.";if(e==="user_active_retry_later")return"Wait until the user is idle, then retry.";if(e==="attachment_pending")return"Open WeChat and let the attachment finish downloading, then retry.";if(e==="manual_review_required")return"Review the current WeChat window before retrying.";if(e==="manager_ipc_timeout"||e==="manager_ipc_network_failed")return"Restart Shennian Desktop or run this command with --transport direct, then retry."}function E(e,i,n){const t=n.trim().toLowerCase();return e==="permission_required"?R(t):!i||i===e||u(i)||w.has(t)?P(e):i}function R(e){return e.includes("screen_recording")?"Shennian Helper.app needs Screen Recording permission.":e.includes("accessibility")?"Shennian Helper.app needs Accessibility permission.":e.includes("input_monitoring")||e.includes("input-monitoring")?"Shennian Helper.app needs Input Monitoring permission.":"Shennian Helper.app needs macOS privacy permissions."}function H(e){const i=e.trim().toLowerCase();return i.includes("screen_recording")?"Grant Shennian Helper.app Screen Recording in Privacy & Security, then retry.":i.includes("accessibility")?"Grant Shennian Helper.app Accessibility in Privacy & Security, then retry.":i.includes("input_monitoring")||i.includes("input-monitoring")?"Grant Shennian Helper.app Input Monitoring in Privacy & Security, then retry.":"Grant Shennian Helper.app the required macOS permissions in Privacy & Security, then retry."}function $(e,i,n){const t=e.trim().toLowerCase();return!(e===i||w.has(t)||u(`${e} ${n}`))}function u(e){const i=e.replace(/\\/g,"/").toLowerCase();return b.some(n=>{const t=n.replace(/\\/g,"/").toLowerCase();return i.includes(t)})}function P(e){return e==="wechat_login_required"?"WeChat needs login or security verification on this machine.":e==="wechat_window_unavailable"?"WeChat window is not available on the active desktop.":e==="wechat_single_main_window_required"?"WeChat window state needs visual confirmation.":e==="windows_visible_desktop_unavailable"?"Windows visible desktop session is unavailable.":e==="dpi_mapping_failed"?"Windows display scaling or monitor geometry cannot be mapped safely.":e==="conversation_not_found"?"WeChat conversation was not found.":e==="helper_missing"?"Shennian Helper runtime is not installed or cannot be found.":e==="helper_version_incompatible"?"Shennian Helper runtime is incompatible with this CLI.":e==="permission_required"?"Shennian Helper.app needs macOS privacy permissions.":e==="entitlement_required"?"WeChat capability is not enabled for this account.":e==="insufficient_credits"?"Insufficient credits.":e==="user_active_retry_later"?"User activity is in progress; retry later.":e==="attachment_pending"?"WeChat attachment is still pending.":e==="manual_review_required"?"Manual review is required before retrying.":e==="manager_ipc_timeout"?"Local Shennian daemon did not respond before the request timed out.":e==="manager_ipc_network_failed"?"Local Shennian daemon connection failed during the request.":e}function I(e){const i=e.attachments||[],n=a(e.text);return n&&i.length===0?n:n&&i.length>0?`${n} ${i.map(g).join("\u3001")}`:i.length>0?i.map(g).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function A(e){return a(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function g(e){const i=a(e.name||y.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",t=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${M(i)}](${L(n)})${t}`:`${i}${t}`}function L(e){const i=e.replace(/\\/g,"/");return/[\s()<>]/.test(i)?`<${i.replace(/[<>]/g,"")}>`:i}function M(e){return a(e).replace(/([\\\]])/g,"\\$1")}export{k as buildWeChatReadMarkdown,S as buildWeChatToolErrorResult,T as printReadLatestResult,C as printToolError,x as printToolResult};
@@ -1,4 +1,4 @@
1
- export { registerWeChatCommand } from './wechat/command.js';
1
+ export { registerWeChatCommand, resolveWeChatCommandTimeout } from './wechat/command.js';
2
2
  export * from './wechat/direct.js';
3
3
  export * from './wechat/doctor.js';
4
4
  export * from './wechat/operations.js';
@@ -1 +1 @@
1
- import{registerWeChatCommand as e}from"./wechat/command.js";export*from"./wechat/direct.js";export*from"./wechat/doctor.js";export*from"./wechat/operations.js";export*from"./wechat/output.js";export*from"./wechat/types.js";export{e as registerWeChatCommand};
1
+ import{registerWeChatCommand as e,resolveWeChatCommandTimeout as m}from"./wechat/command.js";export*from"./wechat/direct.js";export*from"./wechat/doctor.js";export*from"./wechat/operations.js";export*from"./wechat/output.js";export*from"./wechat/types.js";export{e as registerWeChatCommand,m as resolveWeChatCommandTimeout};
@@ -1,5 +1,5 @@
1
1
  import { buildBottomPageReport, buildDownloadVisibleMediaReport } from './wechat-channel-download-report.js';
2
- import { type WeChatChannelObserveActivityGuardOptions } from '../channels/wechat-channel/observer.js';
2
+ import { type WeChatChannelObserveActivityGuardOptions, type WeChatChannelOcrBlock } from '../channels/wechat-channel/observer.js';
3
3
  import { type WeChatChannelRuntimePlatform } from '../channels/wechat-channel/runtime.js';
4
4
  export type WeChatChannelActionSmokeStep = 'open-read' | 'send-text' | 'send-file' | 'send-image' | 'send-video' | 'structure-window' | 'server-observe' | 'read-bottom-page' | 'download-visible-media' | 'send-and-confirm' | 'human-send-preflight-busy' | 'human-send-preflight-busy-then-idle' | 'human-send-user-active-timeout' | 'human-send-takeover-during-run' | 'human-send-takeover-then-idle' | 'human-send-normal' | 'human-observe-deferred';
5
5
  export type WeChatChannelActionSmokeOptions = {
@@ -57,6 +57,7 @@ export declare function runWeChatChannelActionSmoke(options: WeChatChannelAction
57
57
  export declare function buildSendStepAcceptance(input: {
58
58
  sendAccepted: boolean;
59
59
  text?: string | null;
60
+ attachmentPath?: string | null;
60
61
  beforeTextCount?: number | null;
61
62
  afterTextCount?: number | null;
62
63
  }): {
@@ -100,3 +101,4 @@ export declare function buildSendAndConfirmEvidence(input: WeChatChannelSendAndC
100
101
  export declare function countMarkerOccurrencesInOcrBlocks(blocks: Array<{
101
102
  text?: string | null;
102
103
  }>, marker: string): number;
104
+ export declare function countMarkerOccurrencesInChatContentBlocks(blocks: Array<WeChatChannelOcrBlock>, marker: string): number;
@@ -1,10 +1,10 @@
1
- import _ from"node:crypto";import{spawnSync as ae}from"node:child_process";import w from"node:fs";import i from"node:path";import{fileURLToPath as ie}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 de}from"../config/index.js";import{createWeChatChannelApiClient as ce}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ue,WECHAT_CHANNEL_HELPER_VERSION as le}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as me}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as T}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as he,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as fe,saveWeChatChannelLedger as ge,updateWeChatChannelBindingLedger as pe}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as U}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as G,sendQueuedWeChatOutboundRecords as S}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,ensureWeChatWindowReady as C,fallbackWeChatMessageInputPointForWindow as we,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as be,openConversationBySearch as I,recognizeWeChatScreenshot as E}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as ve}from"../channels/wechat-channel/runtime.js";function ye(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 Se="ABC",Ce="wechat-channel-action-smoke";function ke(e){const t=e.filter(u=>u!=="--"),n=ct(t),o=lt(t,"--step").map(ut).filter(u=>u!=null),r=de(),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):Ie(n);return{conversation:g(t,"--conversation")||g(t,"--group")||Se,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:B(g(t,"--file")),imagePath:B(g(t,"--image")),videoPath:B(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 Ie(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 u of e.steps){if(Ae(u)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:u,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 _e({step:u,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(u=>u.ok||u.skipped),m(i.join(e.outDir,"summary.json"),d)}return d}function Ae(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function _e(e){try{return e.step==="open-read"?await Te(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 Me(e):await z(e)}catch(t){const n=bt(t),o=await De(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 De(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 m(o,r),{failureDiagnosticsPath:o}}function Re(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function Me(e){if(e.step==="human-observe-deferred")return await Pe(e);const t=e.step,n=`human-${A(`${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=Q(e.helper,Z(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const re=await C(s,e.options.traceId),se=await k(s,re,e.options.traceId);return I({helper:s,window:se,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},u=new G({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 S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:u,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 S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:u,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(Ve()),f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:u,now:new Date("2026-06-15T00:00:06.000Z")}));const h=Je(e.step,r)?await O({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=h>0?he({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:[]},l=o.records[0],b=Xe(l,f,p.confirmedRecords),v={case:e.step,marker:n,runStatus:b,reasonCode:l.deferReason||l.failureCode||null,outboundStatus:l.sendStatus,attemptCount:l.attemptCount??0,nextAttemptAt:l.nextAttemptAt??null,observedMarkerCount:h,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:l.commitStage??null};return m(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 Pe(e){const t=Q(e.helper,Z("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 m(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 Te(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 je({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 E(e.helper,r,e.options.traceId);return m(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 je(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 E(e.helper,t,e.traceId);m(i.join(e.outDir,"open-read-failure-ocr.json"),o),m(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){m(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function z(e){return await Ee(e,it(e.step,e.options))}async function Ee(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 O({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}:${A(o)}`,idempotencyKey:`action-smoke:${A(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 G({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const l=await C(e.helper,e.options.traceId),b=await k(e.helper,l,e.options.traceId);return{...await I({helper:e.helper,window:b,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:we(b)}}}),a=await $e({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],u=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,h=We({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:f});return{step:e.step,ok:h.ok,reasonCode:u||h.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:h.confirmationRequired,sendAccepted:h.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 $e(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=Oe(r.waitingRecords[0]?.nextAttemptAt,o,n);await L(a),r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function Oe(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(l=>!V(l)),n=e.sendResults.filter(l=>!l.skipped).map(l=>l.details?.text&&typeof l.details.text=="string"?l.details.text:"").filter(Boolean),o=e.observedMessages.map(l=>[l.normalizedText||"",l.anchorText||"",l.textExcerpt||""].join(`
1
+ import M from"node:crypto";import{spawnSync as de}from"node:child_process";import p from"node:fs";import i from"node:path";import{fileURLToPath as ce}from"node:url";import{buildBottomPageReport as T,buildDownloadVisibleMediaReport as K}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as S}from"./wechat-channel-markdown-report.js";import{loadConfig as le}from"../config/index.js";import{createWeChatChannelApiClient as k}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ue,WECHAT_CHANNEL_HELPER_VERSION as me}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as he}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as B}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as fe,enqueueWeChatOutboundReply as G}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as ge,saveWeChatChannelLedger as we,updateWeChatChannelBindingLedger as pe}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as V}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as z,sendQueuedWeChatOutboundRecords as C}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,detectWeChatMessageInputPointByServerVision as be,ensureWeChatWindowReady as y,focusKnownWeChatWindow as I,observeWeChatChannelBindingViaHelper as ve,openConversationBySearch as x,recognizeWeChatScreenshot as E}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as ye}from"../channels/wechat-channel/runtime.js";function Se(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 ke="ABC",Ce="wechat-channel-action-smoke";function Ie(e){const t=e.filter(l=>l!=="--"),n=ht(t),r=gt(t,"--step").map(ft).filter(l=>l!=null),o=le(),s=f(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,d=i.resolve(f(t,"--out")||f(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),a=r.length?wt(r):xe(n);return{conversation:f(t,"--conversation")||f(t,"--group")||ke,mode:r.length?"custom":n,steps:a,outDir:d,workDir:i.resolve(f(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:f(t,"--runtime-id")||Ce,machineId:f(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||o.machineId||"local",sessionId:f(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:f(t,"--server-url")||o.serverUrl,platform:lt(f(t,"--platform")),text:f(t,"--text"),filePath:L(f(t,"--file")),imagePath:L(f(t,"--image")),videoPath:L(f(t,"--video")),downloadAnchorText:f(t,"--download-anchor-text")||f(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function xe(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 De(e){const t=Date.now(),n=new Date(t).toISOString();p.mkdirSync(e.outDir,{recursive:!0});const r=i.join(e.outDir,"helper-trace.jsonl");p.writeFileSync(r,"");const o=ot(e),s=o.bindings[0],d=st(e,r);await d.start();const a={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:r,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ae(l)&&a.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){a.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}a.steps.push(await _e({step:l,options:e,runtime:o,binding:s,helper:d}))}}finally{await d.stop().catch(()=>{}),a.finishedAt=new Date().toISOString(),a.durationMs=Date.now()-t,a.ok=a.steps.every(l=>l.ok||l.skipped),m(i.join(e.outDir,"summary.json"),a)}return a}function Ae(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function _e(e){try{return e.step==="open-read"?await je(e):e.step==="structure-window"?await Ke(e):e.step==="server-observe"?await qe(e):e.step==="read-bottom-page"?await He(e):e.step==="download-visible-media"?await Ue(e):e.step==="send-and-confirm"?await Fe(e):e.step.startsWith("human-")?await Me(e):await J(e)}catch(t){const n=kt(t),r=await Re(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:Ct(n),...r?{details:r}:{}}}}async function Re(e,t,n){if(!Pe(t))return;const r=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),o={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{o.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){o.permissionsError=s instanceof Error?s.message:String(s)}try{o.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){o.windowsError=s instanceof Error?s.message:String(s)}return m(r,o),{failureDiagnosticsPath:r}}function Pe(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function Me(e){if(e.step==="human-observe-deferred")return await Te(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,r={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=G({ledger:r,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=Z(e.helper,Y(t)),d=s.actionLog,a=k({serverUrl:e.options.serverUrl}),l=async P=>{d.push(`openConversation:${P}`);const ae=await y(s,e.options.traceId),ie=await I(s,ae,e.options.traceId);return x({helper:s,window:ie,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:P},runtime:e.runtime,api:a,workDir:e.options.workDir,traceId:e.options.traceId})},c=new z({helper:s,platform:e.runtime.policy.platform,openConversation:l,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 C({ledger:r,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:c,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 C({ledger:r,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:c,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(Je()),h=await C({ledger:r,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:c,now:new Date("2026-06-15T00:00:06.000Z")}));const g=Qe(e.step,o)?await F({helper:s,runtime:e.runtime,binding:e.binding,api:a,workDir:e.options.workDir,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,u=g>0?fe({ledger:r,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:o.text,normalizedText:o.text,textExcerpt:o.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},b=r.records[0],R=nt(b,h,u.confirmedRecords),v={case:e.step,marker:n,runStatus:R,reasonCode:b.deferReason||b.failureCode||null,outboundStatus:b.sendStatus,attemptCount:b.attemptCount??0,nextAttemptAt:b.nextAttemptAt??null,observedMarkerCount:g,actionLog:d,clipboardRestore:d.includes("clipboard.restore")?"attempted":"not_needed",commitStage:b.commitStage??null};return m(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:rt(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function Te(e){const t=Z(e.helper,Y("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),r=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),o={case:e.step,marker:null,runStatus:r?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:r?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return m(i.join(e.options.outDir,`${e.step}-summary.json`),o),{step:e.step,ok:r&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:o.reasonCode||void 0,details:o}}async function je(e){const t=await y(e.helper,e.options.traceId),n=await I(e.helper,t,e.options.traceId),r=k({serverUrl:e.options.serverUrl}),o=await x({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:r,workDir:e.options.workDir,traceId:e.options.traceId});if(!o.opened)throw await Ee({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 s=await j(e.helper,n.windowId,e.options.traceId),d=D(e.options.outDir,"open-read-full-window",s),a=await E(e.helper,s,e.options.traceId);return m(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:d,screenshot:{width:s.width,height:s.height,mimeType:s.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function Ee(e){try{const t=await j(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=D(e.outDir,"open-read-failure-window",t),r=await E(e.helper,t,e.traceId);m(i.join(e.outDir,"open-read-failure-ocr.json"),r),m(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:r.blocks?.length??0,visibleConversationFingerprintCount:r.visibleConversationFingerprints?.length??0,topTexts:(r.blocks??[]).slice(0,30).map(o=>({text:o.text,bbox:o.bbox}))})}catch(t){m(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function J(e){return await We(e,ut(e.step,e.options))}async function We(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n=k({serverUrl:e.options.serverUrl}),r={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,s=t.text?await F({helper:e.helper,runtime:e.runtime,binding:e.binding,api:n,workDir:e.options.workDir,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;G({ledger:r,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 d=new z({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,activityGatePolicy:e.runtime.foregroundPolicy==="work"?{mouseMovedThresholdMs:0,mouseClickThresholdMs:0,scrollWheelThresholdMs:0,keyDownThresholdMs:0}:void 0,takeoverCheck:e.runtime.foregroundPolicy!=="work",onAfterPaste:async({kind:u})=>{await Xe({helper:e.helper,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-paste-${u}`})},openConversation:async u=>{const b=await y(e.helper,e.options.traceId),R=await I(e.helper,b,e.options.traceId),v=await x({helper:e.helper,window:R,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:u},runtime:e.runtime,api:n,workDir:e.options.workDir,traceId:e.options.traceId});if(!v.opened)return{...v,inputPoint:null};const P=await be({runtime:e.runtime,binding:{...e.binding,conversationDisplayName:u},helper:e.helper,api:n,workDir:e.options.workDir,traceId:e.options.traceId,window:R});return{...v,inputPoint:P}}}),a=await Be({ledger:r,bindingId:e.binding.bindingId,sender:d});await V("send-after-submit",`${e.options.traceId}:${e.step}:after-submit`);const l=r.records[0],c=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,w=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,h=w&&t.text?await tt({helper:e.helper,runtime:e.runtime,binding:e.binding,api:n,workDir:e.options.workDir,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:s}):null,g=$e({sendAccepted:w,text:t.text,attachmentPath:t.attachmentPath,beforeTextCount:s,afterTextCount:h});return{step:e.step,ok:g.ok,reasonCode:c||g.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:g.confirmationRequired,sendAccepted:g.sendAccepted,beforeTextCount:s,afterTextCount:h,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:l?.sendStatus,failureCode:l?.failureCode??null,lastErrorSummary:l?.lastErrorSummary??null,deferReason:l?.deferReason,commitStage:l?.commitStage??null,attemptCount:l?.attemptCount??0,nextAttemptAt:l?.nextAttemptAt??null}}}function $e(e){const t=!!(e.text&&e.text.trim()&&!e.attachmentPath);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,r=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,o=n==null?r>0:r>n;return{ok:o,reasonCode:o?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Be(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,r=Date.now();let o=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;o.waitingRecords.length>0&&s<=t&&!(Date.now()-r>=n);s+=1){const d=Ne(o.waitingRecords[0]?.nextAttemptAt,r,n);await q(d),o=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return o}function Ne(e,t,n){const r=Math.max(0,n-(Date.now()-t));if(r<=0)return 0;const o=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(o)?o-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,r)))}function Oe(e){const t=e.sendResults.filter(u=>!Q(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),r=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
2
2
  `)).join(`
3
- `),r=n.filter(l=>o.includes(l)),s=e.sendResults.map(l=>typeof l.details?.sendStatus=="string"?l.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(V).length,d=W(t,"waitingRecords"),u=W(t,"failedRecords"),c=W(t,"manualReviewRecords"),f=t.find(l=>!l.ok||l.skipped),h=s.length===n.length&&s.every(l=>l==="sent_unconfirmed"||l==="confirmed_echo"),p=!f&&n.length>0&&r.length>0&&h&&d===0&&u===0&&c===0;return{ok:p,reasonCode:p?void 0:f?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(u>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(h?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:u,manualReviewCount:c}}function V(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function W(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"],n=[];for(const c of t)n.push(await z({...e,step:c}));await L(1200);const o=await R(e,3,{observePipeline:"edge-structure"}),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)});m(i.join(e.options.outDir,"bottom-page-summary.json"),r),m(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),x(s,a);const d=Be({sendResults:n,observedMessages:o.messages}),u=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:u,reasonCode:u?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 R(e,t,n={}){let o;const r=[];for(let s=1;s<=t;s+=1)try{const a=await J(e,n);return r.length&&(r.push({phase:"observe",attempt:s,ok:!0,observedCount:a.messages.length,at:new Date().toISOString()}),m(i.join(e.options.outDir,"observe-retry-events.json"),r)),a}catch(a){if(o=a,r.push({phase:"observe",attempt:s,ok:!1,errorSummary:a instanceof Error?a.message:String(a||""),at:new Date().toISOString()}),s>=t||!Ne(a))break;try{const d=await C(e.helper,e.options.traceId),u=await k(e.helper,d,e.options.traceId),c=await I({helper:e.helper,window:u,binding:e.binding,traceId:e.options.traceId});r.push({phase:"recover",attempt:s,ok:c.opened,reason:c.reason,windowId:u.windowId,at:new Date().toISOString()})}catch(d){r.push({phase:"recover",attempt:s,ok:!1,errorSummary:d instanceof Error?d.message:String(d||""),at:new Date().toISOString()})}m(i.join(e.options.outDir,"observe-retry-events.json"),r),await U("observe-retry",`${e.options.traceId}:observe-retry:${s}`,1500*s)}throw r.length&&m(i.join(e.options.outDir,"observe-retry-events.json"),r),o}function Ne(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 R(e,3),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 m(i.join(e.options.outDir,"bottom-page-summary.json"),n),m(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),x(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 R(e,3,{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});m(i.join(e.options.outDir,"bottom-page-summary.json"),n),m(i.join(e.options.outDir,"download-visible-summary.json"),s),m(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),x(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 R(e,3,{observePipeline:"server-observe"});$(e.options.outDir,"server-observe",t.observationEvidence),m(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)});x(n,o);const r=ee(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:F(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Ke(e){const t=await J(e,{observePipeline:"edge-structure",downloadMedia:!1});$(e.options.outDir,"structure-window",t.observationEvidence),m(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)});x(n,o);const r=ee(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:F(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function J(e,t={}){const n=ye({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=ce({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");w.mkdirSync(r,{recursive:!0});const s=t.persistLocalLedger?ht(e.options):void 0,a=s?fe(s,e.runtime.runtimeId):void 0;let d,u,c;const f=await be({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,activityGuard:e.options.activityGuard,observePipeline:t.observePipeline,localLedgerTailAnchors:a?ft(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:l,ocr:b})=>{d=Ge(l),u=ze(b),c=Ue(l,b,{captureHash:d,ocrHash:u})}});let h,p;if(a&&s){const l=pe({ledger:a,bindingId:e.binding.bindingId,observedMessages:f});h=l.binding,p=oe({bindingId:e.binding.bindingId,revision:h.revision,messages:l.newMessages}),ge(s,a)}return{messages:f,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...h?{ledgerBinding:h}:{},traceHashes:{...d?{captureHash:d}:{},...u?{ocrHash:u}:{},...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;m(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function Ge(e){if(e.dataBase64)return`sha256:${_.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function ze(e){return oe({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 Q(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(u){n=u,o=0,r=0},async request(u,c,f){if(a.push(u),u==="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(u==="automation.lease.acquire"){const h=await e.request(u,c,f),p=h.result&&typeof h.result=="object"?h.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",h}return u==="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(u,c,f)}}}function Z(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 Ve(){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 O(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 j(e.helper,n.windowId,e.traceId),s=P(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await E(e.helper,r,e.traceId);return m(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=Y(o,n);if(r>0)return r;const s=X(n);return s.length<8?0:Y(X(o),s)}function X(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function Y(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 O({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 ve({runtimeId:e.runtimeId,machineId:e.machineId,pollIntervalMs:6e4,platform:e.platform,foregroundPolicy:"work",bindings:[{bindingId:`action-smoke:${A(e.conversation)}`,sessionId:e.sessionId||`action-smoke:${A(e.runtimeId)}`,conversationDisplayName:e.conversation,enabled:!0,allowReply:!0,downloadMedia:!0}]})}function tt(e,t){const n=ue({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new me({helperPath:n.helperPath,expectedHelperVersion:n.version||le,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:_.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
- ${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||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 ee(e){const t=e.flatMap(n=>dt(n.mediaMetadata));return{count:t.length,byAvailability:F(t.map(n=>te(n,"availability")||"unknown")),localPaths:t.map(n=>te(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(ne):ne(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 B(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 m(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,`${JSON.stringify(t,null,2)}
8
- `,"utf8")}function x(e,t){w.mkdirSync(i.dirname(e),{recursive:!0}),w.writeFileSync(e,t,"utf8")}function ht(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function ft(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 ae("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 F(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function te(e,t){return typeof e[t]=="string"?e[t].trim():""}function ne(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function A(e){return _.createHash("sha256").update(e).digest("hex").slice(0,16)}function oe(e){return`sha256:${_.createHash("sha256").update(wt(e)).digest("hex")}`}function wt(e){return JSON.stringify(N(e))}function N(e){if(Array.isArray(e))return e.map(N);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,N(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 St(){const e=ke(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])===ie(import.meta.url)&&St().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,Oe as nextSmokeDrainWaitMs,ke as parseWeChatChannelActionSmokeArgs,ye as resolveWeChatSmokeObserveSessionMode,xe as runWeChatChannelActionSmoke,Ie as stepsForMode};
3
+ `),o=n.filter(u=>r.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),d=e.sendResults.filter(Q).length,a=N(t,"waitingRecords"),l=N(t,"failedRecords"),c=N(t,"manualReviewRecords"),w=t.find(u=>!u.ok||u.skipped),h=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),g=!w&&n.length>0&&o.length>0&&h&&a===0&&l===0&&c===0;return{ok:g,reasonCode:g?void 0:w?.reasonCode||(a>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(h?void 0:"outbound_status_not_accepted")||(o.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:o,outboundStatuses:s,skippedFixtureCount:d,pendingOutboundCount:a,failedOutboundCount:l,manualReviewCount:c}}function Q(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function N(e,t){return e.reduce((n,r)=>{const o=r.details?.[t];return n+(typeof o=="number"&&Number.isFinite(o)?o:0)},0)}async function Fe(e){const t=["send-text"],n=[];for(const c of t)n.push(await J({...e,step:c}));await q(1200);const r=await W(e,3,{observePipeline:"edge-structure"}),o=T(r.messages),s=i.join(e.options.outDir,"bottom-page.md"),d=S(r.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(r.attachmentsDir)});m(i.join(e.options.outDir,"bottom-page-summary.json"),o),m(i.join(e.options.outDir,"server-observe-messages.json"),r.messages),A(s,d);const a=Oe({sendResults:n,observedMessages:r.messages}),l=a.ok&&r.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:a.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:a.sentTexts,confirmedTexts:a.confirmedTexts,outboundStatuses:a.outboundStatuses,skippedFixtureCount:a.skippedFixtureCount,pendingOutboundCount:a.pendingOutboundCount,failedOutboundCount:a.failedOutboundCount,manualReviewCount:a.manualReviewCount,observedCount:r.messages.length,byKind:o.byKind,attachmentSummary:o.attachmentSummary,attachmentsDir:r.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:d}}}async function W(e,t,n={}){let r;const o=[];for(let s=1;s<=t;s+=1)try{const d=await X(e,n);return o.length&&(o.push({phase:"observe",attempt:s,ok:!0,observedCount:d.messages.length,at:new Date().toISOString()}),m(i.join(e.options.outDir,"observe-retry-events.json"),o)),d}catch(d){if(r=d,o.push({phase:"observe",attempt:s,ok:!1,errorSummary:d instanceof Error?d.message:String(d||""),at:new Date().toISOString()}),s>=t||!Le(d))break;try{const a=await y(e.helper,e.options.traceId),l=await I(e.helper,a,e.options.traceId),c=await x({helper:e.helper,window:l,binding:e.binding,runtime:e.runtime,api:k({serverUrl:e.options.serverUrl}),workDir:e.options.workDir,traceId:e.options.traceId});o.push({phase:"recover",attempt:s,ok:c.opened,reason:c.reason,windowId:l.windowId,at:new Date().toISOString()})}catch(a){o.push({phase:"recover",attempt:s,ok:!1,errorSummary:a instanceof Error?a.message:String(a||""),at:new Date().toISOString()})}m(i.join(e.options.outDir,"observe-retry-events.json"),o),await V("observe-retry",`${e.options.traceId}:observe-retry:${s}`,1500*s)}throw o.length&&m(i.join(e.options.outDir,"observe-retry-events.json"),o),r}function Le(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 He(e){const t=await W(e,3),n=T(t.messages),r=i.join(e.options.outDir,"bottom-page.md"),o=S(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return m(i.join(e.options.outDir,"bottom-page-summary.json"),n),m(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(r,o),{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:r,markdown:o}}}async function Ue(e){const t=await W(e,3,{observePipeline:"edge-structure",persistLocalLedger:!0});O(e.options.outDir,"download-visible",t.observationEvidence);const n=T(t.messages),r=i.join(e.options.outDir,"download-visible.md"),o=S(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=K(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});m(i.join(e.options.outDir,"bottom-page-summary.json"),n),m(i.join(e.options.outDir,"download-visible-summary.json"),s),m(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(r,o);const d=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),a=s.ok||d;return{step:"download-visible-media",ok:a,reasonCode:a?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:d,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:r,markdown:o,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 W(e,3,{observePipeline:"server-observe"});O(e.options.outDir,"server-observe",t.observationEvidence),m(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),r=S(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,r);const o=ne(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:H(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:o,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:r}}}async function Ke(e){const t=await X(e,{observePipeline:"edge-structure",downloadMedia:!1});O(e.options.outDir,"structure-window",t.observationEvidence),m(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),r=S(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,r);const o=ne(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:H(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:o,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:r}}}async function X(e,t={}){const n=Se({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),r=k({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await r.upsertRuntime(e.runtime):await r.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const o=i.join(e.options.outDir,"inbound-attachments");p.mkdirSync(o,{recursive:!0});const s=t.persistLocalLedger?pt(e.options):void 0,d=s?ge(s,e.runtime.runtimeId):void 0;let a,l,c;const w=await ve({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:r,workDir:e.options.workDir,attachmentsDir:o,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,activityGuard:e.options.activityGuard,observePipeline:t.observePipeline,localLedgerTailAnchors:d?bt(d.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:b})=>{a=Ve(u),l=ze(b),c=Ge(u,b,{captureHash:a,ocrHash:l})}});let h,g;if(d&&s){const u=pe({ledger:d,bindingId:e.binding.bindingId,observedMessages:w});h=u.binding,g=se({bindingId:e.binding.bindingId,revision:h.revision,messages:u.newMessages}),we(s,d)}return{messages:w,attachmentsDir:o,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...h?{ledgerBinding:h}:{},traceHashes:{...a?{captureHash:a}:{},...l?{ocrHash:l}:{},...g?{ingestPayloadHash:g}:{}},...c?{observationEvidence:c}:{}}}function Ge(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(r=>({text:r.text,...r.bbox?{bbox:r.bbox}:{},...typeof r.confidence=="number"?{confidence:r.confidence}:{}}))}}}function O(e,t,n){if(!n)return;const r=n.capture.dataBase64?D(e,`${t}-observation-window`,n.capture):void 0;m(i.join(e,`${t}-observation-evidence.json`),{...n.report,...r?{screenshotPath:r}:{}})}function Ve(e){if(e.dataBase64)return`sha256:${M.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function ze(e){return se({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 Z(e,t){let n=t,r=0,o=0,s="";const d=[];return{actionLog:d,setScenario(l){n=l,r=0,o=0},async request(l,c,w){if(d.push(l),l==="activity.snapshot"){if(r+=1,r<=n.preflightBusySnapshots)return{id:`human-${r}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:w};if(n.forceIdleAfterBusy)return{id:`human-${r}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:w}}if(l==="automation.lease.acquire"){const h=await e.request(l,c,w),g=h.result&&typeof h.result=="object"?h.result:{};return s=typeof g.leaseId=="string"?g.leaseId:"",h}return l==="automation.lease.status"?(o+=1,n.simulateInterruptionAtLeaseStatus===o?(d.push("automation.lease.simulateInterruption"),{id:`human-lease-${o}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:w}):{id:`human-lease-${o}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:w}):e.request(l,c,w)}}}function Y(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 Je(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Qe(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 Xe(e){try{const t=await y(e.helper,e.traceId),n=await j(e.helper,t.windowId,e.traceId,t.bounds),r=D(e.outDir,e.filePrefix,n),o=await E(e.helper,n,e.traceId);m(i.join(e.outDir,`${e.filePrefix}-ocr.json`),{screenshotPath:r,ocr:o})}catch(t){m(i.join(e.outDir,`${e.filePrefix}-error.json`),{errorSummary:t instanceof Error?t.message:String(t||"after paste evidence failed")})}}async function F(e){const t=await y(e.helper,e.traceId),n=await I(e.helper,t,e.traceId);if(!(await x({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const o=await j(e.helper,n.windowId,e.traceId),s=D(e.outDir,`${e.filePrefix}-observe-marker`,o),d=await E(e.helper,o,e.traceId);return m(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:d}),Ye(d.blocks||[],e.marker)}function Ze(e,t){const n=t.trim();if(!n)return 0;const r=e.map(d=>d.text||"").join(`
4
+ `),o=te(r,n);if(o>0)return o;const s=ee(n);return s.length<8?0:te(ee(r),s)}function Ye(e,t){const n=e.filter(et);return Ze(n,t)}function et(e){const t=e.bbox;if(!t)return!0;const n=Number(t.x),r=Number(t.y),o=Number(t.width);return[n,r,o].every(Number.isFinite)?n+o/2>=560&&r>=100:!0}function ee(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function te(e,t){if(!t)return 0;let n=0,r=0;for(;;){const o=e.indexOf(t,r);if(o<0)break;n+=1,r=o+t.length}return n}async function tt(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let r=0;for(let o=0;o<t;o+=1){if(r=await F({helper:e.helper,runtime:e.runtime,binding:e.binding,api:e.api,workDir:e.workDir,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${o+1}`,settleToBottom:e.settleToBottom}),n==null?r>0:r>n)return r;o<t-1&&await q(800)}return r}function nt(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 rt(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 ot(e){return ye({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 st(e,t){const n=ue({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new he({helperPath:n.helperPath,expectedHelperVersion:n.version||me,requiredCapabilities:ct(e),guardUnsafeWindowsCommands:(e.platform??process.platform)==="win32",skipUserActivityGuard:!0,cleanupWindowsOverlays:(e.platform??process.platform)==="win32",requestLogger:at(t)})}function at(e){return t=>{p.appendFileSync(e,`${JSON.stringify(it(t))}
5
+ `)}}function it(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=$(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=$(e.result)),t}function $(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return dt(t,e)?{type:"string",length:e.length,sha256:M.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(d=>$(d,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 r=e,o={};for(const[s,d]of Object.entries(r))o[s]=$(d,s,n+1);return o}function dt(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function ct(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?B("send"):e.steps.some(t=>t==="download-visible-media")?B("download"):B("observe")}function lt(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 ut(e,t){const n=i.join(t.outDir,"fixtures");p.mkdirSync(n,{recursive:!0});const r=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);if(e==="send-text")return{text:t.text||`codex-product-action text ${r}`};if(e==="send-file"){const o=t.filePath||i.join(n,`wechat-action-smoke-${r}.txt`);return t.filePath||p.writeFileSync(o,`Shennian WeChat product action smoke
6
+ ${r}
7
+ `,"utf8"),{text:`codex-product-action file ${r}`,attachmentPath:o}}if(e==="send-image"){const o=t.imagePath||i.join(n,`wechat-action-smoke-${r}.png`);return t.imagePath||vt(o),{text:`codex-product-action image ${r}`,attachmentPath:o}}if(e==="send-video"){const o=t.videoPath||i.join(n,`wechat-action-smoke-${r}.mp4`);return!t.videoPath&&!yt(o)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${r}`,attachmentPath:o}}throw new Error(`step ${e} is not a send step`)}function ne(e){const t=e.flatMap(n=>mt(n.mediaMetadata));return{count:t.length,byAvailability:H(t.map(n=>re(n,"availability")||"unknown")),localPaths:t.map(n=>re(n,"localPath")).filter(n=>!!n)}}function mt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(oe):oe(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function ht(e){const t=f(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function ft(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 gt(e,t){const n=[];for(let r=0;r<e.length;r+=1)e[r]===t&&e[r+1]&&n.push(e[r+1]);return n}function f(e,t){const n=e.indexOf(t),r=n>=0?e[n+1]:void 0;return r&&!r.startsWith("--")?r:void 0}function L(e){return e?i.resolve(e):void 0}function wt(e){return Array.from(new Set(e))}function D(e,t,n){const r=n.mimeType.includes("jpeg")?".jpg":".png",o=i.join(e,`${t}${r}`);return p.writeFileSync(o,Buffer.from(n.dataBase64,"base64")),o}function m(e,t){p.mkdirSync(i.dirname(e),{recursive:!0}),p.writeFileSync(e,`${JSON.stringify(t,null,2)}
8
+ `,"utf8")}function A(e,t){p.mkdirSync(i.dirname(e),{recursive:!0}),p.writeFileSync(e,t,"utf8")}function pt(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function bt(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 vt(e){p.writeFileSync(e,Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAVElEQVR4nO3PMQ0AMAgAMUTof2kOKWzo8mCqkmeX9waYtwOwG4DbANwG4DYAtwG4DcBtAG4DcBuA2wDcBuA2ALcBuA3AbQBuA3AbgNsA3AbgNgC3AbgNwG0AbgNwG8BPDyCqAzmh3yHRAAAAAElFTkSuQmCC","base64"))}function yt(e){return de("ffmpeg",["-y","-f","lavfi","-i","testsrc=size=160x120:rate=5","-t","1","-pix_fmt","yuv420p",e],{encoding:"utf8",stdio:"ignore",timeout:2e4}).status===0&&p.existsSync(e)}function H(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function re(e,t){return typeof e[t]=="string"?e[t].trim():""}function oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){return M.createHash("sha256").update(e).digest("hex").slice(0,16)}function se(e){return`sha256:${M.createHash("sha256").update(St(e)).digest("hex")}`}function St(e){return JSON.stringify(U(e))}function U(e){if(Array.isArray(e))return e.map(U);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,U(t[n])]))}function kt(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 Ct(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 q(e){return new Promise(t=>setTimeout(t,e))}function It(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 xt(){const e=Ie(process.argv.slice(2)),t=await De(e);It(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])===ce(import.meta.url)&&xt().catch(e=>{process.stderr.write(`${e instanceof Error?e.stack||e.message:String(e)}
10
+ `),process.exit(1)});export{T as buildBottomPageReport,K as buildDownloadVisibleMediaReport,Oe as buildSendAndConfirmEvidence,$e as buildSendStepAcceptance,Ye as countMarkerOccurrencesInChatContentBlocks,Ze as countMarkerOccurrencesInOcrBlocks,Ne as nextSmokeDrainWaitMs,Ie as parseWeChatChannelActionSmokeArgs,Se as resolveWeChatSmokeObserveSessionMode,De as runWeChatChannelActionSmoke,xe as stepsForMode};
package/dist/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{Command as V}from"commander";import u from"chalk";import M from"node:os";import R from"node:fs";import{loadConfig as w,saveConfig as G,getConfigPath as J,getShennianDir as j,resolveShennianPath as A}from"./config/index.js";import{CliRelayClient as H}from"./relay/client.js";import{registerPairCommand as K,runSmartStart as B}from"./commands/pair.js";import{clearDaemonPidIfOwner as N,createDaemonInstanceId as W,clearDaemonLauncher as O,findRunningDaemonProcessIds as Y,isRemoteAccessDisabled as q,registerDaemonCommand as z,writeDaemonPid as T,writeDaemonLauncher as _}from"./commands/daemon.js";import{registerAgentCommand as Q}from"./commands/agent.js";import{registerManagerCommand as X}from"./commands/manager.js";import{registerExternalCommand as Z}from"./commands/external.js";import{registerWeChatCommand as ee}from"./commands/wechat.js";import{registerRuntimeCommand as ne}from"./commands/runtime.js";import{registerUpgradeCommand as te}from"./commands/upgrade.js";import{registerToolsCommand as oe}from"./commands/tools.js";import{SessionManager as re}from"./session/manager.js";import{SERVERS as k,regionToUrl as se,urlToRegion as ae}from"./region.js";import{getCurrentVersion as P,handleStartupCrashCheck as ie,checkForUpdate as ce,isUpgradeVersionInCooldown as le,recordUpgradeFailure as me}from"./upgrade/engine.js";import{detectAgents as pe}from"./agents/detect.js";import{augmentProcessPath as L}from"./env-path.js";L();const b=P(),de=3e4,ge=5*6e4;import{getCachedAgentInfos as ue,resolveAgentInfos as fe}from"./agents/model-registry.js";import{initCliLogReporter as he,reportLog as v}from"./log-reporter.js";import{isNativeFusionEnabled as ve}from"./native-fusion/config.js";import{NativeSessionFusionService as Se}from"./native-fusion/service.js";import{startDaemonLogRetention as Ie}from"./daemon-log.js";const we=j(),De=A("daemon.pid");function ye(){try{const n=R.readFileSync(De,"utf-8").trim();if(n.startsWith("{")){const o=JSON.parse(n),r=Number(o.pid);return Number.isInteger(r)&&r>0?r:null}const t=parseInt(n,10);return Number.isInteger(t)&&t>0?t:null}catch{return null}}function $e(n){return n.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function Re(n){const t=[`trigger=${n.trigger??"unknown"}`,`phase=${n.phase}`,`code=${n.code??"unknown"}`,`attempt=${n.reconnectAttempt}`];return n.reason&&t.push(`reason=${n.reason}`),n.error&&t.push(`error=${n.error}`),t.join(" ")}async function x(n,t=5e3){const o=Date.now();for(;Date.now()-o<t;){try{process.kill(n,0)}catch{return!0}await new Promise(r=>setTimeout(r,100))}try{return process.kill(n,0),!1}catch{return!0}}const a=new V;a.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(b),a.option("--api <url>","Server URL override").option("--name <name>","Machine name",M.hostname()).action(async n=>{const t=w(),o=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??t.serverUrl??void 0;await B(o,n.name)}),a.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async n=>{const t=A("env.json");try{const e=JSON.parse(R.readFileSync(t,"utf-8"));for(const[g,m]of Object.entries(e))process.env[g]||(process.env[g]=m);L()}catch{}const o=Ie();q()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(o),process.exit(0));const r=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),f=async(e,g)=>{console.log(`[${new Date().toISOString()}] ${g} (PID ${e})`),process.kill(e,"SIGTERM"),await x(e)||(process.kill(e,"SIGKILL"),await x(e,2e3))};try{const e=ye();if(e&&e!==process.pid)try{process.kill(e,0),r?await f(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const p=Y(process.pid);if(p.length>0)if(r)for(const e of p)try{await f(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${p[0]}), skipping duplicate start`),process.exit(0);const i=W();T(process.pid,i,{version:b}),_(process.pid,void 0,i),process.on("exit",()=>{N(process.pid,i),O(i)}),await ie()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));const s=w();s.machineToken||(console.error(u.red("\u2717 Not paired yet. Run: shennian")),process.exit(1));const c=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??k.cn.url,S=`${$e(c)}/relay/machine`,d=P(),y=pe(),h=y.map(e=>e.type),$=ue(y);s.machineId&&he(c,s.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${S}... (v${d}) agents: ${h.join(",")}`),v({level:"info",wsEvent:"daemon.start",metadata:{version:d,agents:h}});let I=null;const l=new H({serverUrl:S,machineToken:s.machineToken,cliVersion:d,agentList:h,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),v({level:"info",wsEvent:"daemon.connected"}),$.some(e=>e.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:$}}),fe(y,{serverUrl:s.serverUrl??c,authToken:s.machineToken??s.accessToken}).then(e=>{JSON.stringify(e)!==JSON.stringify($)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:e}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:e,clearUpgradeAttempt:g})=>{const m=e();g(),m&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${m.from} \u2192 ${m.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:m.from,to:m.to}}))}).catch(()=>{}),ke(l,s.autoUpgrade??"patch",d),I?.handleConnected()},onDisconnected:e=>{console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Re(e)}`),v({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),v({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),C.handleReq(e)}});I=ve()?new Se(l):null;const C=new re(l,I,d);R.mkdirSync(we,{recursive:!0}),T(process.pid,i,{version:d}),_(process.pid,void 0,i),l.connect(),process.stdin.resume();const U=()=>{console.log(u.gray(`
2
- Disconnecting...`)),v({level:"info",wsEvent:"daemon.stop"}),clearInterval(o),C.cleanup(),I?.stop(),l.disconnect(),N(process.pid,i),O(i),process.exit(0)};process.on("SIGINT",U),process.on("SIGTERM",U)});const F=a.command("config").description("View or modify configuration");F.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${J()}`);const t={...w()};if(t.machineToken&&(t.machineToken=t.machineToken.slice(0,12)+"..."),t.serverUrl){const o=ae(t.serverUrl);console.log(`Region: ${o} (${k[o].label})`)}console.log(JSON.stringify(t,null,2))}),F.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((n,t)=>{const o=w();if(n==="server"){t!=="cn"&&t!=="global"&&(console.error(u.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const r=t;o.serverUrl=se(r),G(o),console.log(u.green(`\u2713 Server set to ${k[r].label}`)),console.log(u.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(u.red(`\u2717 Unknown config key: ${n}. Supported: server`)),process.exit(1)}),K(a),z(a),Q(a),X(a),Z(a),ee(a),ne(a),te(a),oe(a),a.parse();async function ke(n,t,o){if(t==="none")return;let r=!1;const f=async()=>{if(r)return;let p;try{p=await ce(o)}catch{return}if(!p.hasUpdate)return;const{changeType:i,current:E,latest:s}=p;if(!(t==="patch"&&i!=="patch")&&!(t==="minor"&&i==="major")&&!le(s)){console.log(`[${new Date().toISOString()}] [upgrade] ${E} \u2192 ${s} (${i}), auto-upgrading...`),r=!0;try{const{handleUpgradeStart:c}=await import("./commands/upgrade.js");await c(n,"auto-upgrade",s,{currentVersion:o})}catch(c){const D=c instanceof Error?c.message:String(c),S=me(s,D);console.log(`[${new Date().toISOString()}] [upgrade] ${s} failed, retry after ${new Date(S.nextRetryAt).toISOString()}: ${D}`)}finally{r=!1}}};setTimeout(()=>{f()},de),setInterval(()=>{f()},ge)}
1
+ import{Command as M}from"commander";import u from"chalk";import G from"node:os";import R from"node:fs";import{loadConfig as I,saveConfig as J,getConfigPath as j,getShennianDir as H,resolveShennianPath as N}from"./config/index.js";import{CliRelayClient as K}from"./relay/client.js";import{registerPairCommand as B,runSmartStart as W}from"./commands/pair.js";import{clearDaemonPidIfOwner as O,createDaemonInstanceId as Y,clearDaemonLauncher as T,findRunningDaemonProcessIds as q,isRemoteAccessDisabled as z,registerDaemonCommand as Q,writeDaemonPid as _,writeDaemonLauncher as P}from"./commands/daemon.js";import{registerAgentCommand as X}from"./commands/agent.js";import{registerManagerCommand as Z}from"./commands/manager.js";import{registerExternalCommand as ee}from"./commands/external.js";import{registerWeChatCommand as ne}from"./commands/wechat.js";import{registerRuntimeCommand as te}from"./commands/runtime.js";import{registerUpgradeCommand as oe}from"./commands/upgrade.js";import{registerToolsCommand as re}from"./commands/tools.js";import{SessionManager as se}from"./session/manager.js";import{SERVERS as k,regionToUrl as ae,urlToRegion as ie}from"./region.js";import{getCurrentVersion as L,handleStartupCrashCheck as ce,checkForUpdate as le,isUpgradeVersionInCooldown as me,recordUpgradeFailure as pe}from"./upgrade/engine.js";import{detectAgents as de}from"./agents/detect.js";import{augmentProcessPath as b}from"./env-path.js";b();const x=L(),ge=3e4,ue=5*6e4;import{getCachedAgentInfos as fe,resolveAgentInfos as he}from"./agents/model-registry.js";import{initCliLogReporter as ve,reportLog as v}from"./log-reporter.js";import{isNativeFusionEnabled as Se}from"./native-fusion/config.js";import{NativeSessionFusionService as we}from"./native-fusion/service.js";import{startDaemonLogRetention as Ie}from"./daemon-log.js";const ye=H(),De=N("daemon.pid");function $e(){try{const n=R.readFileSync(De,"utf-8").trim();if(n.startsWith("{")){const o=JSON.parse(n),r=Number(o.pid);return Number.isInteger(r)&&r>0?r:null}const t=parseInt(n,10);return Number.isInteger(t)&&t>0?t:null}catch{return null}}function Re(n){return n.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function ke(n){const t=[`trigger=${n.trigger??"unknown"}`,`phase=${n.phase}`,`code=${n.code??"unknown"}`,`attempt=${n.reconnectAttempt}`];return n.reason&&t.push(`reason=${n.reason}`),n.error&&t.push(`error=${n.error}`),t.join(" ")}async function F(n,t=5e3){const o=Date.now();for(;Date.now()-o<t;){try{process.kill(n,0)}catch{return!0}await new Promise(r=>setTimeout(r,100))}try{return process.kill(n,0),!1}catch{return!0}}const a=new M;a.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(x),a.option("--api <url>","Server URL override").option("--name <name>","Machine name",G.hostname()).action(async n=>{const t=I(),o=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??t.serverUrl??void 0;await W(o,n.name)}),a.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async n=>{const t=N("env.json");try{const e=JSON.parse(R.readFileSync(t,"utf-8"));for(const[g,m]of Object.entries(e))process.env[g]||(process.env[g]=m);b()}catch{}const o=Ie();z()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(o),process.exit(0));const r=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),f=async(e,g)=>{console.log(`[${new Date().toISOString()}] ${g} (PID ${e})`),process.kill(e,"SIGTERM"),await F(e)||(process.kill(e,"SIGKILL"),await F(e,2e3))};try{const e=$e();if(e&&e!==process.pid)try{process.kill(e,0),r?await f(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const p=q(process.pid);if(p.length>0)if(r)for(const e of p)try{await f(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${p[0]}), skipping duplicate start`),process.exit(0);const i=Y();_(process.pid,i,{version:x}),P(process.pid,void 0,i),process.on("exit",()=>{O(process.pid,i),T(i)}),await ce()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));const s=I();s.machineToken||(console.error(u.red("\u2717 Not paired yet. Run: shennian")),process.exit(1));const c=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??k.cn.url,S=`${Re(c)}/relay/machine`,d=L(),D=de(),h=D.map(e=>e.type),$=fe(D);s.machineId&&ve(c,s.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${S}... (v${d}) agents: ${h.join(",")}`),v({level:"info",wsEvent:"daemon.start",metadata:{version:d,agents:h}});let w=null;const l=new K({serverUrl:S,machineToken:s.machineToken,cliVersion:d,agentList:h,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),v({level:"info",wsEvent:"daemon.connected"}),$.some(e=>e.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:$}}),he(D,{serverUrl:s.serverUrl??c,authToken:s.machineToken??s.accessToken}).then(e=>{JSON.stringify(e)!==JSON.stringify($)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:e}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:e,clearUpgradeAttempt:g})=>{const m=e();g(),m&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${m.from} \u2192 ${m.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:m.from,to:m.to}}))}).catch(()=>{}),Ee(l,s.autoUpgrade??"patch",d),w?.handleConnected()},onDisconnected:e=>{console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${ke(e)}`),v({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),v({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),C.handleReq(e)}});w=Se()?new we(l):null;const C=new se(l,w,d);R.mkdirSync(ye,{recursive:!0}),_(process.pid,i,{version:d}),P(process.pid,void 0,i),l.connect(),process.stdin.resume();let U=!1;const A=async()=>{if(!U){U=!0,console.log(u.gray(`
2
+ Disconnecting...`)),v({level:"info",wsEvent:"daemon.stop"}),clearInterval(o);try{await C.cleanup(),w?.stop(),l.disconnect(),O(process.pid,i),T(i)}finally{process.exit(0)}}};process.on("SIGINT",()=>{A()}),process.on("SIGTERM",()=>{A()})});const V=a.command("config").description("View or modify configuration");V.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${j()}`);const t={...I()};if(t.machineToken&&(t.machineToken=t.machineToken.slice(0,12)+"..."),t.serverUrl){const o=ie(t.serverUrl);console.log(`Region: ${o} (${k[o].label})`)}console.log(JSON.stringify(t,null,2))}),V.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((n,t)=>{const o=I();if(n==="server"){t!=="cn"&&t!=="global"&&(console.error(u.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const r=t;o.serverUrl=ae(r),J(o),console.log(u.green(`\u2713 Server set to ${k[r].label}`)),console.log(u.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(u.red(`\u2717 Unknown config key: ${n}. Supported: server`)),process.exit(1)}),B(a),Q(a),X(a),Z(a),ee(a),ne(a),te(a),oe(a),re(a),a.parse();async function Ee(n,t,o){if(t==="none")return;let r=!1;const f=async()=>{if(r)return;let p;try{p=await le(o)}catch{return}if(!p.hasUpdate)return;const{changeType:i,current:E,latest:s}=p;if(!(t==="patch"&&i!=="patch")&&!(t==="minor"&&i==="major")&&!me(s)){console.log(`[${new Date().toISOString()}] [upgrade] ${E} \u2192 ${s} (${i}), auto-upgrading...`),r=!0;try{const{handleUpgradeStart:c}=await import("./commands/upgrade.js");await c(n,"auto-upgrade",s,{currentVersion:o})}catch(c){const y=c instanceof Error?c.message:String(c),S=pe(s,y);console.log(`[${new Date().toISOString()}] [upgrade] ${s} failed, retry after ${new Date(S.nextRetryAt).toISOString()}: ${y}`)}finally{r=!1}}};setTimeout(()=>{f()},ge),setInterval(()=>{f()},ue)}
@@ -1,24 +1,24 @@
1
- import U from"node:http";import{randomBytes as K,randomUUID as p}from"node:crypto";import k from"node:fs";import x from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as H,extractPayloadText as G,formatExternalConversationText as j,formatExternalMessageLine as z,formatExternalAttachmentReference as J,isAgentHiddenPayload as Y,isToolPayload as V}from"@shennian/wire";import{ManagerRegistry as Q}from"./registry.js";import{readMessages as X}from"../session/store.js";import{ChannelRuntime as Z}from"../channels/runtime.js";import{splitExternalReplyText as ee}from"../channels/reply-split.js";import{resolveShennianPath as te}from"../config/index.js";import{buildExternalChannelInstructions as ne}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as re,sendDirectWeChatMessageOnce as ae}from"../commands/wechat.js";import{createWeChatAutomationLane as se}from"../channels/wechat-channel/automation-lane.js";const C=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024);let N=null;function Be(r){N=r}function qe(){return N}function S(r){return y.resolve(r||x.homedir())}function u(r,e,a){r.writeHead(e,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(a))}function ie(r){return/^agent-(.+)-\d+$/.exec(r)?.[1]??null}function oe(r){return r==="manager"?!1:r.startsWith("custom:")?!0:H.includes(r)}function ce(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 D(r){return r.replace(/\r\n/g,`
2
- `).trim()}function le(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 P(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 de(r,e){const a=[...r].sort((d,c)=>d.ts-c.ts),n=[];let t=null,i=null,o=null,s="";const l=()=>{if(!t)return;const d=s.trim();d&&n.push({...t,id:`${t.id}-compact`,payload:d}),t=null,i=null,o=null,s=""};for(const d of a){if(Y(d.payload)){l();continue}if(d.role==="user"){l(),n.push(d);continue}if(V(d.payload)){l(),n.push({...d,payload:le(d.payload)});continue}const c=G(d.payload);if(!c.trim())continue;const h=ie(d.id),g=ce(d.id);t&&t.role===d.role&&i===h&&h&&g!==null&&o!==null&&g===o+1?(s+=c,t.ts=d.ts,o=g):(l(),t=d,i=h,o=g,s=c)}return l(),n.slice(-e).sort((d,c)=>c.ts-d.ts)}async function ue(r){const e=[];let a=0;for await(const t of r){const i=Buffer.from(t);if(a+=i.byteLength,Number.isFinite(C)&&C>0&&a>C)throw new Error(`Manager IPC request body is too large. Max: ${C} 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 v(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function $(){return he()?y.join(x.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):te("runtime","manager-ipc.json")}function he(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Fe{opts;registry=new Q;channelRuntime;server=null;ipcUrl=null;ipcToken=K(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??se(),this.channelRuntime=e.channelRuntime??new Z((a,n)=>{this.handleExternalMessage(a,n)},a=>this.registry.createReplyTarget(a).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=U.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=$();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=$(),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=D(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=D(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",d=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const c=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:c});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"wechat-rpa",{includeSecret:!0})});return}if(s){const c=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:B(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:c});return}if(l){const c=await this.channelRuntime.syncManagerWeChatRpaChannel(t);this.broadcastManagerChannelStatus(t),w(a,e.id,!0,c);return}if(d){const c=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,c);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 ue(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(!oe(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 d=String(t.message||"");if(!d)throw new Error("message is required");const c=this.registry.addWorker({managerSessionId:i,agentType:s,workDir:l,summary:d.slice(0,120)}),h=String(t.modelId||(s===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(c.sessionId,s,l,d,null,h),u(a,200,{ok:!0,session:c});return}if(n.pathname==="/sessions/send"){const s=String(t.sessionId||""),l=String(t.message||""),d=t.enqueue===void 0?!0:!!t.enqueue,c=this.registry.getWorkerForManager(i,s);if(!c)throw new Error("Worker not found in this manager scope");const h=String(t.modelId||(c.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));d?await this.dispatchChatEnqueue(c.sessionId,c.agentType,c.workDir,l,c.agentSessionId??null,h):await this.dispatchChatSend(c.sessionId,c.agentType,c.workDir,l,c.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 d=this.opts.getRuntime().chatQueue?.getSnapshot(s);u(a,200,{ok:!0,queue:d});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-${p()}`,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-${p()}`,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-${p()}`,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 c=X(s,{limit:Math.max(l*20,l)});u(a,200,{ok:!0,messages:de(c,l),rawMessageCount:c.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||""),d=P(t.attachment),c=String(t.idempotencyKey||p()),h=String(t.channelId||""),g=String(t.conversationId||""),R=!s&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(i).catch(()=>null):null,M=s?.channelId||h||R?.channelId||"",b=s?.conversationId||g||R?.conversationId||"";if(M&&this.channelRuntime.getChannelById(M)){if(!b)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:i,channelId:M,conversationId:b,messageId:s?.messageId??void 0,text:l,attachment:d,idempotencyKey:c});u(a,f.ok?200:400,f);return}let I;try{I=await this.sendManagedWeComReply({managerSessionId:i,text:l,attachment:d,idempotencyKey:c})}catch(f){const T=f instanceof Error?f.message:String(f);if(!v(T))throw f;I={ok:!1,error:T}}if(I.ok){u(a,200,{ok:!0,payload:I.payload});return}if(!v(I.error||"")||!M||!b){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,d=typeof t.traceId=="string"?t.traceId:void 0;let c;try{c=await this.weChatAutomationLane.run(`wechat-tool:read:${s.channelId}`,()=>re(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,limit:l,recentLimit:l,download:t.download==="never"?"never":"auto",traceId:d,timeoutMs:m(t.timeoutMs)}))}catch(h){u(a,400,{ok:!1,...O(h,d)});return}u(a,200,{ok:!0,messages:c.messages,outDir:c.outDir,helperTracePath:c.helperTracePath,activityGuardPath:c.activityGuardPath});return}if(n.pathname==="/wechat-rpa/tool/send"){const s=W(t),l=String(t.text||""),d=P(t.attachment),c=typeof t.traceId=="string"?t.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${s.channelId}`,()=>ae(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,text:l,attachment:d,traceId:c,timeoutMs:m(t.timeoutMs)}))}catch(g){u(a,400,{ok:!1,...O(g,c)});return}u(a,200,{ok:!0,...h});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:B(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-${p()}`,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-${p()}`,method:"chat.enqueue",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async sendManagedWeComReply(e){const a=ee(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-${p()}`,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-${p()}`,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
1
+ import K from"node:http";import{randomBytes as G,randomUUID as p}from"node:crypto";import k from"node:fs";import N from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as H,extractPayloadText as j,formatExternalConversationText as z,formatExternalMessageLine as J,formatExternalAttachmentReference as Y,isAgentHiddenPayload as V,isToolPayload as Q}from"@shennian/wire";import{ManagerRegistry as X}from"./registry.js";import{readMessages as Z}from"../session/store.js";import{ChannelRuntime as ee}from"../channels/runtime.js";import{splitExternalReplyText as te}from"../channels/reply-split.js";import{resolveShennianPath as ne}from"../config/index.js";import{buildExternalChannelInstructions as re}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as ae,sendDirectWeChatMessageOnce as se}from"../commands/wechat.js";import{createWeChatAutomationLane as ie}from"../channels/wechat-channel/automation-lane.js";const C=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),A=12*6e4;let P=null;function qe(r){P=r}function Fe(){return P}function S(r){return y.resolve(r||N.homedir())}function u(r,e,a){r.writeHead(e,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(a))}function oe(r){return/^agent-(.+)-\d+$/.exec(r)?.[1]??null}function le(r){return r==="manager"?!1:r.startsWith("custom:")?!0:H.includes(r)}function ce(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 D(r){return r.replace(/\r\n/g,`
2
+ `).trim()}function de(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 W(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||""),c=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(c)||c<0)&&!(!o&&!s))return{kind:a,name:n,mimeType:t,size:c,...o?{localPath:o}:{},...s?{url:s}:{}}}function v(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 ue(r,e){const a=[...r].sort((d,l)=>d.ts-l.ts),n=[];let t=null,i=null,o=null,s="";const c=()=>{if(!t)return;const d=s.trim();d&&n.push({...t,id:`${t.id}-compact`,payload:d}),t=null,i=null,o=null,s=""};for(const d of a){if(V(d.payload)){c();continue}if(d.role==="user"){c(),n.push(d);continue}if(Q(d.payload)){c(),n.push({...d,payload:de(d.payload)});continue}const l=j(d.payload);if(!l.trim())continue;const h=oe(d.id),g=ce(d.id);t&&t.role===d.role&&i===h&&h&&g!==null&&o!==null&&g===o+1?(s+=l,t.ts=d.ts,o=g):(c(),t=d,i=h,o=g,s=l)}return c(),n.slice(-e).sort((d,l)=>l.ts-d.ts)}async function he(r){const e=[];let a=0;for await(const t of r){const i=Buffer.from(t);if(a+=i.byteLength,Number.isFinite(C)&&C>0&&a>C)throw new Error(`Manager IPC request body is too large. Max: ${C} 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 $(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function _(){return ge()?y.join(N.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):ne("runtime","manager-ipc.json")}function ge(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Le{opts;registry=new X;channelRuntime;server=null;ipcUrl=null;ipcToken=G(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??ie(),this.channelRuntime=e.channelRuntime??new ee((a,n)=>{this.handleExternalMessage(a,n)},a=>this.registry.createReplyTarget(a).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=K.createServer((a,n)=>{this.handleIpc(a,n)}),this.server.requestTimeout=A,this.server.timeout=A,this.server.keepAliveTimeout=A,this.server.headersTimeout=A+5e3,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=_();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=_(),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 c=D(s);c&&(t.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(a.state==="final"||a.state==="error"||a.state==="aborted"){t.status=a.state;const s=D(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",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",d=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const l=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:l});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"wechat-rpa",{includeSecret:!0})});return}if(s){const l=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:B(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:q(n.source),pollIntervalMs:m(n.pollIntervalMs),recentLimit:m(n.recentLimit),idleSeconds:m(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,selfTriggerMarker:typeof n.selfTriggerMarker=="string"?n.selfTriggerMarker:void 0,deferInitialPoll:n.deferInitialPoll===void 0?void 0:!!n.deferInitialPoll,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:l});return}if(c){const l=await this.channelRuntime.syncManagerWeChatRpaChannel(t);this.broadcastManagerChannelStatus(t),w(a,e.id,!0,l);return}if(d){const l=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,l);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 he(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(!le(s))throw new Error(`Unsupported manager worker agent: ${s}`);const c=S(String(t.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const d=String(t.message||"");if(!d)throw new Error("message is required");const l=this.registry.addWorker({managerSessionId:i,agentType:s,workDir:c,summary:d.slice(0,120)}),h=String(t.modelId||(s===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(l.sessionId,s,c,d,null,h),u(a,200,{ok:!0,session:l});return}if(n.pathname==="/sessions/send"){const s=String(t.sessionId||""),c=String(t.message||""),d=t.enqueue===void 0?!0:!!t.enqueue,l=this.registry.getWorkerForManager(i,s);if(!l)throw new Error("Worker not found in this manager scope");const h=String(t.modelId||(l.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));d?await this.dispatchChatEnqueue(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h):await this.dispatchChatSend(l.sessionId,l.agentType,l.workDir,c,l.agentSessionId??null,h),u(a,200,{ok:!0});return}if(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 d=this.opts.getRuntime().chatQueue?.getSnapshot(s);u(a,200,{ok:!0,queue:d});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-${p()}`,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-${p()}`,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-${p()}`,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||""),c=Number(t.limit||200);if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const l=Z(s,{limit:Math.max(c*20,c)});u(a,200,{ok:!0,messages:ue(l,c),rawMessageCount:l.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),c=String(t.text||""),d=W(t.attachment),l=String(t.idempotencyKey||p()),h=String(t.channelId||""),g=String(t.conversationId||""),M=!s&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(i).catch(()=>null):null,R=s?.channelId||h||M?.channelId||"",E=s?.conversationId||g||M?.conversationId||"";if(R&&this.channelRuntime.getChannelById(R)){if(!E)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:i,channelId:R,conversationId:E,messageId:s?.messageId??void 0,text:c,attachment:d,idempotencyKey:l});u(a,f.ok?200:400,f);return}let I;try{I=await this.sendManagedWeComReply({managerSessionId:i,text:c,attachment:d,idempotencyKey:l})}catch(f){const x=f instanceof Error?f.message:String(f);if(!$(x))throw f;I={ok:!1,error:x}}if(I.ok){u(a,200,{ok:!0,payload:I.payload});return}if(!$(I.error||"")||!R||!E){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 ${R}`});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=v(t),c=m(t.limit)??10,d=typeof t.traceId=="string"?t.traceId:void 0;let l;try{l=await this.weChatAutomationLane.run(`wechat-tool:read:${s.channelId}`,()=>ae(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,limit:c,recentLimit:c,download:t.download==="never"?"never":"auto",traceId:d,timeoutMs:m(t.timeoutMs)}))}catch(h){u(a,400,{ok:!1,...U(h,d)});return}u(a,200,{ok:!0,messages:l.messages,outDir:l.outDir,helperTracePath:l.helperTracePath,activityGuardPath:l.activityGuardPath});return}if(n.pathname==="/wechat-rpa/tool/send"){const s=v(t),c=String(t.text||""),d=W(t.attachment),l=typeof t.traceId=="string"?t.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${s.channelId}`,()=>se(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,text:c,attachment:d,traceId:l,timeoutMs:m(t.timeoutMs)}))}catch(g){u(a,400,{ok:!1,...U(g,l)});return}u(a,200,{ok:!0,...h});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 c=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:B(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:q(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:c});return}if(n.pathname==="/wechat-rpa/channel/sync"){const{channel:s,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(i);this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:s,messages:c});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-${p()}`,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-${p()}`,method:"chat.enqueue",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async sendManagedWeComReply(e){const a=te(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-${p()}`,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-${p()}`,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
5
  \u72B6\u6001\uFF1A${n}
6
6
  \u6700\u7EC8\u7ED3\u679C\uFF1A
7
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(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,d=n?.modelId||i?.modelId||"",c=ge(a.attachments,a.channelType),h=me(a);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:s,agentSessionId:l,modelId:d,text:h,attachments:c,externalChannel:we(t),replyTarget:a.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",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
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(),c=n?.agentSessionId??i?.agentSessionId??null,d=n?.modelId||i?.modelId||"",l=pe(a.attachments,a.channelType),h=fe(a);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:s,agentSessionId:c,modelId:d,text:h,attachments:l,externalChannel:Ie(t),replyTarget:a.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",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)),c=`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
12
  - \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
13
13
  - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
14
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(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-${p()}`,method:"chat.abort",params:{sessionId:e.sessionId}}));const o=n?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${n}
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:c}),o.status==="idle"&&this.dispatchChatSend(o.sessionId,"manager",o.workDir,c,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-${p()}`,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",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=>ne(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=>re(a,void 0,e,"manager")).join(`
19
19
 
20
- `).trim()}}function _(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function B(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 ge(r,e){const a=r.map(n=>q(n,e)).filter(n=>n!=null);return a.length?a:void 0}function pe(r,e,a,n){const t=e||"",i=new Set;return r.map((s,l)=>{const d=q(s,n),c=J({type:s.type,name:d?.name||s.name,path:d?.path,mimeType:d?.mimeType||s.mimeType,availability:s.availability,providerError:s.providerError||ye(s)},l),h=d?.path||"";if(h){const R=`${c}
21
- ${h}`;return i.has(R)||t.includes(h)?"":(i.add(R),`${a}: ${c}`)}const g=c;return i.has(g)?"":(i.add(g),`${a}: ${c}`)}).filter(s=>!t.includes(s)).join(`
22
- `)}function me(r){const a=(r.channelType==="wechat-rpa"?fe(r.sender.name):r.sender.name||r.sender.id)||(r.channelType==="wechat-rpa"?"\u5BF9\u65B9":r.sender.id)||"\u5BF9\u65B9",n=pe(r.attachments,r.text,a,r.channelType),i=[r.text?z({senderName:a,senderId:r.sender.id,text:r.text}):n?"":`${a}:`,n].filter(Boolean).join(`
23
- `);return[j({conversationName:r.conversationName||r.conversationId,messages:[]}),i].filter(Boolean).join(`
24
- `)}function fe(r){const e=r?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function ye(r){return!r.localPath||r.availability&&r.availability!=="edge-local"||E(r.localPath)?"":"edge-local-unavailable"}function q(r,e){return r.localPath&&Ie(r,e)&&E(r.localPath)?{path:r.localPath,name:r.name||y.basename(r.localPath)||"attachment",mimeType:r.mimeType||L(r)}:r.url&&ke(r)?{path:r.url,name:r.name||r.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:r.mimeType||L(r)}:r.thumbnailPath&&!F(r,e)&&E(r.thumbnailPath)?{path:r.thumbnailPath,name:r.name?`${r.name}-preview.png`:y.basename(r.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function we(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 Ie(r,e){return r.providerError||r.availability&&r.availability!=="edge-local"?!1:F(r,e)?r.materializationKind==="original-file"&&r.isOriginal===!0&&r.mimeKindMatches===!0&&!Se(r.localPath):!0}function ke(r){return!r.providerError&&(!r.availability||r.availability==="server-url")}function F(r,e){if(e!=="wechat-rpa")return!1;const a=String(r.type||"").toLowerCase();return a==="file"||a==="video"||a==="video-file"}function Se(r){const e=String(r||"").replace(/\\/g,"/"),a=y.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(a)}function E(r){try{return k.statSync(r).isFile()}catch{return!1}}function L(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}function O(r,e){const a=A(r,"reasonCode")||Re(r),n=A(r,"outDir"),t=A(r,"helperTracePath"),i=A(r,"activityGuardPath");return{error:r instanceof Error?r.message:String(r||a),reasonCode:a,...n?{outDir:n}:{},...t?{helperTracePath:t}:{},...i?{activityGuardPath:i}:{},...e?{traceId:e}:{}}}function A(r,e){if(!r||typeof r!="object")return"";const a=r[e];return typeof a=="string"&&a.trim()?a.trim():""}function Re(r){const e=r instanceof Error?r.message:String(r||"");return/^([a-z][a-z0-9_]*)(?::|\b)/i.exec(e.trim())?.[1]||"wechat_tool_failed"}export{Fe as ManagerRuntimeService,qe as getManagerRuntimeService,Be as setManagerRuntimeService};
20
+ `).trim()}}function B(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function q(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 pe(r,e){const a=r.map(n=>F(n,e)).filter(n=>n!=null);return a.length?a:void 0}function me(r,e,a,n){const t=e||"",i=new Set;return r.map((s,c)=>{const d=F(s,n),l=Y({type:s.type,name:d?.name||s.name,path:d?.path,mimeType:d?.mimeType||s.mimeType,availability:s.availability,providerError:s.providerError||we(s)},c),h=d?.path||"";if(h){const M=`${l}
21
+ ${h}`;return i.has(M)||t.includes(h)?"":(i.add(M),`${a}: ${l}`)}const g=l;return i.has(g)?"":(i.add(g),`${a}: ${l}`)}).filter(s=>!t.includes(s)).join(`
22
+ `)}function fe(r){const a=(r.channelType==="wechat-rpa"?ye(r.sender.name):r.sender.name||r.sender.id)||(r.channelType==="wechat-rpa"?"\u5BF9\u65B9":r.sender.id)||"\u5BF9\u65B9",n=me(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[z({conversationName:r.conversationName||r.conversationId,messages:[]}),i].filter(Boolean).join(`
24
+ `)}function ye(r){const e=r?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function we(r){return!r.localPath||r.availability&&r.availability!=="edge-local"||b(r.localPath)?"":"edge-local-unavailable"}function F(r,e){return r.localPath&&ke(r,e)&&b(r.localPath)?{path:r.localPath,name:r.name||y.basename(r.localPath)||"attachment",mimeType:r.mimeType||O(r)}:r.url&&Se(r)?{path:r.url,name:r.name||r.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:r.mimeType||O(r)}:r.thumbnailPath&&!L(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 Ie(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 ke(r,e){return r.providerError||r.availability&&r.availability!=="edge-local"?!1:L(r,e)?r.materializationKind==="original-file"&&r.isOriginal===!0&&r.mimeKindMatches===!0&&!Me(r.localPath):!0}function Se(r){return!r.providerError&&(!r.availability||r.availability==="server-url")}function L(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=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 O(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}function U(r,e){const a=T(r,"reasonCode")||Re(r),n=T(r,"outDir"),t=T(r,"helperTracePath"),i=T(r,"activityGuardPath");return{error:r instanceof Error?r.message:String(r||a),reasonCode:a,...n?{outDir:n}:{},...t?{helperTracePath:t}:{},...i?{activityGuardPath:i}:{},...e?{traceId:e}:{}}}function T(r,e){if(!r||typeof r!="object")return"";const a=r[e];return typeof a=="string"&&a.trim()?a.trim():""}function Re(r){const e=r instanceof Error?r.message:String(r||"");return/^([a-z][a-z0-9_]*)(?::|\b)/i.exec(e.trim())?.[1]||"wechat_tool_failed"}export{Le as ManagerRuntimeService,Fe as getManagerRuntimeService,qe as setManagerRuntimeService};