shennian 0.2.100 → 0.2.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +70 -25
- package/dist/src/channels/runtime.d.ts +5 -1
- package/dist/src/channels/runtime.js +3 -3
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.js +2 -2
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa.d.ts +5 -1
- package/dist/src/channels/wechat-rpa.js +4 -4
- package/dist/src/commands/runtime.d.ts +89 -0
- package/dist/src/commands/runtime.js +1 -0
- package/dist/src/commands/wechat/command.d.ts +2 -0
- package/dist/src/commands/wechat/command.js +1 -0
- package/dist/src/commands/wechat/direct.d.ts +14 -0
- package/dist/src/commands/wechat/direct.js +2 -0
- package/dist/src/commands/wechat/doctor.d.ts +2 -0
- package/dist/src/commands/wechat/doctor.js +1 -0
- package/dist/src/commands/wechat/ipc.d.ts +9 -0
- package/dist/src/commands/wechat/ipc.js +1 -0
- package/dist/src/commands/wechat/operations.d.ts +3 -0
- package/dist/src/commands/wechat/operations.js +4 -0
- package/dist/src/commands/wechat/output.d.ts +7 -0
- package/dist/src/commands/wechat/output.js +4 -0
- package/dist/src/commands/wechat/types.d.ts +141 -0
- package/dist/src/commands/wechat/types.js +1 -0
- package/dist/src/commands/wechat/utils.d.ts +14 -0
- package/dist/src/commands/wechat/utils.js +1 -0
- package/dist/src/commands/wechat.d.ts +6 -127
- package/dist/src/commands/wechat.js +1 -7
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +2 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
- package/dist/src/index.js +2 -2
- package/dist/src/manager/runtime.d.ts +3 -1
- package/dist/src/manager/runtime.js +5 -5
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildBottomPageReport, buildDownloadVisibleMediaReport } from './wechat-channel-download-report.js';
|
|
2
|
+
import { type WeChatChannelObserveActivityGuardOptions } from '../channels/wechat-channel/observer.js';
|
|
2
3
|
import { type WeChatChannelRuntimePlatform } from '../channels/wechat-channel/runtime.js';
|
|
3
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';
|
|
4
5
|
export type WeChatChannelActionSmokeOptions = {
|
|
@@ -19,6 +20,7 @@ export type WeChatChannelActionSmokeOptions = {
|
|
|
19
20
|
videoPath?: string;
|
|
20
21
|
downloadAnchorText?: string;
|
|
21
22
|
allowEmptyMedia?: boolean;
|
|
23
|
+
activityGuard?: WeChatChannelObserveActivityGuardOptions;
|
|
22
24
|
skipRuntimeUpsert?: boolean;
|
|
23
25
|
requireServer: boolean;
|
|
24
26
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import R from"node:crypto";import{spawnSync as se}from"node:child_process";import w from"node:fs";import i from"node:path";import{fileURLToPath as ae}from"node:url";import{buildBottomPageReport as D,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as ie}from"../config/index.js";import{createWeChatChannelApiClient as de}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ce,WECHAT_CHANNEL_HELPER_VERSION as ue}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as le}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as T}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as me,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as fe,saveWeChatChannelLedger as he,updateWeChatChannelBindingLedger as ge}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as U}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as z,sendQueuedWeChatOutboundRecords as C}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,ensureWeChatWindowReady as S,fallbackWeChatMessageInputPointForWindow as pe,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as we,openConversationBySearch as x,recognizeWeChatScreenshot as W}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as be}from"../channels/wechat-channel/runtime.js";function ve(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const ye="ABC",Ce="wechat-channel-action-smoke";function Se(e){const t=e.filter(l=>l!=="--"),n=ct(t),o=lt(t,"--step").map(ut).filter(l=>l!=null),r=ie(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?mt(o):ke(n);return{conversation:g(t,"--conversation")||g(t,"--group")||ye,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||Ce,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:at(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ke(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function xe(e){const t=Date.now(),n=new Date(t).toISOString();w.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");w.writeFileSync(o,"");const r=et(e),s=r.bindings[0],a=tt(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ie(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await Ae({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),h(i.join(e.outDir,"summary.json"),d)}return d}function Ie(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function Ae(e){try{return e.step==="open-read"?await Pe(e):e.step==="structure-window"?await Ke(e):e.step==="server-observe"?await qe(e):e.step==="read-bottom-page"?await Le(e):e.step==="download-visible-media"?await He(e):e.step==="send-and-confirm"?await Fe(e):e.step.startsWith("human-")?await De(e):await V(e)}catch(t){const n=bt(t),o=await _e(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:vt(n),...o?{details:o}:{}}}}async function _e(e,t,n){if(!Re(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return h(o,r),{failureDiagnosticsPath:o}}function Re(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function De(e){if(e.step==="human-observe-deferred")return await Me(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=J(e.helper,Q(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const oe=await S(s,e.options.traceId),re=await k(s,oe,e.options.traceId);return x({helper:s,window:re,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new z({helper:s,platform:e.runtime.policy.platform,openConversation:d,traceId:e.options.traceId,activityGatePolicy:e.step==="human-send-user-active-timeout"?{keyDownThresholdMs:3e4,mouseMovedThresholdMs:3e4,mouseClickThresholdMs:3e4,scrollWheelThresholdMs:3e4}:void 0});let f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ge()),f=await C({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Je(e.step,r)?await B({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?me({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],b=Xe(u,f,p.confirmedRecords),v={case:e.step,marker:n,runStatus:b,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return h(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Ye(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function Me(e){const t=J(e.helper,Q("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return h(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Pe(e){const t=await S(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await x({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await Te({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await j(e.helper,n.windowId,e.options.traceId),s=P(e.options.outDir,"open-read-full-window",r),a=await W(e.helper,r,e.options.traceId);return h(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function Te(e){try{const t=await j(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=P(e.outDir,"open-read-failure-window",t),o=await W(e.helper,t,e.traceId);h(i.join(e.outDir,"open-read-failure-ocr.json"),o),h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function V(e){return await je(e,it(e.step,e.options))}async function je(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await B({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new z({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await S(e.helper,e.options.traceId),b=await k(e.helper,u,e.options.traceId);return{...await x({helper:e.helper,window:b,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:pe(b)}}}),a=await Ee({ledger:n,bindingId:e.binding.bindingId,sender:s});await U("send-after-submit",`${e.options.traceId}:${e.step}:after-submit`);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,f=c&&t.text?await Ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=We({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:f});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:f,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function We(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Ee(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;r.waitingRecords.length>0&&s<=t&&!(Date.now()-o>=n);s+=1){const a=$e(r.waitingRecords[0]?.nextAttemptAt,o,n);await L(a),r=await C({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function $e(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function Be(e){const t=e.sendResults.filter(u=>!G(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
|
|
1
|
+
import R from"node:crypto";import{spawnSync as se}from"node:child_process";import b from"node:fs";import i from"node:path";import{fileURLToPath as ae}from"node:url";import{buildBottomPageReport as D,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as ie}from"../config/index.js";import{createWeChatChannelApiClient as de}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ce,WECHAT_CHANNEL_HELPER_VERSION as ue}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as le}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as T}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as me,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as fe,saveWeChatChannelLedger as he,updateWeChatChannelBindingLedger as ge}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as U}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as G,sendQueuedWeChatOutboundRecords as S}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,ensureWeChatWindowReady as C,fallbackWeChatMessageInputPointForWindow as pe,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as be,openConversationBySearch as x,recognizeWeChatScreenshot as W}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as we}from"../channels/wechat-channel/runtime.js";function ve(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const ye="ABC",Se="wechat-channel-action-smoke";function Ce(e){const t=e.filter(l=>l!=="--"),n=ct(t),o=lt(t,"--step").map(ut).filter(l=>l!=null),r=ie(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?mt(o):ke(n);return{conversation:g(t,"--conversation")||g(t,"--group")||ye,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||Se,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:at(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ke(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function xe(e){const t=Date.now(),n=new Date(t).toISOString();b.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");b.writeFileSync(o,"");const r=et(e),s=r.bindings[0],a=tt(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ie(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await Ae({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),h(i.join(e.outDir,"summary.json"),d)}return d}function Ie(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function Ae(e){try{return e.step==="open-read"?await Pe(e):e.step==="structure-window"?await Ke(e):e.step==="server-observe"?await qe(e):e.step==="read-bottom-page"?await Le(e):e.step==="download-visible-media"?await He(e):e.step==="send-and-confirm"?await Fe(e):e.step.startsWith("human-")?await De(e):await z(e)}catch(t){const n=wt(t),o=await _e(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:vt(n),...o?{details:o}:{}}}}async function _e(e,t,n){if(!Re(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return h(o,r),{failureDiagnosticsPath:o}}function Re(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function De(e){if(e.step==="human-observe-deferred")return await Me(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=J(e.helper,Q(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const oe=await C(s,e.options.traceId),re=await k(s,oe,e.options.traceId);return x({helper:s,window:re,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new 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:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ve()),f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Je(e.step,r)?await B({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?me({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],w=Xe(u,f,p.confirmedRecords),v={case:e.step,marker:n,runStatus:w,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return h(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Ye(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function Me(e){const t=J(e.helper,Q("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return h(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Pe(e){const t=await C(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await x({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await Te({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await j(e.helper,n.windowId,e.options.traceId),s=P(e.options.outDir,"open-read-full-window",r),a=await W(e.helper,r,e.options.traceId);return h(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function Te(e){try{const t=await j(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=P(e.outDir,"open-read-failure-window",t),o=await W(e.helper,t,e.traceId);h(i.join(e.outDir,"open-read-failure-ocr.json"),o),h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function z(e){return await je(e,it(e.step,e.options))}async function je(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await B({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new G({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await C(e.helper,e.options.traceId),w=await k(e.helper,u,e.options.traceId);return{...await x({helper:e.helper,window:w,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:pe(w)}}}),a=await Ee({ledger:n,bindingId:e.binding.bindingId,sender:s});await U("send-after-submit",`${e.options.traceId}:${e.step}:after-submit`);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,f=c&&t.text?await Ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=We({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:f});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:f,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function We(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Ee(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await 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=$e(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 $e(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function Be(e){const t=e.sendResults.filter(u=>!V(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
|
|
2
2
|
`)).join(`
|
|
3
|
-
`),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(G).length,d=E(t,"waitingRecords"),l=E(t,"failedRecords"),c=E(t,"manualReviewRecords"),f=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!f&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:f?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function G(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function E(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Fe(e){const t=["send-text","send-file","send-image","send-video"],n=[];for(const c of t)n.push(await V({...e,step:c}));await L(1200);const o=await Ne(e,3),r=D(o.messages),s=i.join(e.options.outDir,"bottom-page.md"),a=y(o.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(o.attachmentsDir)});h(i.join(e.options.outDir,"bottom-page-summary.json"),r),h(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=Be({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function Ne(e,t){let n;for(let o=1;o<=t;o+=1)try{return await I(e)}catch(r){if(n=r,o>=t||!Oe(r))break;try{const s=await S(e.helper,e.options.traceId),a=await k(e.helper,s,e.options.traceId);await x({helper:e.helper,window:a,binding:e.binding,traceId:e.options.traceId})}catch{}await U("observe-retry",`${e.options.traceId}:observe-retry:${o}`,1500*o)}throw n}function Oe(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Le(e){const t=await I(e),n=D(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function He(e){const t=await I(e,{observePipeline:"edge-structure",persistLocalLedger:!0});$(e.options.outDir,"download-visible",t.observationEvidence);const n=D(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"download-visible-summary.json"),s),h(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function qe(e){const t=await I(e,{observePipeline:"server-observe"});$(e.options.outDir,"server-observe",t.observationEvidence),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Ke(e){const t=await I(e,{observePipeline:"edge-structure",downloadMedia:!1});$(e.options.outDir,"structure-window",t.observationEvidence),h(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function I(e,t={}){const n=ve({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=de({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const r=i.join(e.options.outDir,"inbound-attachments"),s=t.persistLocalLedger?ft(e.options):void 0,a=s?fe(s,e.runtime.runtimeId):void 0;let d,l,c;const f=await we({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:o,workDir:e.options.workDir,attachmentsDir:r,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,observePipeline:t.observePipeline,localLedgerTailAnchors:a?ht(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:b})=>{d=ze(u),l=Ve(b),c=Ue(u,b,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=ge({ledger:a,bindingId:e.binding.bindingId,observedMessages:f});m=u.binding,p=ne({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),he(s,a)}return{messages:f,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function Ue(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function $(e,t,n){if(!n)return;const o=n.capture.dataBase64?P(e,`${t}-observation-window`,n.capture):void 0;h(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function ze(e){if(e.dataBase64)return`sha256:${R.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function Ve(e){return ne({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function J(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,f){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,f),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:f}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:f}):e.request(l,c,f)}}}function Q(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ge(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Je(e,t){return t.sendStatus!=="sent_unconfirmed"?!1:e==="human-send-normal"||e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"}async function B(e){const t=await S(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await x({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await j(e.helper,n.windowId,e.traceId),s=P(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await W(e.helper,r,e.traceId);return h(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),Qe(a.blocks||[],e.marker)}function Qe(e,t){const n=t.trim();if(!n)return 0;const o=e.map(a=>a.text||"").join(`
|
|
4
|
-
`),r=X(o,n);if(r>0)return r;const s=Z(n);return s.length<8?0:X(Z(o),s)}function Z(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function X(e,t){if(!t)return 0;let n=0,o=0;for(;;){const r=e.indexOf(t,o);if(r<0)break;n+=1,o=r+t.length}return n}async function Ze(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let o=0;for(let r=0;r<t;r+=1){if(o=await B({helper:e.helper,binding:e.binding,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${r+1}`,settleToBottom:e.settleToBottom}),n==null?o>0:o>n)return o;r<t-1&&await L(800)}return o}function Xe(e,t,n){return n.length>0||e.sendStatus==="confirmed_echo"?"confirmed_echo":t.waitingRecords.length>0?"send_waiting_user_idle":t.failedRecords.length>0?e.failureCode==="user_active_timeout"?"failed:user_active_timeout":"outbound_send_failed":t.manualReviewRecords.length>0||e.sendStatus==="manual_review"?"manual_review":e.sendStatus==="sent_unconfirmed"?"sent_unconfirmed":e.sendStatus}function Ye(e,t){return e==="human-send-preflight-busy"?t.outboundStatus==="queued"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-user-active-timeout"?t.outboundStatus==="failed"&&t.runStatus==="failed:user_active_timeout"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-takeover-during-run"?(t.outboundStatus==="queued"||t.outboundStatus==="manual_review")&&t.observedMarkerCount===0:e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"||e==="human-send-normal"?t.outboundStatus==="confirmed_echo"||t.outboundStatus==="sent_unconfirmed"&&t.observedMarkerCount===1:t.runStatus==="observe_deferred_user_active"}function et(e){return
|
|
5
|
-
`)}}function ot(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=M(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=M(e.result)),t}function M(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return rt(t,e)?{type:"string",length:e.length,sha256:R.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const s=e.slice(0,10).map(a=>M(a,t,n+1));return e.length>s.length?{type:"array",length:e.length,items:s}:s}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[s,a]of Object.entries(o))r[s]=M(a,s,n+1);return r}function rt(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function st(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?T("send"):e.steps.some(t=>t==="download-visible-media")?T("download"):T("observe")}function at(e){const t=String(e||"").trim().toLowerCase();if(t){if(t==="windows"||t==="win32")return"win32";if(t==="macos"||t==="darwin")return"darwin";throw new Error(`unsupported_wechat_smoke_platform:${e}`)}}function it(e,t){const n=i.join(t.outDir,"fixtures");
|
|
3
|
+
`),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(V).length,d=E(t,"waitingRecords"),l=E(t,"failedRecords"),c=E(t,"manualReviewRecords"),f=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!f&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:f?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function V(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function E(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Fe(e){const t=["send-text"],n=[];for(const c of t)n.push(await z({...e,step:c}));await L(1200);const o=await Ne(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)});h(i.join(e.options.outDir,"bottom-page-summary.json"),r),h(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=Be({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function Ne(e,t,n={}){let o;for(let r=1;r<=t;r+=1)try{return await I(e,n)}catch(s){if(o=s,r>=t||!Oe(s))break;try{const a=await C(e.helper,e.options.traceId),d=await k(e.helper,a,e.options.traceId);await x({helper:e.helper,window:d,binding:e.binding,traceId:e.options.traceId})}catch{}await U("observe-retry",`${e.options.traceId}:observe-retry:${r}`,1500*r)}throw o}function Oe(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Le(e){const t=await I(e),n=D(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function He(e){const t=await I(e,{observePipeline:"edge-structure",persistLocalLedger:!0});$(e.options.outDir,"download-visible",t.observationEvidence);const n=D(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"download-visible-summary.json"),s),h(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function qe(e){const t=await I(e,{observePipeline:"server-observe"});$(e.options.outDir,"server-observe",t.observationEvidence),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Ke(e){const t=await I(e,{observePipeline:"edge-structure",downloadMedia:!1});$(e.options.outDir,"structure-window",t.observationEvidence),h(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function I(e,t={}){const n=ve({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=de({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const r=i.join(e.options.outDir,"inbound-attachments");b.mkdirSync(r,{recursive:!0});const s=t.persistLocalLedger?ft(e.options):void 0,a=s?fe(s,e.runtime.runtimeId):void 0;let d,l,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?ht(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:w})=>{d=Ge(u),l=ze(w),c=Ue(u,w,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=ge({ledger:a,bindingId:e.binding.bindingId,observedMessages:f});m=u.binding,p=ne({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),he(s,a)}return{messages:f,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function Ue(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function $(e,t,n){if(!n)return;const o=n.capture.dataBase64?P(e,`${t}-observation-window`,n.capture):void 0;h(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function Ge(e){if(e.dataBase64)return`sha256:${R.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function ze(e){return ne({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function J(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,f){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,f),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:f}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:f}):e.request(l,c,f)}}}function Q(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function 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 B(e){const t=await C(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await x({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await j(e.helper,n.windowId,e.traceId),s=P(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await W(e.helper,r,e.traceId);return h(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),Qe(a.blocks||[],e.marker)}function Qe(e,t){const n=t.trim();if(!n)return 0;const o=e.map(a=>a.text||"").join(`
|
|
4
|
+
`),r=X(o,n);if(r>0)return r;const s=Z(n);return s.length<8?0:X(Z(o),s)}function Z(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function X(e,t){if(!t)return 0;let n=0,o=0;for(;;){const r=e.indexOf(t,o);if(r<0)break;n+=1,o=r+t.length}return n}async function Ze(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let o=0;for(let r=0;r<t;r+=1){if(o=await B({helper:e.helper,binding:e.binding,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${r+1}`,settleToBottom:e.settleToBottom}),n==null?o>0:o>n)return o;r<t-1&&await L(800)}return o}function Xe(e,t,n){return n.length>0||e.sendStatus==="confirmed_echo"?"confirmed_echo":t.waitingRecords.length>0?"send_waiting_user_idle":t.failedRecords.length>0?e.failureCode==="user_active_timeout"?"failed:user_active_timeout":"outbound_send_failed":t.manualReviewRecords.length>0||e.sendStatus==="manual_review"?"manual_review":e.sendStatus==="sent_unconfirmed"?"sent_unconfirmed":e.sendStatus}function Ye(e,t){return e==="human-send-preflight-busy"?t.outboundStatus==="queued"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-user-active-timeout"?t.outboundStatus==="failed"&&t.runStatus==="failed:user_active_timeout"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-takeover-during-run"?(t.outboundStatus==="queued"||t.outboundStatus==="manual_review")&&t.observedMarkerCount===0:e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"||e==="human-send-normal"?t.outboundStatus==="confirmed_echo"||t.outboundStatus==="sent_unconfirmed"&&t.observedMarkerCount===1:t.runStatus==="observe_deferred_user_active"}function et(e){return we({runtimeId:e.runtimeId,machineId:e.machineId,pollIntervalMs:6e4,platform:e.platform,foregroundPolicy:"work",bindings:[{bindingId:`action-smoke:${_(e.conversation)}`,sessionId:e.sessionId||`action-smoke:${_(e.runtimeId)}`,conversationDisplayName:e.conversation,enabled:!0,allowReply:!0,downloadMedia:!0}]})}function tt(e,t){const n=ce({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new le({helperPath:n.helperPath,expectedHelperVersion:n.version||ue,requiredCapabilities:st(e),guardUnsafeWindowsCommands:(e.platform??process.platform)==="win32",cleanupWindowsOverlays:(e.platform??process.platform)==="win32",requestLogger:nt(t)})}function nt(e){return t=>{b.appendFileSync(e,`${JSON.stringify(ot(t))}
|
|
5
|
+
`)}}function ot(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=M(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=M(e.result)),t}function M(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return rt(t,e)?{type:"string",length:e.length,sha256:R.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const s=e.slice(0,10).map(a=>M(a,t,n+1));return e.length>s.length?{type:"array",length:e.length,items:s}:s}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[s,a]of Object.entries(o))r[s]=M(a,s,n+1);return r}function rt(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function st(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?T("send"):e.steps.some(t=>t==="download-visible-media")?T("download"):T("observe")}function at(e){const t=String(e||"").trim().toLowerCase();if(t){if(t==="windows"||t==="win32")return"win32";if(t==="macos"||t==="darwin")return"darwin";throw new Error(`unsupported_wechat_smoke_platform:${e}`)}}function it(e,t){const n=i.join(t.outDir,"fixtures");b.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||b.writeFileSync(r,`Shennian WeChat product action smoke
|
|
6
6
|
${o}
|
|
7
|
-
`,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||gt(r),{text:`codex-product-action image ${o}`,attachmentPath:r}}if(e==="send-video"){const r=t.videoPath||i.join(n,`wechat-action-smoke-${o}.mp4`);return!t.videoPath&&!pt(r)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${o}`,attachmentPath:r}}throw new Error(`step ${e} is not a send step`)}function Y(e){const t=e.flatMap(n=>dt(n.mediaMetadata));return{count:t.length,byAvailability:N(t.map(n=>ee(n,"availability")||"unknown")),localPaths:t.map(n=>ee(n,"localPath")).filter(n=>!!n)}}function dt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(te):te(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function ct(e){const t=g(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function ut(e){return e==="open-read"||e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="structure-window"||e==="server-observe"||e==="read-bottom-page"||e==="download-visible-media"||e==="send-and-confirm"||e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"||e==="human-send-normal"||e==="human-observe-deferred"?e:null}function lt(e,t){const n=[];for(let o=0;o<e.length;o+=1)e[o]===t&&e[o+1]&&n.push(e[o+1]);return n}function g(e,t){const n=e.indexOf(t),o=n>=0?e[n+1]:void 0;return o&&!o.startsWith("--")?o:void 0}function F(e){return e?i.resolve(e):void 0}function mt(e){return Array.from(new Set(e))}function P(e,t,n){const o=n.mimeType.includes("jpeg")?".jpg":".png",r=i.join(e,`${t}${o}`);return
|
|
8
|
-
`,"utf8")}function A(e,t){
|
|
9
|
-
`)}async function
|
|
10
|
-
`),process.exit(1)});export{D as buildBottomPageReport,q as buildDownloadVisibleMediaReport,Be as buildSendAndConfirmEvidence,We as buildSendStepAcceptance,Qe as countMarkerOccurrencesInOcrBlocks,$e as nextSmokeDrainWaitMs,
|
|
7
|
+
`,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||gt(r),{text:`codex-product-action image ${o}`,attachmentPath:r}}if(e==="send-video"){const r=t.videoPath||i.join(n,`wechat-action-smoke-${o}.mp4`);return!t.videoPath&&!pt(r)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${o}`,attachmentPath:r}}throw new Error(`step ${e} is not a send step`)}function Y(e){const t=e.flatMap(n=>dt(n.mediaMetadata));return{count:t.length,byAvailability:N(t.map(n=>ee(n,"availability")||"unknown")),localPaths:t.map(n=>ee(n,"localPath")).filter(n=>!!n)}}function dt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(te):te(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function ct(e){const t=g(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function ut(e){return e==="open-read"||e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="structure-window"||e==="server-observe"||e==="read-bottom-page"||e==="download-visible-media"||e==="send-and-confirm"||e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"||e==="human-send-normal"||e==="human-observe-deferred"?e:null}function lt(e,t){const n=[];for(let o=0;o<e.length;o+=1)e[o]===t&&e[o+1]&&n.push(e[o+1]);return n}function g(e,t){const n=e.indexOf(t),o=n>=0?e[n+1]:void 0;return o&&!o.startsWith("--")?o:void 0}function F(e){return e?i.resolve(e):void 0}function mt(e){return Array.from(new Set(e))}function P(e,t,n){const o=n.mimeType.includes("jpeg")?".jpg":".png",r=i.join(e,`${t}${o}`);return b.writeFileSync(r,Buffer.from(n.dataBase64,"base64")),r}function h(e,t){b.mkdirSync(i.dirname(e),{recursive:!0}),b.writeFileSync(e,`${JSON.stringify(t,null,2)}
|
|
8
|
+
`,"utf8")}function A(e,t){b.mkdirSync(i.dirname(e),{recursive:!0}),b.writeFileSync(e,t,"utf8")}function ft(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function ht(e,t=3){return(e?.recent??[]).slice(-t).map(n=>({stableMessageKey:n.stableMessageKey,senderRole:n.senderRole,kind:n.kind,anchorText:n.anchorText??n.normalizedText??n.textExcerpt??null,anchorMetadata:n.anchorMetadata??null}))}function gt(e){b.writeFileSync(e,Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAVElEQVR4nO3PMQ0AMAgAMUTof2kOKWzo8mCqkmeX9waYtwOwG4DbANwG4DYAtwG4DcBtAG4DcBuA2wDcBuA2ALcBuA3AbQBuA3AbgNsA3AbgNgC3AbgNwG0AbgNwG8BPDyCqAzmh3yHRAAAAAElFTkSuQmCC","base64"))}function pt(e){return se("ffmpeg",["-y","-f","lavfi","-i","testsrc=size=160x120:rate=5","-t","1","-pix_fmt","yuv420p",e],{encoding:"utf8",stdio:"ignore",timeout:2e4}).status===0&&b.existsSync(e)}function N(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function ee(e,t){return typeof e[t]=="string"?e[t].trim():""}function te(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){return R.createHash("sha256").update(e).digest("hex").slice(0,16)}function ne(e){return`sha256:${R.createHash("sha256").update(bt(e)).digest("hex")}`}function bt(e){return JSON.stringify(O(e))}function O(e){if(Array.isArray(e))return e.map(O);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,O(t[n])]))}function wt(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=Ce(process.argv.slice(2)),t=await xe(e);yt(t),(!t.ok||e.requireServer&&t.steps.some(n=>n.step==="server-observe"&&!n.ok))&&process.exit(1)}process.argv[1]&&i.resolve(process.argv[1])===ae(import.meta.url)&&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,$e as nextSmokeDrainWaitMs,Ce as parseWeChatChannelActionSmokeArgs,ve as resolveWeChatSmokeObserveSessionMode,xe as runWeChatChannelActionSmoke,ke 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{
|
|
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=
|
|
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)}
|
|
@@ -2,11 +2,13 @@ import type { AgentEvent, AgentAdapter } from '../agents/adapter.js';
|
|
|
2
2
|
import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from '@shennian/wire';
|
|
3
3
|
import { ManagerRegistry } from './registry.js';
|
|
4
4
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
5
|
-
import { ChannelRuntime } from '../channels/runtime.js';
|
|
5
|
+
import { ChannelRuntime, type ChannelRuntimeOptions } from '../channels/runtime.js';
|
|
6
6
|
export type LocalReqDispatcher = (req: ReqFrame) => Promise<void>;
|
|
7
7
|
type ManagerRuntimeServiceOptions = {
|
|
8
8
|
getRuntime: () => SessionManagerRuntime;
|
|
9
9
|
dispatchReq: LocalReqDispatcher;
|
|
10
|
+
channelRuntime?: ChannelRuntime;
|
|
11
|
+
createWeChatRpaProductRunner?: ChannelRuntimeOptions['createWeChatRpaProductRunner'];
|
|
10
12
|
};
|
|
11
13
|
export declare function setManagerRuntimeService(service: ManagerRuntimeService | null): void;
|
|
12
14
|
export declare function getManagerRuntimeService(): ManagerRuntimeService | null;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import L from"node:http";import{randomBytes as O,randomUUID as g}from"node:crypto";import k from"node:fs";import T from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as U,extractPayloadText as K,formatExternalConversationText as H,formatExternalMessageLine as j,formatExternalAttachmentReference as G,isAgentHiddenPayload as z,isToolPayload as J}from"@shennian/wire";import{ManagerRegistry as Y}from"./registry.js";import{readMessages as V}from"../session/store.js";import{ChannelRuntime as Q}from"../channels/runtime.js";import{splitExternalReplyText as X}from"../channels/reply-split.js";import{resolveShennianPath as Z}from"../config/index.js";import{buildExternalChannelInstructions as ee}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as te,sendDirectWeChatMessageOnce as ne}from"../commands/wechat.js";const
|
|
2
|
-
`).trim()}function ie(r){try{const e=JSON.parse(r),a=e.type==="tool_result"||e.result?"tool_result":"tool_call",n=e.name||"tool",t=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",i=t.length>220?`${t.slice(0,220)}...`:t;return i?`[${a}] ${n}: ${i}`:`[${a}] ${n}`}catch{return"[tool]"}}function D(r){if(!r||typeof r!="object")return;const e=r,a=String(e.kind||""),n=String(e.name||""),t=String(e.mimeType||""),i=String(e.dataBase64||""),o=String(e.localPath||""),s=String(e.url||""),l=Number(e.size||0);if(i)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(a!=="image"&&a!=="video"&&a!=="file")&&!(!n||!t||!Number.isFinite(l)||l<0)&&!(!o&&!s))return{kind:a,name:n,mimeType:t,size:l,...o?{localPath:o}:{},...s?{url:s}:{}}}function W(r){const e=r.binding&&typeof r.binding=="object"&&!Array.isArray(r.binding)?r.binding:{},a=String(e.sessionId||r.managerSessionId||"").trim(),n=String(e.channelId||"").trim(),t=String(e.conversationId||"").trim(),i=String(e.conversationName||r.conversation||"").trim(),o=String(e.workDir||r.workDir||"").trim();if(!a)throw new Error("WeChat tool sessionId is required");if(!n)throw new Error("WeChat tool channelId is required");if(!t)throw new Error("WeChat tool conversationId is required");if(!i)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:a,channelId:n,conversationId:t,conversationName:i,workDir:o}}function oe(r,e){const a=[...r].sort((c,d)=>c.ts-d.ts),n=[];let t=null,i=null,o=null,s="";const l=()=>{if(!t)return;const c=s.trim();c&&n.push({...t,id:`${t.id}-compact`,payload:c}),t=null,i=null,o=null,s=""};for(const c of a){if(z(c.payload)){l();continue}if(c.role==="user"){l(),n.push(c);continue}if(J(c.payload)){l(),n.push({...c,payload:ie(c.payload)});continue}const d=K(c.payload);if(!d.trim())continue;const h=re(c.id),p=se(c.id);t&&t.role===c.role&&i===h&&h&&p!==null&&o!==null&&p===o+1?(s+=d,t.ts=c.ts,o=p):(l(),t=c,i=h,o=p,s=d)}return l(),n.slice(-e).sort((c,d)=>d.ts-c.ts)}async function le(r){const e=[];let a=0;for await(const t of r){const i=Buffer.from(t);if(a+=i.byteLength,Number.isFinite(A)&&A>0&&a>A)throw new Error(`Manager IPC request body is too large. Max: ${A} bytes.`);e.push(i)}const n=Buffer.concat(e).toString("utf-8");return n?JSON.parse(n):{}}function w(r,e,a,n){r.client.sendRes({type:"res",id:e,ok:a,...a?{payload:n}:{error:String(n.error||"unknown error")}})}function P(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function v(){return ce()?y.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):Z("runtime","manager-ipc.json")}function ce(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ve{opts;registry=new Y;channelRuntime=new Q((e,a)=>{this.handleExternalMessage(e,a)},e=>this.registry.createReplyTarget(e).replyTarget);server=null;ipcUrl=null;ipcToken=O(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=L.createServer((a,n)=>{this.handleIpc(a,n)}),await new Promise((a,n)=>{this.server.once("error",n),this.server.listen(0,"127.0.0.1",()=>a())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=v();k.mkdirSync(y.dirname(e),{recursive:!0}),k.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),k.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=v(),a=JSON.parse(k.readFileSync(e,"utf-8"));(a.url===this.ipcUrl||a.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,a,n,t){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:a??"",SHENNIAN_MANAGER_WORKDIR:S(n),SHENNIAN_MANAGER_MODEL:t,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:S(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,a,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,defaultWorkerAgentType:a??null,defaultWorkerModelId:n??null})}getManagerWorkerDefaults(e){const a=this.registry.getManager(e);return{agentType:a?.defaultWorkerAgentType??null,modelId:a?.defaultWorkerModelId??null}}noteManagerAgentSession(e,a,n,t){this.registry.upsertManager({sessionId:e,agentSessionId:a,workDir:S(n),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:t})}noteAgentEvent(e,a){if(!this.findWorker(e))return;const t={lastActivityAt:new Date().toISOString(),runId:a.runId};a.agentSessionId&&(t.agentSessionId=a.agentSessionId);const i=`${e}:${a.runId}`;if(a.state==="delta"&&a.text&&!a.thinking){const s=(this.workerTextAcc.get(i)??"")+a.text;this.workerTextAcc.set(i,s);const l=N(s);l&&(t.summary=l.length>160?`${l.slice(0,160)}...`:l)}if(a.state==="final"||a.state==="error"||a.state==="aborted"){t.status=a.state;const s=N(this.workerTextAcc.get(i)??"");s&&(t.summary=s.length>240?`${s.slice(0,240)}...`:s),this.workerTextAcc.delete(i)}else a.state==="start"&&(t.status="running");const o=this.registry.updateWorker(e,t);o&&(a.state==="final"||a.state==="error"||a.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,a.state,a.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const a=this.opts.getRuntime(),n=e.params??{};try{const t=String(n.managerSessionId||n.sessionId||"");if(!t)throw new Error("sessionId is required");const i=this.registry.getManager(t),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",s=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",l=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",c=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${t}`),managerSessionId:t,sessionId:t,workDir:String(n.workDir||i?.workDir||""),type:"websocket",name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,wsUrl:typeof n.wsUrl=="string"?n.wsUrl:void 0,token:typeof n.token=="string"?n.token:void 0,canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"wechat-rpa",{includeSecret:!0})});return}if(s){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(n.id||`wechat-rpa:${t}`),managerSessionId:t,sessionId:t,workDir:S(String(n.workDir||i?.workDir||"")),name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,groups:$(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:_(n.source),pollIntervalMs:m(n.pollIntervalMs),recentLimit:m(n.recentLimit),idleSeconds:m(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(l){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(t);this.broadcastManagerChannelStatus(t),w(a,e.id,!0,d);return}if(c){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:t,idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:null,replyId:typeof n.replyId=="string"?n.replyId:null,reason:typeof n.reason=="string"?n.reason:"user_cancelled"});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(t){w(a,e.id,!1,{error:t instanceof Error?t.message:String(t)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const a=this.opts.getRuntime();if(!a.client?.sendEvent)return;const n=this.registry.getManager(e),t=this.channelRuntime.getManagerChannelStatus(e),i=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n?"manager":i?.agentType,agentSessionId:n?.agentSessionId??i?.agentSessionId??null,modelId:n?.modelId??i?.modelId??null,workDir:n?.workDir??i?.workDir,externalChannel:t}}})}async handleIpc(e,a){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(a,401,{ok:!1,error:"Unauthorized"});return}try{const n=new URL(e.url??"/","http://127.0.0.1"),t=await le(e),i=String(t.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!i)throw new Error("managerSessionId is required");const o=this.registry.getManager(i);if(n.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const s=new Set(this.opts.getRuntime().sessions.keys());u(a,200,{ok:!0,sessions:this.registry.listWorkers(i,{runningSessionIds:s})});return}if(n.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const s=String(t.agentType||t.agent||o.defaultWorkerAgentType||"codex");if(!ae(s))throw new Error(`Unsupported manager worker agent: ${s}`);const l=S(String(t.workDir||o.workDir));if(l!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const c=String(t.message||"");if(!c)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:i,agentType:s,workDir:l,summary:c.slice(0,120)}),h=String(t.modelId||(s===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,s,l,c,null,h),u(a,200,{ok:!0,session:d});return}if(n.pathname==="/sessions/send"){const s=String(t.sessionId||""),l=String(t.message||""),c=t.enqueue===void 0?!0:!!t.enqueue,d=this.registry.getWorkerForManager(i,s);if(!d)throw new Error("Worker not found in this manager scope");const h=String(t.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));c?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,l,d.agentSessionId??null,h),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/queue"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const c=this.opts.getRuntime().chatQueue?.getSnapshot(s);u(a,200,{ok:!0,queue:c});return}if(n.pathname==="/sessions/queue/edit"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${g()}`,method:"chat.queue.edit",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||""),text:String(t.message||t.text||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/queue/delete"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${g()}`,method:"chat.queue.delete",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/stop"||n.pathname==="/sessions/terminate"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${g()}`,method:"chat.abort",params:{sessionId:s}}),this.registry.updateWorker(s,{status:"aborted"}),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/read"){const s=String(t.sessionId||""),l=Number(t.limit||200);if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const d=V(s,{limit:Math.max(l*20,l)});u(a,200,{ok:!0,messages:oe(d,l),rawMessageCount:d.length});return}if(n.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(a,200,{ok:!0,path:y.join(o.workDir,".shennian")});return}if(n.pathname==="/external/reply"){const s=typeof t.replyTarget=="string"?this.registry.getReplyTarget(t.replyTarget):this.registry.getLatestReplyTargetForManager(i),l=String(t.text||""),c=D(t.attachment),d=String(t.idempotencyKey||g()),h=String(t.channelId||""),p=String(t.conversationId||""),R=!s&&(!h||!p)?await this.channelRuntime.getDefaultReplyTarget(i).catch(()=>null):null,M=s?.channelId||h||R?.channelId||"",C=s?.conversationId||p||R?.conversationId||"";if(M&&this.channelRuntime.getChannelById(M)){if(!C)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:i,channelId:M,conversationId:C,messageId:s?.messageId??void 0,text:l,attachment:c,idempotencyKey:d});u(a,f.ok?200:400,f);return}let I;try{I=await this.sendManagedWeComReply({managerSessionId:i,text:l,attachment:c,idempotencyKey:d})}catch(f){const E=f instanceof Error?f.message:String(f);if(!P(E))throw f;I={ok:!1,error:E}}if(I.ok){u(a,200,{ok:!0,payload:I.payload});return}if(!P(I.error||"")||!M||!C){u(a,400,{ok:!1,error:I.error||"External send failed"});return}u(a,400,{ok:!1,error:`No local external channel is configured for ${M}`});return}if(n.pathname==="/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(i,"websocket")});return}if(n.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const s=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${i}`),managerSessionId:i,workDir:o.workDir,type:"websocket",name:typeof t.name=="string"?t.name:void 0,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:s});return}if(n.pathname==="/wechat-rpa/tool/read"){const s=W(t),l=m(t.limit)??10,c=await te(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,limit:l,recentLimit:l,download:t.download==="never"?"never":"auto",traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,messages:c.messages,outDir:c.outDir,helperTracePath:c.helperTracePath});return}if(n.pathname==="/wechat-rpa/tool/send"){const s=W(t),l=String(t.text||""),c=D(t.attachment),d=await ne(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,text:l,attachment:c,traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,...d});return}if(n.pathname==="/wechat-rpa/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(i,"wechat-rpa",{includeSecret:!0})});return}if(n.pathname==="/wechat-rpa/channel/upsert"){const s=S(String(t.workDir||o?.workDir||""));if(!s)throw new Error("workDir is required");const l=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${i}`),managerSessionId:i,sessionId:i,workDir:s,name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:$(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:_(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:l});return}if(n.pathname==="/wechat-rpa/channel/sync"){const{channel:s,messages:l}=await this.channelRuntime.syncManagerWeChatRpaChannel(i);this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel:s,messages:l});return}if(n.pathname==="/wechat-rpa/outbound/cancel"){const s=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:i,idempotencyKey:typeof t.idempotencyKey=="string"?t.idempotencyKey:null,replyId:typeof t.replyId=="string"?t.replyId:null,reason:typeof t.reason=="string"?t.reason:"user_cancelled"});this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,...s});return}u(a,404,{ok:!1,error:`Unknown manager IPC path: ${n.pathname}`})}catch(n){u(a,400,{ok:!1,error:n instanceof Error?n.message:String(n)})}}async dispatchChatSend(e,a,n,t,i,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${g()}`,method:"chat.send",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async dispatchChatEnqueue(e,a,n,t,i,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${g()}`,method:"chat.enqueue",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async sendManagedWeComReply(e){const a=X(e.text);if(!a.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const n=this.opts.getRuntime().client;if(!n||typeof n.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const t=[];for(const[i,o]of a.entries()){const s=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:a.length>1?`${e.idempotencyKey}:${i+1}`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};t.push(s.payload)}if(e.attachment){const i=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:a.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};t.push(i.payload)}return{ok:!0,payload:t.length===1?t[0]:t}}wakeManagerForWorker(e,a,n,t){const i=this.registry.getManager(e);if(!i)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
|
|
1
|
+
import L from"node:http";import{randomBytes as O,randomUUID as g}from"node:crypto";import k from"node:fs";import T from"node:os";import y from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as U,extractPayloadText as K,formatExternalConversationText as H,formatExternalMessageLine as j,formatExternalAttachmentReference as G,isAgentHiddenPayload as z,isToolPayload as J}from"@shennian/wire";import{ManagerRegistry as Y}from"./registry.js";import{readMessages as V}from"../session/store.js";import{ChannelRuntime as Q}from"../channels/runtime.js";import{splitExternalReplyText as X}from"../channels/reply-split.js";import{resolveShennianPath as Z}from"../config/index.js";import{buildExternalChannelInstructions as ee}from"../agents/external-channel-instructions.js";import{readDirectWeChatLatestOnce as te,sendDirectWeChatMessageOnce as ne}from"../commands/wechat.js";const C=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024);let x=null;function We(r){x=r}function Pe(){return x}function S(r){return y.resolve(r||T.homedir())}function u(r,e,a){r.writeHead(e,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(a))}function re(r){return/^agent-(.+)-\d+$/.exec(r)?.[1]??null}function ae(r){return r==="manager"?!1:r.startsWith("custom:")?!0:U.includes(r)}function se(r){const e=/^agent-.+-(\d+)$/.exec(r);if(!e)return null;const a=Number(e[1]);return Number.isInteger(a)&&a>=0?a:null}function N(r){return r.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function ie(r){try{const e=JSON.parse(r),a=e.type==="tool_result"||e.result?"tool_result":"tool_call",n=e.name||"tool",t=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",i=t.length>220?`${t.slice(0,220)}...`:t;return i?`[${a}] ${n}: ${i}`:`[${a}] ${n}`}catch{return"[tool]"}}function D(r){if(!r||typeof r!="object")return;const e=r,a=String(e.kind||""),n=String(e.name||""),t=String(e.mimeType||""),i=String(e.dataBase64||""),o=String(e.localPath||""),s=String(e.url||""),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 W(r){const e=r.binding&&typeof r.binding=="object"&&!Array.isArray(r.binding)?r.binding:{},a=String(e.sessionId||r.managerSessionId||"").trim(),n=String(e.channelId||"").trim(),t=String(e.conversationId||"").trim(),i=String(e.conversationName||r.conversation||"").trim(),o=String(e.workDir||r.workDir||"").trim();if(!a)throw new Error("WeChat tool sessionId is required");if(!n)throw new Error("WeChat tool channelId is required");if(!t)throw new Error("WeChat tool conversationId is required");if(!i)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:a,channelId:n,conversationId:t,conversationName:i,workDir:o}}function oe(r,e){const a=[...r].sort((l,d)=>l.ts-d.ts),n=[];let t=null,i=null,o=null,s="";const c=()=>{if(!t)return;const l=s.trim();l&&n.push({...t,id:`${t.id}-compact`,payload:l}),t=null,i=null,o=null,s=""};for(const l of a){if(z(l.payload)){c();continue}if(l.role==="user"){c(),n.push(l);continue}if(J(l.payload)){c(),n.push({...l,payload:ie(l.payload)});continue}const d=K(l.payload);if(!d.trim())continue;const h=re(l.id),p=se(l.id);t&&t.role===l.role&&i===h&&h&&p!==null&&o!==null&&p===o+1?(s+=d,t.ts=l.ts,o=p):(c(),t=l,i=h,o=p,s=d)}return c(),n.slice(-e).sort((l,d)=>d.ts-l.ts)}async function ce(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 P(r){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(r)}function v(){return le()?y.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):Z("runtime","manager-ipc.json")}function le(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ve{opts;registry=new Y;channelRuntime;server=null;ipcUrl=null;ipcToken=O(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new Q((a,n)=>{this.handleExternalMessage(a,n)},a=>this.registry.createReplyTarget(a).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=L.createServer((a,n)=>{this.handleIpc(a,n)}),await new Promise((a,n)=>{this.server.once("error",n),this.server.listen(0,"127.0.0.1",()=>a())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=v();k.mkdirSync(y.dirname(e),{recursive:!0}),k.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),k.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=v(),a=JSON.parse(k.readFileSync(e,"utf-8"));(a.url===this.ipcUrl||a.token===this.ipcToken)&&k.unlinkSync(e)}catch{}}getInjectedEnv(e,a,n,t){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:a??"",SHENNIAN_MANAGER_WORKDIR:S(n),SHENNIAN_MANAGER_MODEL:t,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:S(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,a,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,defaultWorkerAgentType:a??null,defaultWorkerModelId:n??null})}getManagerWorkerDefaults(e){const a=this.registry.getManager(e);return{agentType:a?.defaultWorkerAgentType??null,modelId:a?.defaultWorkerModelId??null}}noteManagerAgentSession(e,a,n,t){this.registry.upsertManager({sessionId:e,agentSessionId:a,workDir:S(n),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:t})}noteAgentEvent(e,a){if(!this.findWorker(e))return;const t={lastActivityAt:new Date().toISOString(),runId:a.runId};a.agentSessionId&&(t.agentSessionId=a.agentSessionId);const i=`${e}:${a.runId}`;if(a.state==="delta"&&a.text&&!a.thinking){const s=(this.workerTextAcc.get(i)??"")+a.text;this.workerTextAcc.set(i,s);const c=N(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=N(this.workerTextAcc.get(i)??"");s&&(t.summary=s.length>240?`${s.slice(0,240)}...`:s),this.workerTextAcc.delete(i)}else a.state==="start"&&(t.status="running");const o=this.registry.updateWorker(e,t);o&&(a.state==="final"||a.state==="error"||a.state==="aborted")&&this.wakeManagerForWorker(o.managedBy,o,a.state,a.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const a=this.opts.getRuntime(),n=e.params??{};try{const t=String(n.managerSessionId||n.sessionId||"");if(!t)throw new Error("sessionId is required");const i=this.registry.getManager(t),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",s=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",l=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(n.id||`websocket:${t}`),managerSessionId:t,sessionId:t,workDir:String(n.workDir||i?.workDir||""),type:"websocket",name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,wsUrl:typeof n.wsUrl=="string"?n.wsUrl:void 0,token:typeof n.token=="string"?n.token:void 0,canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(o){w(a,e.id,!0,{channel:this.channelRuntime.getManagerChannel(t,"wechat-rpa",{includeSecret:!0})});return}if(s){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(n.id||`wechat-rpa:${t}`),managerSessionId:t,sessionId:t,workDir:S(String(n.workDir||i?.workDir||"")),name:typeof n.name=="string"?n.name:void 0,agentType:typeof n.agentType=="string"?n.agentType:void 0,agentSessionId:typeof n.agentSessionId=="string"?n.agentSessionId:null,modelId:typeof n.modelId=="string"?n.modelId:null,enabled:!!n.enabled,groups:$(n.groups),canReply:n.canReply===void 0?void 0:!!n.canReply,systemPrompt:typeof n.systemPrompt=="string"?n.systemPrompt:void 0,source:_(n.source),pollIntervalMs:m(n.pollIntervalMs),recentLimit:m(n.recentLimit),idleSeconds:m(n.idleSeconds),forceForeground:n.forceForeground===void 0?void 0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?void 0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:void 0,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:void 0,privacyConsentAccepted:n.privacyConsentAccepted===void 0?void 0:!!n.privacyConsentAccepted,flowScriptPath:typeof n.flowScriptPath=="string"?n.flowScriptPath:void 0});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,{channel:d});return}if(c){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(t);this.broadcastManagerChannelStatus(t),w(a,e.id,!0,d);return}if(l){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:t,idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:null,replyId:typeof n.replyId=="string"?n.replyId:null,reason:typeof n.reason=="string"?n.reason:"user_cancelled"});this.broadcastManagerChannelStatus(t),w(a,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(t){w(a,e.id,!1,{error:t instanceof Error?t.message:String(t)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const a=this.opts.getRuntime();if(!a.client?.sendEvent)return;const n=this.registry.getManager(e),t=this.channelRuntime.getManagerChannelStatus(e),i=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n?"manager":i?.agentType,agentSessionId:n?.agentSessionId??i?.agentSessionId??null,modelId:n?.modelId??i?.modelId??null,workDir:n?.workDir??i?.workDir,externalChannel:t}}})}async handleIpc(e,a){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(a,401,{ok:!1,error:"Unauthorized"});return}try{const n=new URL(e.url??"/","http://127.0.0.1"),t=await ce(e),i=String(t.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!i)throw new Error("managerSessionId is required");const o=this.registry.getManager(i);if(n.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const s=new Set(this.opts.getRuntime().sessions.keys());u(a,200,{ok:!0,sessions:this.registry.listWorkers(i,{runningSessionIds:s})});return}if(n.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const s=String(t.agentType||t.agent||o.defaultWorkerAgentType||"codex");if(!ae(s))throw new Error(`Unsupported manager worker agent: ${s}`);const c=S(String(t.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const l=String(t.message||"");if(!l)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:i,agentType:s,workDir:c,summary:l.slice(0,120)}),h=String(t.modelId||(s===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,s,c,l,null,h),u(a,200,{ok:!0,session:d});return}if(n.pathname==="/sessions/send"){const s=String(t.sessionId||""),c=String(t.message||""),l=t.enqueue===void 0?!0:!!t.enqueue,d=this.registry.getWorkerForManager(i,s);if(!d)throw new Error("Worker not found in this manager scope");const h=String(t.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));l?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/queue"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const l=this.opts.getRuntime().chatQueue?.getSnapshot(s);u(a,200,{ok:!0,queue:l});return}if(n.pathname==="/sessions/queue/edit"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${g()}`,method:"chat.queue.edit",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||""),text:String(t.message||t.text||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/queue/delete"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${g()}`,method:"chat.queue.delete",params:{sessionId:s,queueMessageId:String(t.queueMessageId||t.messageId||"")}}),u(a,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(s)});return}if(n.pathname==="/sessions/stop"||n.pathname==="/sessions/terminate"){const s=String(t.sessionId||"");if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${g()}`,method:"chat.abort",params:{sessionId:s}}),this.registry.updateWorker(s,{status:"aborted"}),u(a,200,{ok:!0});return}if(n.pathname==="/sessions/read"){const s=String(t.sessionId||""),c=Number(t.limit||200);if(!this.registry.getWorkerForManager(i,s))throw new Error("Worker not found in this manager scope");const d=V(s,{limit:Math.max(c*20,c)});u(a,200,{ok:!0,messages:oe(d,c),rawMessageCount:d.length});return}if(n.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(a,200,{ok:!0,path:y.join(o.workDir,".shennian")});return}if(n.pathname==="/external/reply"){const s=typeof t.replyTarget=="string"?this.registry.getReplyTarget(t.replyTarget):this.registry.getLatestReplyTargetForManager(i),c=String(t.text||""),l=D(t.attachment),d=String(t.idempotencyKey||g()),h=String(t.channelId||""),p=String(t.conversationId||""),R=!s&&(!h||!p)?await this.channelRuntime.getDefaultReplyTarget(i).catch(()=>null):null,M=s?.channelId||h||R?.channelId||"",A=s?.conversationId||p||R?.conversationId||"";if(M&&this.channelRuntime.getChannelById(M)){if(!A)throw new Error("No external channel target is available for this Manager");const f=await this.channelRuntime.reply({managerSessionId:i,channelId:M,conversationId:A,messageId:s?.messageId??void 0,text:c,attachment:l,idempotencyKey:d});u(a,f.ok?200:400,f);return}let I;try{I=await this.sendManagedWeComReply({managerSessionId:i,text:c,attachment:l,idempotencyKey:d})}catch(f){const E=f instanceof Error?f.message:String(f);if(!P(E))throw f;I={ok:!1,error:E}}if(I.ok){u(a,200,{ok:!0,payload:I.payload});return}if(!P(I.error||"")||!M||!A){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),c=m(t.limit)??10,l=await te(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,limit:c,recentLimit:c,download:t.download==="never"?"never":"auto",traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,messages:l.messages,outDir:l.outDir,helperTracePath:l.helperTracePath});return}if(n.pathname==="/wechat-rpa/tool/send"){const s=W(t),c=String(t.text||""),l=D(t.attachment),d=await ne(s,{conversation:s.conversationName,workDir:s.workDir,sessionId:s.sessionId,text:c,attachment:l,traceId:typeof t.traceId=="string"?t.traceId:void 0,timeoutMs:m(t.timeoutMs)});u(a,200,{ok:!0,...d});return}if(n.pathname==="/wechat-rpa/channel/get"){u(a,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(i,"wechat-rpa",{includeSecret:!0})});return}if(n.pathname==="/wechat-rpa/channel/upsert"){const s=S(String(t.workDir||o?.workDir||""));if(!s)throw new Error("workDir is required");const 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:$(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:_(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(i),u(a,200,{ok:!0,channel: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-${g()}`,method:"chat.send",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async dispatchChatEnqueue(e,a,n,t,i,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${g()}`,method:"chat.enqueue",params:{sessionId:e,text:t,agentType:a,workDir:n,agentSessionId:i,modelId:o}})}async sendManagedWeComReply(e){const a=X(e.text);if(!a.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const n=this.opts.getRuntime().client;if(!n||typeof n.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const t=[];for(const[i,o]of a.entries()){const s=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:a.length>1?`${e.idempotencyKey}:${i+1}`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};t.push(s.payload)}if(e.attachment){const i=await n.sendReq({type:"req",id:`external-send-${g()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:a.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};t.push(i.payload)}return{ok:!0,payload:t.length===1?t[0]:t}}wakeManagerForWorker(e,a,n,t){const i=this.registry.getManager(e);if(!i)return;const o=`\u4F60\u7BA1\u7406\u7684 worker \u5DF2\u7ED3\u675F\u3002
|
|
3
3
|
|
|
4
4
|
Worker: ${a.sessionId}
|
|
5
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(),
|
|
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,l=n?.modelId||i?.modelId||"",d=de(a.attachments,a.channelType),h=he(a);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:s,agentSessionId:c,modelId:l,text:h,attachments:d,externalChannel:me(t),replyTarget:a.replyTarget})}async dispatchExternalMessage(e){await this.opts.dispatchReq({type:"req",id:`external-enqueue-${g()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),a=this.registry.load();for(const n of Object.values(a.workers)){if(n.status!=="running")continue;const t=e-Date.parse(n.createdAt);if(t<10*6e4)continue;const i=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(i&&e-i<10*6e4)continue;const o=a.managers[n.managedBy];if(!o)continue;const s=Math.max(0,Math.floor((e-Date.parse(n.lastActivityAt))/6e4)),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:
|
|
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-${g()}`,method:"chat.abort",params:{sessionId:e.sessionId}}));const o=n?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${n}
|
|
17
17
|
|
|
18
18
|
${a}`:a;await this.dispatchChatSend(e.sessionId,"manager",e.workDir,o,e.agentSessionId,e.modelId)}bindManagerAdapterEvents(e,a){a.on("agentEvent",n=>{if(n.state==="start"&&n.agentSessionId){const t=this.registry.getManager(e);t&&this.noteManagerAgentSession(e,n.agentSessionId,t.workDir,t.modelId)}n.state==="start"&&this.updateManagerStatus(e,"running"),(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.updateManagerStatus(e,"idle")})}updateManagerStatus(e,a){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,status:a})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(a=>ee(a,void 0,e,"manager")).join(`
|
|
19
19
|
|
|
20
|
-
`).trim()}}function $(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function _(r){return r==="wechat-channel"||r==="macos-flow"||r==="macos-probe"||r==="windows-visual-flow"||r==="wechat-rpa-lab"||r==="fixture-jsonl"?r:void 0}function de(r,e){const a=r.map(n=>B(n,e)).filter(n=>n!=null);return a.length?a:void 0}function ue(r,e,a,n){const t=e||"",i=new Set;return r.map((s,
|
|
20
|
+
`).trim()}}function $(r){return Array.isArray(r)?r.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function _(r){return r==="wechat-channel"||r==="macos-flow"||r==="macos-probe"||r==="windows-visual-flow"||r==="wechat-rpa-lab"||r==="fixture-jsonl"?r:void 0}function de(r,e){const a=r.map(n=>B(n,e)).filter(n=>n!=null);return a.length?a:void 0}function ue(r,e,a,n){const t=e||"",i=new Set;return r.map((s,c)=>{const l=B(s,n),d=G({type:s.type,name:l?.name||s.name,path:l?.path,mimeType:l?.mimeType||s.mimeType,availability:s.availability,providerError:s.providerError||ge(s)},c),h=l?.path||"";if(h){const R=`${d}
|
|
21
21
|
${h}`;return i.has(R)||t.includes(h)?"":(i.add(R),`${a}: ${d}`)}const p=d;return i.has(p)?"":(i.add(p),`${a}: ${d}`)}).filter(s=>!t.includes(s)).join(`
|
|
22
22
|
`)}function he(r){const a=(r.channelType==="wechat-rpa"?pe(r.sender.name):r.sender.name||r.sender.id)||(r.channelType==="wechat-rpa"?"\u5BF9\u65B9":r.sender.id)||"\u5BF9\u65B9",n=ue(r.attachments,r.text,a,r.channelType),i=[r.text?j({senderName:a,senderId:r.sender.id,text:r.text}):n?"":`${a}:`,n].filter(Boolean).join(`
|
|
23
23
|
`);return[H({conversationName:r.conversationName||r.conversationId,messages:[]}),i].filter(Boolean).join(`
|