shennian 0.4.41 → 0.4.43
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 +54 -24
- package/dist/src/index.js +2 -2
- package/dist/src/manager/registry.d.ts +4 -0
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +1 -0
- package/dist/src/manager/runtime.js +7 -7
- package/dist/src/room/activation-service.js +5 -5
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/mirror-sync-service.d.ts +2 -0
- package/dist/src/room/mirror-sync-service.js +1 -1
- package/dist/src/room/registration-authority.d.ts +4 -0
- package/dist/src/room/registration-authority.js +1 -0
- package/dist/src/room/room-action-client.d.ts +3 -0
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/room/session-registry.d.ts +5 -0
- package/dist/src/room/session-registry.js +2 -2
- package/dist/src/session/deletion-sync-validation.d.ts +2 -0
- package/dist/src/session/deletion-sync-validation.js +1 -0
- package/dist/src/session/handlers/agent-control.d.ts +4 -0
- package/dist/src/session/handlers/agent-control.js +1 -0
- package/dist/src/session/handlers/room-managed.d.ts +1 -0
- package/dist/src/session/handlers/room-managed.js +1 -1
- package/dist/src/session/manager.d.ts +1 -5
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/queue-helpers.d.ts +10 -0
- package/dist/src/session/queue-helpers.js +11 -0
- package/dist/src/session/queue-retry.d.ts +11 -0
- package/dist/src/session/queue-retry.js +1 -0
- package/dist/src/session/queue.d.ts +11 -1
- package/dist/src/session/queue.js +2 -12
- package/dist/src/session/queued-chat-dispatch.d.ts +7 -0
- package/dist/src/session/queued-chat-dispatch.js +1 -0
- package/node_modules/@shennian/wire/dist/message-payload.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 190,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -441,8 +441,8 @@
|
|
|
441
441
|
},
|
|
442
442
|
{
|
|
443
443
|
"file": "src/index.js",
|
|
444
|
-
"beforeBytes":
|
|
445
|
-
"afterBytes":
|
|
444
|
+
"beforeBytes": 36726,
|
|
445
|
+
"afterBytes": 18286
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
"file": "src/log-redaction.js",
|
|
@@ -461,8 +461,8 @@
|
|
|
461
461
|
},
|
|
462
462
|
{
|
|
463
463
|
"file": "src/manager/registry.js",
|
|
464
|
-
"beforeBytes":
|
|
465
|
-
"afterBytes":
|
|
464
|
+
"beforeBytes": 13941,
|
|
465
|
+
"afterBytes": 6792
|
|
466
466
|
},
|
|
467
467
|
{
|
|
468
468
|
"file": "src/manager/retired-external-channel-compat.js",
|
|
@@ -471,8 +471,8 @@
|
|
|
471
471
|
},
|
|
472
472
|
{
|
|
473
473
|
"file": "src/manager/runtime.js",
|
|
474
|
-
"beforeBytes":
|
|
475
|
-
"afterBytes":
|
|
474
|
+
"beforeBytes": 77215,
|
|
475
|
+
"afterBytes": 38507
|
|
476
476
|
},
|
|
477
477
|
{
|
|
478
478
|
"file": "src/native-fusion/codex-files.js",
|
|
@@ -591,8 +591,8 @@
|
|
|
591
591
|
},
|
|
592
592
|
{
|
|
593
593
|
"file": "src/room/activation-service.js",
|
|
594
|
-
"beforeBytes":
|
|
595
|
-
"afterBytes":
|
|
594
|
+
"beforeBytes": 24933,
|
|
595
|
+
"afterBytes": 12454
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
598
|
"file": "src/room/attachment-cache.js",
|
|
@@ -636,8 +636,8 @@
|
|
|
636
636
|
},
|
|
637
637
|
{
|
|
638
638
|
"file": "src/room/managed-room-binding.js",
|
|
639
|
-
"beforeBytes":
|
|
640
|
-
"afterBytes":
|
|
639
|
+
"beforeBytes": 6573,
|
|
640
|
+
"afterBytes": 3483
|
|
641
641
|
},
|
|
642
642
|
{
|
|
643
643
|
"file": "src/room/managed-room-ipc.js",
|
|
@@ -646,8 +646,8 @@
|
|
|
646
646
|
},
|
|
647
647
|
{
|
|
648
648
|
"file": "src/room/mirror-sync-service.js",
|
|
649
|
-
"beforeBytes":
|
|
650
|
-
"afterBytes":
|
|
649
|
+
"beforeBytes": 9331,
|
|
650
|
+
"afterBytes": 4258
|
|
651
651
|
},
|
|
652
652
|
{
|
|
653
653
|
"file": "src/room/public-attachment-cache.js",
|
|
@@ -659,10 +659,15 @@
|
|
|
659
659
|
"beforeBytes": 5858,
|
|
660
660
|
"afterBytes": 3219
|
|
661
661
|
},
|
|
662
|
+
{
|
|
663
|
+
"file": "src/room/registration-authority.js",
|
|
664
|
+
"beforeBytes": 1006,
|
|
665
|
+
"afterBytes": 462
|
|
666
|
+
},
|
|
662
667
|
{
|
|
663
668
|
"file": "src/room/room-action-client.js",
|
|
664
|
-
"beforeBytes":
|
|
665
|
-
"afterBytes":
|
|
669
|
+
"beforeBytes": 29085,
|
|
670
|
+
"afterBytes": 14709
|
|
666
671
|
},
|
|
667
672
|
{
|
|
668
673
|
"file": "src/room/room-binding-client.js",
|
|
@@ -671,8 +676,8 @@
|
|
|
671
676
|
},
|
|
672
677
|
{
|
|
673
678
|
"file": "src/room/session-registry.js",
|
|
674
|
-
"beforeBytes":
|
|
675
|
-
"afterBytes":
|
|
679
|
+
"beforeBytes": 30575,
|
|
680
|
+
"afterBytes": 17164
|
|
676
681
|
},
|
|
677
682
|
{
|
|
678
683
|
"file": "src/security/managed-agent-credential-paths.js",
|
|
@@ -716,9 +721,14 @@
|
|
|
716
721
|
},
|
|
717
722
|
{
|
|
718
723
|
"file": "src/session/deletion-sync-state.js",
|
|
719
|
-
"beforeBytes":
|
|
724
|
+
"beforeBytes": 6026,
|
|
720
725
|
"afterBytes": 3190
|
|
721
726
|
},
|
|
727
|
+
{
|
|
728
|
+
"file": "src/session/deletion-sync-validation.js",
|
|
729
|
+
"beforeBytes": 1590,
|
|
730
|
+
"afterBytes": 919
|
|
731
|
+
},
|
|
722
732
|
{
|
|
723
733
|
"file": "src/session/handlers/agent-capabilities.js",
|
|
724
734
|
"beforeBytes": 767,
|
|
@@ -729,6 +739,11 @@
|
|
|
729
739
|
"beforeBytes": 5590,
|
|
730
740
|
"afterBytes": 2803
|
|
731
741
|
},
|
|
742
|
+
{
|
|
743
|
+
"file": "src/session/handlers/agent-control.js",
|
|
744
|
+
"beforeBytes": 6699,
|
|
745
|
+
"afterBytes": 3876
|
|
746
|
+
},
|
|
732
747
|
{
|
|
733
748
|
"file": "src/session/handlers/agent-workspace-validation.js",
|
|
734
749
|
"beforeBytes": 8799,
|
|
@@ -776,8 +791,8 @@
|
|
|
776
791
|
},
|
|
777
792
|
{
|
|
778
793
|
"file": "src/session/handlers/room-managed.js",
|
|
779
|
-
"beforeBytes":
|
|
780
|
-
"afterBytes":
|
|
794
|
+
"beforeBytes": 4928,
|
|
795
|
+
"afterBytes": 2628
|
|
781
796
|
},
|
|
782
797
|
{
|
|
783
798
|
"file": "src/session/handlers/session-refresh.js",
|
|
@@ -811,8 +826,8 @@
|
|
|
811
826
|
},
|
|
812
827
|
{
|
|
813
828
|
"file": "src/session/manager.js",
|
|
814
|
-
"beforeBytes":
|
|
815
|
-
"afterBytes":
|
|
829
|
+
"beforeBytes": 41277,
|
|
830
|
+
"afterBytes": 21705
|
|
816
831
|
},
|
|
817
832
|
{
|
|
818
833
|
"file": "src/session/native-cleanup-outbox.js",
|
|
@@ -834,10 +849,25 @@
|
|
|
834
849
|
"beforeBytes": 1991,
|
|
835
850
|
"afterBytes": 966
|
|
836
851
|
},
|
|
852
|
+
{
|
|
853
|
+
"file": "src/session/queue-helpers.js",
|
|
854
|
+
"beforeBytes": 4759,
|
|
855
|
+
"afterBytes": 2538
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
"file": "src/session/queue-retry.js",
|
|
859
|
+
"beforeBytes": 1253,
|
|
860
|
+
"afterBytes": 561
|
|
861
|
+
},
|
|
837
862
|
{
|
|
838
863
|
"file": "src/session/queue.js",
|
|
839
|
-
"beforeBytes":
|
|
840
|
-
"afterBytes":
|
|
864
|
+
"beforeBytes": 35377,
|
|
865
|
+
"afterBytes": 15602
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
"file": "src/session/queued-chat-dispatch.js",
|
|
869
|
+
"beforeBytes": 1541,
|
|
870
|
+
"afterBytes": 822
|
|
841
871
|
},
|
|
842
872
|
{
|
|
843
873
|
"file": "src/session/run-recovery/barrier.js",
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{Command as fe}from"commander";import k from"chalk";import he from"node:os";import j from"node:fs";import{loadConfig as p,saveConfig as ve,getConfigPath as Ie,getShennianDir as Se,resolveShennianPath as H,normalizeAutoUpgradePolicy as ye}from"./config/index.js";import{CliRelayClient as we}from"./relay/client.js";import{collectKnownLogSecrets as Q,installCliLogRedaction as Ce,sanitizeCliLogValue as Re}from"./log-redaction.js";import{registerPairCommand as be,runSmartStart as ke}from"./commands/pair.js";import{registerLogoutCommand as Pe}from"./commands/logout.js";import{clearDaemonPidIfOwner as X,createDaemonInstanceId as
|
|
2
|
-
`),process.exitCode=1}function $o(n){const i=[`trigger=${n.trigger??"unknown"}`,`phase=${n.phase}`,`code=${n.code??"unknown"}`,`attempt=${n.reconnectAttempt}`];return n.reason&&i.push(`reason=${n.reason}`),n.error&&i.push(`error=${n.error}`),i.join(" ")}const s=new fe;s.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(B),s.enablePositionalOptions(),je(s),He(s),s.option("--api <url>","Server URL override").option("--name <name>","Machine name",he.hostname()).action(async n=>{const i=p(),d=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??i.serverUrl??void 0;await ke(d,n.name)}),s.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 i=H("env.json");try{const e=JSON.parse(j.readFileSync(i,"utf-8"));for(const[t,o]of Object.entries(e))process.env[t]||(process.env[t]=o);ae()}catch{}Ce(Q(process.env,p()));const d=io();oe()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(d),process.exit(0));const c=Ae(),h=eo({lockPath:To,instanceId:c});h.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(h.ownerPid?` (PID ${h.ownerPid})`:"")+", skipping duplicate start"),clearInterval(d),process.exit(0));try{const e=Mo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),h.release(),process.exit(0)}catch{}}catch{}const q=Te(process.env,de)?[]:Ee(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),h.release(),process.exit(0)),ne(process.pid,c,{version:B}),re(process.pid,void 0,c),process.on("exit",()=>{X(process.pid,c),ee(c)}),process.on("exit",h.release);let N=null;try{N=Ye()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const A=await Je();if(A){N||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{Ze({fromVersion:B,targetVersion:A.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:A.jobId},{readiness:N}),console.error(`[upgrade] New version crashed ${A.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Ao()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=p(),f=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??F.cn.url,g=te(),y=await Xe({serverUrl:f,authToken:l.machineToken??l.accessToken}),w=y.map(e=>e.type),O=oo(y),v=new go,E=new ho,C=new Ro(v),I=new uo(f),pe=new wo({actionClient:I}),ge=new Co({actionClient:I});let S=null,a;const z=bo({authority:C,actionClient:I,attachmentCache:pe,attachmentUploader:ge,observations:E,getMachineCredentials:()=>({machineToken:p().machineToken}),onPaused:({registryKey:e,sessionId:t})=>{const r=v.getByRegistryKey(e)?.binding;r&&v.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),a?.discardPendingManagedRoomMessages(t)}});let x=null,V=null,L=null;const R=await se({paths:co(),handlers:mo({daemonVersion:g,protocolVersion:po,getStatus:()=>Me(),getAgents:()=>y,completeAgentAuthentication:e=>a.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!x)throw new Error("upgrade_unavailable");return x(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let b;try{b=await se({paths:lo(),handlers:{"managed-room.execute":e=>z(e)}})}catch(e){throw await R.close(),e}let J=!1;const U=()=>{J||(J=!0,Promise.allSettled([R.close(),b.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",U),process.once("SIGTERM",U),!l.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([R.close(),b.close()]),process.exitCode=1;return}process.removeListener("SIGINT",U),process.removeListener("SIGTERM",U);const K=`${_o(f)}/relay/machine`,D=l.machineToken;if(!D)return;try{await Po({serverUrl:f,machineToken:D,machineId:l.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([R.close(),b.close()]),process.exit(1)}l.machineId&&ro(f,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${g}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:g,agents:w}});let T=null,M=null,_=0,u=Ke();const W=ye(l.autoUpgrade),m=new we({serverUrl:K,machineToken:D,cliVersion:g,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ie,sessionSyncCapabilities:ce,daemonInstallationId:ko(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),O.some(o=>o.models.length>0)&&m.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:O}}),no(y,{serverUrl:l.serverUrl??f,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(O)&&m.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),u&&m.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:u.from,to:u.to,...u.jobId?{jobId:u.jobId}:{},activeRunCount:a.getPendingRecoveryCount()}}),$.start(),M?.start();const e=++_,t=async()=>{try{if(await a.synchronizeSessionDeletions(),await a.recoverInterruptedRuns(),u){const o=u;u=null,o.jobId&&Qe(o.jobId,o.to),ze(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),to({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),m.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&m.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&m.getState()==="connected"&&T?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&m.getState()==="connected"){if(o instanceof Uo)return;const r=o instanceof Eo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{t()},r).unref?.()}}};t()},onDisconnected:e=>{_+=1,a?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${$o(e)}`),P({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||a?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),a.handleReq(e)}});T=ao()?new so(m):null,M=new vo({registry:v,observations:E,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),onMessages:async(e,t,o,r)=>{await S?.processMessages(e,t,o,r)},onBindingPaused:e=>{C.revokeSession(e.registryKey),a?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new We({currentVersion:g,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??f,getIdleState:()=>a.getUpgradeIdleState(),sessionSyncProtocolVersion:ie,dataEpoch:le,advertisedCapabilities:ce,updaterProtocolVersion:1,startUpgrade:async(e,t)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(m,"auto-upgrade",e,{currentVersion:g,prepareForUpgrade:r=>a.prepareForUpgrade(r),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:t?void 0:()=>a.getUpgradeIdleState().idle})}});$.setPolicy(W),a=new Fe(m,T,g,$,new fo({registry:v,observations:E,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Ue(),isDisabled:()=>oe(),shutdownAfterResponse:()=>{setTimeout(()=>{De(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Be(D)),x=async e=>{const t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof t.targetVersion=="string"?t.targetVersion:void 0,r=t.confirmedMajor===!0;return await xe(m,`local-upgrade-${Date.now()}`,o,{currentVersion:g,confirmedMajor:r,prepareForUpgrade:ue=>a.prepareForUpgrade(ue),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof t.targetVersion=="string"?t.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...a.prepareForUpgrade(r)}},L=()=>(a.cancelUpgradePreparation(),{cancelled:!0}),S=new Io({registry:v,observations:E,enqueueConversationBatch:e=>a.enqueueManagedRoomBatch(e),loadContext:async e=>{const t=p().machineToken?.trim(),o=e.binding;if(!t||!o)throw new Error("managed_room_context_unavailable");const r=await I.status({machineToken:t,roomId:o.roomId,bindingId:o.bindingId});return{roomName:r.room.name,memberCount:r.room.memberCount,members:r.room.members,agentsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent}:{},agentSystemPromptsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,t)=>{const o=p().machineToken;if(!o)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:o,activationCauseMessageId:t})},audit:new yo,resolveManagedAgentPolicy:e=>So({...e,runtime:y.find(t=>t.type===e.registration.runtimeAdapter)})}),a.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),a.setManagedAgentDirectTurnPreparer(e=>S.prepareDirectConversationTurn(e)),a.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),j.mkdirSync(de,{recursive:!0}),ne(process.pid,c,{version:g}),re(process.pid,void 0,c),m.connect(),process.stdin.resume();let Y=!1;const Z=async()=>{if(!Y){Y=!0,console.log(k.gray(`
|
|
1
|
+
import{Command as fe}from"commander";import k from"chalk";import he from"node:os";import j from"node:fs";import{loadConfig as p,saveConfig as ve,getConfigPath as Ie,getShennianDir as Se,resolveShennianPath as H,normalizeAutoUpgradePolicy as ye}from"./config/index.js";import{CliRelayClient as we}from"./relay/client.js";import{collectKnownLogSecrets as Q,installCliLogRedaction as Ce,sanitizeCliLogValue as Re}from"./log-redaction.js";import{registerPairCommand as be,runSmartStart as ke}from"./commands/pair.js";import{registerLogoutCommand as Pe}from"./commands/logout.js";import{clearDaemonPidIfOwner as X,createDaemonInstanceId as Ee,clearDaemonLauncher as ee,findRunningDaemonProcessIds as Ae,isRemoteAccessDisabled as oe,prepareRemoteAccessPause as Ue,stopPreparedRemoteAccessService as De,isIsolatedDaemonE2eInstance as Te,getDaemonStatus as Me,registerDaemonCommand as _e,writeDaemonPid as ne,writeDaemonLauncher as re}from"./commands/daemon.js";import{registerRuntimeCommand as $e}from"./commands/runtime.js";import{registerAgentCommand as Ne}from"./commands/agent.js";import{registerManagerCommand as Oe}from"./commands/manager.js";import{handleUpgradeStart as xe,registerUpgradeCommand as Ve}from"./commands/upgrade.js";import{registerToolsCommand as Le}from"./commands/tools.js";import{registerIntegrationCommand as je}from"./commands/integration.js";import{registerRoomCommand as He}from"./commands/room.js";import{SessionManager as Fe}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Be}from"./session/handlers/project-fs.js";import{SERVERS as F,regionToUrl as Ge,urlToRegion as qe}from"./region.js";import{clearUpgradeAttempt as ze,getCurrentVersion as te,handleStartupCrashCheck as Je,readUpgradeAttempt as Ke}from"./upgrade/engine.js";import{AutoUpgradeScheduler as We}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as Ye}from"./upgrade/updater-bootstrap.js";import{launchUpdater as Ze,markUpdaterHealthy as Qe}from"./upgrade/updater-launcher.js";import{detectAgents as Xe}from"./agents/detect.js";import{augmentProcessPath as ae}from"./env-path.js";import{acquireDaemonInstanceLock as eo}from"./commands/daemon-instance-lock.js";ae();const B=te();import{getCachedAgentInfos as oo,resolveAgentInfos as no}from"./agents/model-registry.js";import{initCliLogReporter as ro,reportLog as P,reportUpgradeAggregateMetric as to}from"./log-reporter.js";import{isNativeFusionEnabled as ao}from"./native-fusion/config.js";import{NativeSessionFusionService as so}from"./native-fusion/service.js";import{startDaemonLogRetention as io}from"./daemon-log.js";import{startDaemonIpcServer as se}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as co,resolveManagedRoomIpcPaths as lo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as mo}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as po}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as go}from"./room/session-registry.js";import{HttpRoomBindingClient as uo}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as fo}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as ho}from"./room/host-session-observations.js";import{RoomMirrorSyncService as vo}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Io}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as So,isCurrentRegistration as yo}from"./room/registration-authority.js";import{RoomActivationService as wo,resolveManagedAgentPolicy as Co}from"./room/activation-service.js";import{RoomActivationAudit as Ro}from"./room/activation-audit.js";import{RoomAttachmentCache as bo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as ko}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Po}from"./room/managed-context.js";import{createManagedRoomExecutor as Eo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ie,SESSION_SYNC_ADVERTISED_CAPABILITIES as ce}from"@shennian/wire";import{getDaemonInstallationId as Ao}from"./session/store.js";import{synchronizeMachinePersonalAuthority as Uo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as le,ensurePersonalDataEpoch as Do}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as To,DeletionSyncTerminalError as Mo}from"./session/deletion-sync-state.js";const de=Se(),_o=H("daemon.pid"),$o=H("daemon.instance.lock");function No(){try{const n=j.readFileSync(_o,"utf-8").trim();if(n.startsWith("{")){const d=JSON.parse(n),c=Number(d.pid);return Number.isInteger(c)&&c>0?c:null}const i=parseInt(n,10);return Number.isInteger(i)&&i>0?i:null}catch{return null}}function Oo(n){return n.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function G(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
|
|
2
|
+
`),process.exitCode=1}function xo(n){const i=[`trigger=${n.trigger??"unknown"}`,`phase=${n.phase}`,`code=${n.code??"unknown"}`,`attempt=${n.reconnectAttempt}`];return n.reason&&i.push(`reason=${n.reason}`),n.error&&i.push(`error=${n.error}`),i.join(" ")}const s=new fe;s.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(B),s.enablePositionalOptions(),je(s),He(s),s.option("--api <url>","Server URL override").option("--name <name>","Machine name",he.hostname()).action(async n=>{const i=p(),d=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??i.serverUrl??void 0;await ke(d,n.name)}),s.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 i=H("env.json");try{const e=JSON.parse(j.readFileSync(i,"utf-8"));for(const[t,o]of Object.entries(e))process.env[t]||(process.env[t]=o);ae()}catch{}Ce(Q(process.env,p()));const d=io();oe()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(d),process.exit(0));const c=Ee(),v=eo({lockPath:$o,instanceId:c});v.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(v.ownerPid?` (PID ${v.ownerPid})`:"")+", skipping duplicate start"),clearInterval(d),process.exit(0));try{const e=No();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),v.release(),process.exit(0)}catch{}}catch{}const q=Te(process.env,de)?[]:Ae(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),v.release(),process.exit(0)),ne(process.pid,c,{version:B}),re(process.pid,void 0,c),process.on("exit",()=>{X(process.pid,c),ee(c)}),process.on("exit",v.release);let N=null;try{N=Ye()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Je();if(E){N||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{Ze({fromVersion:B,targetVersion:E.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:E.jobId},{readiness:N}),console.error(`[upgrade] New version crashed ${E.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Do()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=p(),f=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??F.cn.url,g=te(),y=await Xe({serverUrl:f,authToken:l.machineToken??l.accessToken}),w=y.map(e=>e.type),O=oo(y),h=new go,A=new ho,C=new Po(h),I=new uo(f),pe=new bo({actionClient:I}),ge=new ko({actionClient:I});let S=null,a;const z=Eo({authority:C,actionClient:I,attachmentCache:pe,attachmentUploader:ge,observations:A,getMachineCredentials:()=>({machineToken:p().machineToken}),onPaused:({registryKey:e,sessionId:t})=>{const r=h.getByRegistryKey(e)?.binding;r&&h.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),a?.discardPendingManagedRoomMessages(t)}});let x=null,V=null,L=null;const R=await se({paths:co(),handlers:mo({daemonVersion:g,protocolVersion:po,getStatus:()=>Me(),getAgents:()=>y,completeAgentAuthentication:e=>a.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!x)throw new Error("upgrade_unavailable");return x(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let b;try{b=await se({paths:lo(),handlers:{"managed-room.execute":e=>z(e)}})}catch(e){throw await R.close(),e}let J=!1;const U=()=>{J||(J=!0,Promise.allSettled([R.close(),b.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",U),process.once("SIGTERM",U),!l.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([R.close(),b.close()]),process.exitCode=1;return}process.removeListener("SIGINT",U),process.removeListener("SIGTERM",U);const K=`${Oo(f)}/relay/machine`,D=l.machineToken;if(!D)return;try{await Uo({serverUrl:f,machineToken:D,machineId:l.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([R.close(),b.close()]),process.exit(1)}l.machineId&&ro(f,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${g}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:g,agents:w}});let T=null,M=null,_=0,u=Ke();const W=ye(l.autoUpgrade),m=new we({serverUrl:K,machineToken:D,cliVersion:g,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ie,sessionSyncCapabilities:ce,daemonInstallationId:Ao(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),O.some(o=>o.models.length>0)&&m.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:O}}),no(y,{serverUrl:l.serverUrl??f,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(O)&&m.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),u&&m.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:u.from,to:u.to,...u.jobId?{jobId:u.jobId}:{},activeRunCount:a.getPendingRecoveryCount()}}),$.start(),M?.start();const e=++_,t=async()=>{try{if(await a.synchronizeSessionDeletions(),await a.recoverInterruptedRuns(),u){const o=u;u=null,o.jobId&&Qe(o.jobId,o.to),ze(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),to({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),m.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&m.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&m.getState()==="connected"&&T?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&m.getState()==="connected"){if(o instanceof Mo)return;const r=o instanceof To?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{t()},r).unref?.()}}};t()},onDisconnected:e=>{_+=1,a?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${xo(e)}`),P({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||a?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),a.handleReq(e)}});T=ao()?new so(m):null,M=new vo({registry:h,observations:A,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Io().hasSession(e),onMessages:async(e,t,o,r)=>{await S?.processMessages(e,t,o,r)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?a?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):a?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new We({currentVersion:g,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??f,getIdleState:()=>a.getUpgradeIdleState(),sessionSyncProtocolVersion:ie,dataEpoch:le,advertisedCapabilities:ce,updaterProtocolVersion:1,startUpgrade:async(e,t)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(m,"auto-upgrade",e,{currentVersion:g,prepareForUpgrade:r=>a.prepareForUpgrade(r),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:t?void 0:()=>a.getUpgradeIdleState().idle})}});$.setPolicy(W),a=new Fe(m,T,g,$,new fo({registry:h,observations:A,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Ue(),isDisabled:()=>oe(),shutdownAfterResponse:()=>{setTimeout(()=>{De(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Be(D)),x=async e=>{const t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof t.targetVersion=="string"?t.targetVersion:void 0,r=t.confirmedMajor===!0;return await xe(m,`local-upgrade-${Date.now()}`,o,{currentVersion:g,confirmedMajor:r,prepareForUpgrade:ue=>a.prepareForUpgrade(ue),cancelUpgradePreparation:()=>a.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof t.targetVersion=="string"?t.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...a.prepareForUpgrade(r)}},L=()=>(a.cancelUpgradePreparation(),{cancelled:!0}),S=new wo({registry:h,observations:A,enqueueConversationBatch:e=>a.enqueueManagedRoomBatch(e),loadContext:async e=>{const t=p().machineToken?.trim(),o=e.binding;if(!t||!o)throw new Error("managed_room_context_unavailable");const r=await I.status({machineToken:t,roomId:o.roomId,bindingId:o.bindingId});if(!yo(h,e)||!So(e,r))throw new Error("managed_room_binding_unavailable");return{roomName:r.room.name,memberCount:r.room.memberCount,members:r.room.members,agentsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent}:{},agentSystemPromptsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,t)=>{const o=p().machineToken;if(!o)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:o,activationCauseMessageId:t})},audit:new Ro,resolveManagedAgentPolicy:e=>Co({...e,runtime:y.find(t=>t.type===e.registration.runtimeAdapter)})}),a.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),a.setManagedAgentDirectTurnPreparer(e=>S.prepareDirectConversationTurn(e)),a.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),j.mkdirSync(de,{recursive:!0}),ne(process.pid,c,{version:g}),re(process.pid,void 0,c),m.connect(),process.stdin.resume();let Y=!1;const Z=async()=>{if(!Y){Y=!0,console.log(k.gray(`
|
|
3
3
|
Disconnecting...`)),P({level:"info",wsEvent:"daemon.stop"}),clearInterval(d);try{await a.cleanup(),$.stop(),T?.stop(),M?.stop(),m.disconnect(),await Promise.allSettled([R.close(),b.close()]),X(process.pid,c),ee(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{Z()}),process.on("SIGTERM",()=>{Z()})}),s.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>G()),s.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>G()),s.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>G());const me=s.command("config").description("View or modify configuration");me.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${Ie()}`);const n=p(),i=Re(n,Q(process.env,n));if(n.serverUrl){const d=qe(n.serverUrl);console.log(`Region: ${d} (${F[d].label})`)}console.log(JSON.stringify(i,null,2))}),me.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,i)=>{const d=p();if(n==="server"){i!=="cn"&&i!=="global"&&(console.error(k.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=i;d.serverUrl=Ge(c),ve(d),console.log(k.green(`\u2713 Server set to ${F[c].label}`)),console.log(k.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(k.red(`\u2717 Unknown config key: ${n}. Supported: server`)),process.exit(1)}),be(s),Pe(s),_e(s),$e(s),Ne(s),Oe(s),Ve(s),Le(s),s.parse();
|
|
@@ -34,6 +34,9 @@ export type ManagerRecord = {
|
|
|
34
34
|
providerModelId?: string | null;
|
|
35
35
|
defaultWorkerAgentType?: AgentType | null;
|
|
36
36
|
defaultWorkerModelId?: string | null;
|
|
37
|
+
/** This Manager Session originated from a managed Room and every synthetic
|
|
38
|
+
* continuation must reacquire that Room's current trusted context. */
|
|
39
|
+
managedRoomBound?: boolean;
|
|
37
40
|
status: ManagerStatus;
|
|
38
41
|
/** Inbox events remain durable until the provider emits `start`. This makes
|
|
39
42
|
* continuation delivery at-least-once across daemon crashes and dispatch
|
|
@@ -85,6 +88,7 @@ export declare class ManagerRegistry {
|
|
|
85
88
|
providerModelId?: string | null;
|
|
86
89
|
defaultWorkerAgentType?: AgentType | null;
|
|
87
90
|
defaultWorkerModelId?: string | null;
|
|
91
|
+
managedRoomBound?: boolean;
|
|
88
92
|
status?: ManagerStatus;
|
|
89
93
|
deliveringInboxEventIds?: string[];
|
|
90
94
|
}): ManagerRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import u from"node:fs";import S from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as v,sessionPreviewFromPayload as w}from"@shennian/wire";import{resolveShennianPath as A}from"../config/index.js";import{listSessionRecords as f,readMessages as T}from"../session/store.js";import{listProjectedSessions as M}from"../session/projection.js";const I=A("manager-registry.json");function g(){return new Date().toISOString()}function W(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class
|
|
1
|
+
import u from"node:fs";import S from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as v,sessionPreviewFromPayload as w}from"@shennian/wire";import{resolveShennianPath as A}from"../config/index.js";import{listSessionRecords as f,readMessages as T}from"../session/store.js";import{listProjectedSessions as M}from"../session/projection.js";const I=A("manager-registry.json");function g(){return new Date().toISOString()}function W(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class B{load(){try{const e=JSON.parse(u.readFileSync(I,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return W()}}save(e){u.mkdirSync(S.dirname(I),{recursive:!0}),u.writeFileSync(I,JSON.stringify(e,null,2))}upsertManager(e){const t=this.load(),r=t.managers[e.sessionId],s=g(),a={sessionId:e.sessionId,agentSessionId:e.agentSessionId??r?.agentSessionId??null,workDir:e.workDir,machineId:e.machineId??r?.machineId??null,modelId:e.modelId,agentType:e.agentType??r?.agentType??"manager",providerModelId:e.providerModelId??r?.providerModelId??null,defaultWorkerAgentType:e.defaultWorkerAgentType??r?.defaultWorkerAgentType??null,defaultWorkerModelId:e.defaultWorkerModelId??r?.defaultWorkerModelId??null,managedRoomBound:e.managedRoomBound??r?.managedRoomBound??!1,status:e.status??r?.status??"idle",deliveringInboxEventIds:e.deliveringInboxEventIds??r?.deliveringInboxEventIds??[],managedWorkerSessionIds:r?.managedWorkerSessionIds??[],attachedExternalChannels:r?.attachedExternalChannels??[],createdAt:r?.createdAt??s,updatedAt:s};return t.managers[e.sessionId]=a,this.save(t),a}getManager(e){return this.load().managers[e]}enqueueInboxEvent(e){const t=this.load(),r=t.managers[e.managerSessionId],s=t.workers[e.workerSessionId];if(!r||!s||s.managedBy!==r.sessionId||s.workDir!==r.workDir)return null;const a=g(),o=t.inbox[e.managerSessionId]??[],d=o.findIndex(c=>c.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?c.kind==="worker.health":c.kind===e.kind&&c.runId===e.runId)),l=d>=0?{...o[d],...e,updatedAt:a}:{...e,id:`mie_${m()}`,createdAt:a,updatedAt:a};return d>=0?o[d]=l:o.push(l),t.inbox[e.managerSessionId]=o,this.save(t),l}listInboxEvents(e){return[...this.load().inbox[e]??[]].sort((t,r)=>t.createdAt.localeCompare(r.createdAt))}removeInboxEvents(e,t){if(!t.length)return;const r=this.load(),s=new Set(t);r.inbox[e]=(r.inbox[e]??[]).filter(a=>!s.has(a.id)),this.save(r)}addWorker(e){const t=this.load(),r=t.managers[e.managerSessionId];if(!r)throw new Error("Manager runtime is not registered");const s=e.sessionId??`sess_worker_${m()}`,a=g(),o={sessionId:s,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:a,updatedAt:a,lastActivityAt:a,summary:e.summary??null,agentSessionId:null,runId:null};return t.workers[s]=o,r.managedWorkerSessionIds.includes(s)||r.managedWorkerSessionIds.push(s),r.updatedAt=a,this.save(t),o}updateWorker(e,t){const r=this.load(),s=r.workers[e];if(!s)return;const a={...s,...t,updatedAt:g()};return r.workers[e]=a,this.save(r),a}listWorkers(e,t={}){const r=this.load(),s=r.managers[e];if(!s)return[];const a=Object.values(r.workers).filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").map(n=>this.decorateManagedWorker(n,t.runningSessionIds)),o=new Set(a.map(n=>n.sessionId)),d=f().filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").filter(n=>!o.has(n.sessionId)).map(n=>this.sessionRecordToWorker(n,t.runningSessionIds)),l=new Set([...a,...d].map(n=>n.sessionId)),h=(t.projectedSessions??M()).filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").filter(n=>!l.has(n.id)).map(n=>this.projectedSessionToWorker(n,t.runningSessionIds));return[...a,...d,...h].sort((n,k)=>k.lastActivityAt.localeCompare(n.lastActivityAt))}getWorkerForManager(e,t){const r=this.load(),s=r.managers[e],a=r.workers[t];if(!s)return;if(a)return a.workDir!==s.workDir||a.agentType==="manager"?void 0:this.decorateManagedWorker(a);const o=f().find(d=>d.sessionId===t);if(!(!o||o.workDir!==s.workDir||o.agentType==="manager"))return this.sessionRecordToWorker(o)}decorateManagedWorker(e,t){const r=t?.has(e.sessionId)?"running":e.status,s=this.readTranscriptSummary(e.sessionId,e.summary??null),a=s.lastMessagePreview??e.lastMessagePreview??e.summary??null,o=e.userGoal??s.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??o??a??e.summary??e.sessionId,userGoal:o,lastMessagePreview:a,summary:e.summary??a,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}sessionRecordToWorker(e,t){const r=this.readTranscriptSummary(e.sessionId,e.lastMessagePreview??null),s=r.lastMessagePreview??e.lastMessagePreview??null,a=r.userGoal,o=t?.has(e.sessionId)?"running":e.status==="failed"?"error":e.status==="completed"?"final":"idle";return{sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,managedBy:null,status:o,createdAt:e.createdAt,updatedAt:e.updatedAt,lastActivityAt:e.lastActivityAt,title:a??s??e.sessionId,userGoal:a,lastMessagePreview:s,summary:s??a??null,agentSessionId:e.agentSessionId??null,runId:null,canResume:!!e.agentSessionId,source:"local_index",role:"project_session"}}projectedSessionToWorker(e,t){const r=e.lastMessagePreview??null,s=e.title?.trim()||r,a=t?.has(e.id)?"running":e.status==="failed"?"error":e.status==="completed"?"final":"idle";return{sessionId:e.id,agentType:e.agentType,workDir:e.workDir,managedBy:null,status:a,createdAt:e.createdAt,updatedAt:e.updatedAt,lastActivityAt:e.lastActivityAt||e.updatedAt,title:s??e.id,userGoal:s??null,lastMessagePreview:r,summary:r??s??null,agentSessionId:e.agentSessionId??null,runId:e.activity?.runId??null,canResume:!0,source:"app_projection",role:"project_session"}}readTranscriptSummary(e,t){const r=T(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:t};const s=[...r].sort((d,l)=>d.ts-l.ts),a=s.find(d=>d.role==="user"),o=[...s].reverse().find(d=>y(d));return{userGoal:a?p(y(a),160):null,lastMessagePreview:p(y(o),180)??t}}createReplyTarget(e){const t=this.load(),r={replyTarget:`rt_${m()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:g()};return t.replyTargets[r.replyTarget]=r,this.save(t),r}getReplyTarget(e){return this.load().replyTargets[e]}getLatestReplyTargetForManager(e){return Object.values(this.load().replyTargets).filter(t=>t.managerSessionId===e).sort((t,r)=>r.createdAt.localeCompare(t.createdAt))[0]}}function y(i){return!i||v(i.payload)?null:w(i.payload,180)}function p(i,e){return i?i.length>e?`${i.slice(0,e)}...`:i:null}export{B as ManagerRegistry};
|
|
@@ -49,6 +49,7 @@ export declare class ManagerRuntimeService {
|
|
|
49
49
|
providerModelId?: string | null;
|
|
50
50
|
defaultWorkerAgentType?: AgentType | null;
|
|
51
51
|
defaultWorkerModelId?: string | null;
|
|
52
|
+
managedRoomBound?: boolean;
|
|
52
53
|
status?: 'idle' | 'running' | 'interrupting';
|
|
53
54
|
}): void;
|
|
54
55
|
setManagedAgentPolicy(sessionId: string, policy: CompiledManagedAgentPolicy | null): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Y from"node:http";import{createHash as V,randomBytes as Q,randomUUID as
|
|
1
|
+
import Y from"node:http";import{createHash as V,randomBytes as Q,randomUUID as m}from"node:crypto";import R from"node:fs";import T from"node:os";import w from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as X,extractPayloadText as Z,formatExternalConversationText as ee,formatExternalMessageLine as te,formatExternalAttachmentReference as ne,isAgentHiddenPayload as re,isToolPayload as ae}from"@shennian/wire";import{ManagerRegistry as se}from"./registry.js";import{readMessages as ie}from"../session/store.js";import{listProjectedSessions as oe}from"../session/projection.js";import{RetiredChannelRuntime as le,RetiredWeChatRpaSessionBindingSync as ce,createRetiredAutomationLane as de,createRetiredChannelApiClient as ue,retiredDirectWeChat as he,retiredDirectWeChat as $,splitExternalReplyText as ge,weChatChannelConversationId as me}from"./retired-external-channel-compat.js";import{loadConfig as D,resolveShennianPath as pe}from"../config/index.js";import{buildExternalChannelInstructions as fe}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as C}from"../personal-sync/epoch.js";const A=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),x=12*6e4;let _=null;function Ve(a){_=a}function Qe(){return _}function I(a){return w.resolve(a||T.homedir())}function u(a,e,n){a.writeHead(e,{"content-type":"application/json; charset=utf-8"}),a.end(JSON.stringify(n))}function ye(a){return/^agent-(.+)-\d+$/.exec(a)?.[1]??null}function we(a){return a==="manager"||a==="pi"?!1:a.startsWith("custom:")?!0:X.includes(a)}function Ie(a){const e=/^agent-.+-(\d+)$/.exec(a);if(!e)return null;const n=Number(e[1]);return Number.isInteger(n)&&n>=0?n:null}function B(a){return a.replace(/\r\n/g,`
|
|
2
2
|
`).trim()}function ke(a){const e=a.map((n,t)=>[`${t+1}. \u4E8B\u4EF6\u7C7B\u578B\uFF1A${n.kind}`,`Worker\uFF1A${n.workerSessionId}`,n.runId?`Run\uFF1A${n.runId}`:"",`\u6458\u8981\uFF1A
|
|
3
3
|
${n.summary}`].filter(Boolean).join(`
|
|
4
4
|
`));return`Manager \u4E8B\u4EF6\u6536\u4EF6\u7BB1\u4E2D\u6709 ${a.length} \u6761\u5F85\u5904\u7406\u4E8B\u4EF6\uFF1A
|
|
@@ -7,19 +7,19 @@ ${e.join(`
|
|
|
7
7
|
|
|
8
8
|
`)}
|
|
9
9
|
|
|
10
|
-
\u8BF7\u7ED3\u5408\u7528\u6237\u76EE\u6807\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA\u6216\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`}function Se(a,e){return`manager-inbox:${V("sha256").update(a).update("\0").update(e.map(t=>t.id).join("\0")).digest("hex").slice(0,32)}`}function Me(a){try{const e=JSON.parse(a),n=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",r=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=r.length>220?`${r.slice(0,220)}...`:r;return s?`[${n}] ${t}: ${s}`:`[${n}] ${t}`}catch{return"[tool]"}}function q(a){if(!a||typeof a!="object")return;const e=a,n=String(e.kind||""),t=String(e.name||""),r=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(n!=="image"&&n!=="video"&&n!=="file")&&!(!t||!r||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:n,name:t,mimeType:r,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function L(a){const e=a.binding&&typeof a.binding=="object"&&!Array.isArray(a.binding)?a.binding:{},n=String(e.sessionId||a.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),r=String(e.conversationId||"").trim(),s=String(e.conversationName||a.conversation||"").trim(),o=String(e.workDir||a.workDir||"").trim();if(!n)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!r)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:n,channelId:t,conversationId:r,conversationName:s,workDir:o}}function Re(a,e){const n=[...a].sort((l,d)=>l.ts-d.ts),t=[];let r=null,s=null,o=null,i="";const c=()=>{if(!r)return;const l=i.trim();l&&t.push({...r,id:`${r.id}-compact`,payload:l}),r=null,s=null,o=null,i=""};for(const l of n){if(re(l.payload)){c();continue}if(l.role==="user"){c(),t.push(l);continue}if(ae(l.payload)){c(),t.push({...l,payload:Me(l.payload)});continue}const d=Z(l.payload);if(!d.trim())continue;const h=ye(l.id),g=Ie(l.id);r&&r.role===l.role&&s===h&&h&&g!==null&&o!==null&&g===o+1?(i+=d,r.ts=l.ts,o=g):(c(),r=l,s=h,o=g,i=d)}return c(),t.slice(-e).sort((l,d)=>d.ts-l.ts)}async function Ce(a){const e=[];let n=0;for await(const r of a){const s=Buffer.from(r);if(n+=s.byteLength,Number.isFinite(A)&&A>0&&n>A)throw new Error(`Manager IPC request body is too large. Max: ${A} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function f(a,e,n,t){a.client.sendRes({type:"res",id:e,ok:n,...n?{payload:t}:{error:String(t.error||"unknown error")}})}function F(a){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(a)}function O(){return Ae()?w.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):me("runtime","manager-ipc.json")}function Ae(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Xe{opts;managedPolicies=new Map;registry=new se;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=Q(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??de(),this.channelRuntime=e.channelRuntime??new le((n,t)=>{this.handleExternalMessage(n,t)},n=>this.registry.createReplyTarget(n).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new ce({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||D().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts(),listAuthoritativeSessions:()=>this.listAuthoritativeWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[n,t]of e)yield{sessionId:n,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async listAuthoritativeWeChatRpaSessionContexts(){const e=D(),n=process.env.SHENNIAN_MACHINE_ID||e.machineId||"";if(!e.machineToken||!n)return null;const t=this.listLocalWeChatRpaSessionContexts(n);return(await ue({serverUrl:e.serverUrl,machineToken:e.machineToken}).listBindings({machineId:n})).bindings.filter(s=>s.enabled!==!1).map(s=>{const o=t.get(s.sessionId);return{sessionId:s.sessionId,workDir:s.sessionWorkDir?.trim()||o?.workDir?.trim()||T.homedir(),agentType:s.sessionAgentType?.trim()||o?.agentType||void 0,agentSessionId:s.sessionAgentSessionId??o?.agentSessionId??null,modelId:s.sessionModelId??o?.modelId??null,externalChannel:{connected:!0,configured:!0,type:"wechat-rpa",channelId:s.id,machineId:s.machineId,name:s.conversationName,canReply:s.allowReply!==!1,systemPrompt:null,wechatRpaSource:"wechat-channel",wechatRpaGroups:[{name:s.conversationName}],downloadAttachments:s.downloadMedia!==!1}}})}listLocalWeChatRpaSessionContexts(e){const n=new Map;for(const t of this.listWeChatRpaSessionContexts())n.set(t.sessionId,t);for(const t of oe())!t?.id||t.deletedAt||t.machineId&&e&&t.machineId!==e||n.has(t.id)||n.set(t.id,{sessionId:t.id,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,modelId:t.modelId,externalChannel:t.externalChannel??null});return n}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(n=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${n instanceof Error?n.message:String(n)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=Y.createServer((n,t)=>{this.handleIpc(n,t)}),this.server.requestTimeout=x,this.server.timeout=x,this.server.keepAliveTimeout=x,this.server.headersTimeout=x+5e3,await new Promise((n,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>n())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}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=O();M.mkdirSync(w.dirname(e),{recursive:!0}),M.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),M.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=O(),n=JSON.parse(M.readFileSync(e,"utf-8"));(n.url===this.ipcUrl||n.token===this.ipcToken)&&M.unlinkSync(e)}catch{}}getInjectedEnv(e,n,t,r){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:n??"",SHENNIAN_MANAGER_WORKDIR:I(t),SHENNIAN_MANAGER_MODEL:r,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:I(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagedAgentPolicy(e,n){n?this.managedPolicies.set(e,n):this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,n){const t=this.registry.getManager(e),r=this.managedPolicies.get(e);return!t||!r||I(n)!==t.workDir?null:r}setManagerWorkerDefaults(e,n,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,defaultWorkerAgentType:n??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const n=this.registry.getManager(e);return{agentType:n?.defaultWorkerAgentType??null,modelId:n?.defaultWorkerModelId??null}}noteManagerAgentSession(e,n,t,r){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:n,workDir:I(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:r,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,n){if(!this.findWorker(e))return;const r={lastActivityAt:new Date().toISOString(),runId:n.runId};n.agentSessionId&&(r.agentSessionId=n.agentSessionId);const s=`${e}:${n.runId}`;if(n.state==="delta"&&n.text&&!n.thinking){const i=(this.workerTextAcc.get(s)??"")+n.text;this.workerTextAcc.set(s,i);const c=B(i);c&&(r.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(n.state==="final"||n.state==="error"||n.state==="aborted"){r.status=n.state;const i=B(this.workerTextAcc.get(s)??"");i&&(r.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else n.state==="start"&&(r.status="running");const o=this.registry.updateWorker(e,r);o&&(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.enqueueWorkerEvent(o.managedBy,o,n.state,n.runId,n.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const n=this.opts.getRuntime();if(e.method.includes("channel")||e.method.includes("wechat-rpa")){f(n,e.id,!1,{error:"feature_retired"});return}const t=e.params??{};try{const r=String(t.managerSessionId||t.sessionId||"");if(!r)throw new Error("sessionId is required");const s=this.registry.getManager(r),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",l=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${r}`),managerSessionId:r,sessionId:r,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(o){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"wechat-rpa",{includeSecret:!0})});return}if(i){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${r}`),managerSessionId:r,sessionId:r,workDir:I(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:U(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:H(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(c){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(r);this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}if(l){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:r,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(r),f(n,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(r){f(n,e.id,!1,{error:r instanceof Error?r.message:String(r)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const n=this.opts.getRuntime();if(!n.client?.sendEvent)return;const t=this.registry.getManager(e),r=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");n.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:r}}})}async handleIpc(e,n){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(n,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),r=await Ce(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){u(n,410,{ok:!1,error:"feature_retired"});return}const s=String(r.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(n,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(r.agentType||r.agent||o.defaultWorkerAgentType||"codex");if(!we(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=I(String(r.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const l=String(r.message||"");if(!l)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:l.slice(0,120)}),h=String(r.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,i,c,l,null,h,s),u(n,200,{ok:!0,session:d});return}if(t.pathname==="/sessions/send"){const i=String(r.sessionId||""),c=String(r.message||""),l=r.enqueue===void 0?!0:!!r.enqueue,d=this.registry.getWorkerForManager(s,i);if(!d)throw new Error("Worker not found in this manager scope");const h=String(r.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));l?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h,s):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h,s),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(n,200,{ok:!0,queue:l});return}if(t.pathname==="/sessions/queue/edit"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${p()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||""),text:String(r.message||r.text||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${p()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(r.sessionId||""),c=Number(r.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=ie(i,{limit:Math.max(c*20,c)});u(n,200,{ok:!0,messages:Re(d,c),rawMessageCount:d.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(n,200,{ok:!0,path:w.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof r.replyTarget=="string"?this.registry.getReplyTarget(r.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(r.text||""),l=q(r.attachment),d=String(r.idempotencyKey||p()),h=String(r.channelId||""),g=String(r.conversationId||""),R=!i&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,k=i?.channelId||h||R?.channelId||"",v=i?.conversationId||g||R?.conversationId||"",P=k?this.channelRuntime.getChannelById(k):void 0,J=P&&(!!(i||h)||P.managedBy!=="session-sync");if(k&&J){if(!v)throw new Error("No external channel target is available for this Manager");const y=await this.channelRuntime.reply({managerSessionId:s,channelId:k,conversationId:v,messageId:i?.messageId??void 0,text:c,attachment:l,idempotencyKey:d});u(n,y.ok?200:400,y);return}const b=await this.tryDirectWeChatRpaReply(s,c,l);if(b){u(n,b.ok?200:400,b);return}let S;try{S=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:l,idempotencyKey:d})}catch(y){const W=y instanceof Error?y.message:String(y);if(!F(W))throw y;S={ok:!1,error:W}}if(S.ok){u(n,200,{ok:!0,payload:S.payload});return}if(!F(S.error||"")||!k||!v){u(n,400,{ok:!1,error:S.error||"External send failed"});return}u(n,400,{ok:!1,error:`No local external channel is configured for ${k}`});return}if(t.pathname==="/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(r.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof r.name=="string"?r.name:void 0,enabled:!!r.enabled,wsUrl:typeof r.wsUrl=="string"?r.wsUrl:void 0,token:typeof r.token=="string"?r.token:void 0,canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=L(r),c=m(r.limit)??10,l=typeof r.traceId=="string"?r.traceId:void 0;let d;try{d=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>he(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:r.download==="never"?"never":"auto",traceId:l,timeoutMs:m(r.timeoutMs)}))}catch(h){u(n,400,{ok:!1,...z(h,l)});return}u(n,200,{ok:!0,messages:d.messages,outDir:d.outDir,helperTracePath:d.helperTracePath,activityGuardPath:d.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=L(r),c=String(r.text||""),l=q(r.attachment),d=typeof r.traceId=="string"?r.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>$(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:l,traceId:d,timeoutMs:m(r.timeoutMs)}))}catch(g){u(n,400,{ok:!1,...z(g,d)});return}u(n,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=I(String(r.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(r.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof r.name=="string"?r.name:void 0,agentType:typeof r.agentType=="string"?r.agentType:void 0,agentSessionId:typeof r.agentSessionId=="string"?r.agentSessionId:null,modelId:typeof r.modelId=="string"?r.modelId:null,enabled:!!r.enabled,groups:U(r.groups),canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0,source:H(r.source),pollIntervalMs:m(r.pollIntervalMs),recentLimit:m(r.recentLimit),idleSeconds:m(r.idleSeconds),forceForeground:r.forceForeground===void 0?void 0:!!r.forceForeground,noRestore:r.noRestore===void 0?void 0:!!r.noRestore,downloadAttachments:r.downloadAttachments===void 0?void 0:!!r.downloadAttachments,downloadAttachmentsDir:typeof r.downloadAttachmentsDir=="string"?r.downloadAttachmentsDir:void 0,selfNickname:typeof r.selfNickname=="string"?r.selfNickname:void 0,selfTriggerMarker:typeof r.selfTriggerMarker=="string"?r.selfTriggerMarker:void 0,deferInitialPoll:r.deferInitialPoll===void 0?void 0:!!r.deferInitialPoll,privacyConsentAccepted:r.privacyConsentAccepted===void 0?void 0:!!r.privacyConsentAccepted,flowScriptPath:typeof r.flowScriptPath=="string"?r.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof r.idempotencyKey=="string"?r.idempotencyKey:null,replyId:typeof r.replyId=="string"?r.replyId:null,reason:typeof r.reason=="string"?r.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,...i});return}u(n,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(n,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,n,t,r,s,o,i){await this.opts.dispatchReq({type:"req",id:`manager-send-${p()}`,method:"chat.send",params:{...C(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}async dispatchChatEnqueue(e,n,t,r,s,o,i){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${p()}`,method:"chat.enqueue",params:{...C(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}async tryDirectWeChatRpaReply(e,n,t){const r=this.opts.getRuntime().sessions.get(e),s=r?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||D().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!n.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=r?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:pe(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>$(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:n,attachment:t}))}}catch(l){return{ok:!1,error:l instanceof Error?l.message:String(l)}}}async sendManagedWeComReply(e){const n=ge(e.text);if(!n.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const r=[];for(const[s,o]of n.entries()){const i=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:n.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};r.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:n.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};r.push(s.payload)}return{ok:!0,payload:r.length===1?r[0]:r}}enqueueWorkerEvent(e,n,t,r,s){this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:n.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:r??null,summary:s||n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"})&&this.drainManagerInbox(e)}handleExternalMessage(e,n){const t=this.channelRuntime.getChannelById(n.channelId)??this.channelRuntime.getManagerChannel(e,n.channelType),r=this.channelRuntime.getChannelStatusById(n.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),c=t?.agentSessionId??s?.agentSessionId??null,l=t?.modelId||s?.modelId||"",d=xe(n.attachments,n.channelType),h=ve(n);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:c,modelId:l,text:h,messageId:n.messageId,attachments:d,externalChannel:De(r),replyTarget:n.replyTarget})}async dispatchExternalMessage(e){const n=typeof e.messageId=="string"&&e.messageId.trim()?e.messageId.trim():void 0;await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{...C(),sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",queueMessageId:n,clientMessageId:n,attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),n=this.registry.load();for(const t of Object.values(n.workers)){if(t.status!=="running")continue;const r=e-Date.parse(t.createdAt);if(r<10*6e4)continue;const s=t.healthNotifiedAt?Date.parse(t.healthNotifiedAt):0;if(s&&e-s<10*6e4)continue;const o=n.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),c=`\u5DF2\u8FD0\u884C ${Math.floor(r/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
10
|
+
\u8BF7\u7ED3\u5408\u7528\u6237\u76EE\u6807\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA\u6216\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`}function Se(a,e){return`manager-inbox:${V("sha256").update(a).update("\0").update(e.map(t=>t.id).join("\0")).digest("hex").slice(0,32)}`}function Re(a){try{const e=JSON.parse(a),n=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",r=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=r.length>220?`${r.slice(0,220)}...`:r;return s?`[${n}] ${t}: ${s}`:`[${n}] ${t}`}catch{return"[tool]"}}function q(a){if(!a||typeof a!="object")return;const e=a,n=String(e.kind||""),t=String(e.name||""),r=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(n!=="image"&&n!=="video"&&n!=="file")&&!(!t||!r||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:n,name:t,mimeType:r,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function L(a){const e=a.binding&&typeof a.binding=="object"&&!Array.isArray(a.binding)?a.binding:{},n=String(e.sessionId||a.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),r=String(e.conversationId||"").trim(),s=String(e.conversationName||a.conversation||"").trim(),o=String(e.workDir||a.workDir||"").trim();if(!n)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!r)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:n,channelId:t,conversationId:r,conversationName:s,workDir:o}}function Me(a,e){const n=[...a].sort((l,d)=>l.ts-d.ts),t=[];let r=null,s=null,o=null,i="";const c=()=>{if(!r)return;const l=i.trim();l&&t.push({...r,id:`${r.id}-compact`,payload:l}),r=null,s=null,o=null,i=""};for(const l of n){if(re(l.payload)){c();continue}if(l.role==="user"){c(),t.push(l);continue}if(ae(l.payload)){c(),t.push({...l,payload:Re(l.payload)});continue}const d=Z(l.payload);if(!d.trim())continue;const h=ye(l.id),g=Ie(l.id);r&&r.role===l.role&&s===h&&h&&g!==null&&o!==null&&g===o+1?(i+=d,r.ts=l.ts,o=g):(c(),r=l,s=h,o=g,i=d)}return c(),t.slice(-e).sort((l,d)=>d.ts-l.ts)}async function Ce(a){const e=[];let n=0;for await(const r of a){const s=Buffer.from(r);if(n+=s.byteLength,Number.isFinite(A)&&A>0&&n>A)throw new Error(`Manager IPC request body is too large. Max: ${A} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function f(a,e,n,t){a.client.sendRes({type:"res",id:e,ok:n,...n?{payload:t}:{error:String(t.error||"unknown error")}})}function F(a){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(a)}function O(){return Ae()?w.join(T.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):pe("runtime","manager-ipc.json")}function Ae(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Xe{opts;managedPolicies=new Map;registry=new se;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=Q(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??de(),this.channelRuntime=e.channelRuntime??new le((n,t)=>{this.handleExternalMessage(n,t)},n=>this.registry.createReplyTarget(n).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new ce({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||D().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts(),listAuthoritativeSessions:()=>this.listAuthoritativeWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[n,t]of e)yield{sessionId:n,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async listAuthoritativeWeChatRpaSessionContexts(){const e=D(),n=process.env.SHENNIAN_MACHINE_ID||e.machineId||"";if(!e.machineToken||!n)return null;const t=this.listLocalWeChatRpaSessionContexts(n);return(await ue({serverUrl:e.serverUrl,machineToken:e.machineToken}).listBindings({machineId:n})).bindings.filter(s=>s.enabled!==!1).map(s=>{const o=t.get(s.sessionId);return{sessionId:s.sessionId,workDir:s.sessionWorkDir?.trim()||o?.workDir?.trim()||T.homedir(),agentType:s.sessionAgentType?.trim()||o?.agentType||void 0,agentSessionId:s.sessionAgentSessionId??o?.agentSessionId??null,modelId:s.sessionModelId??o?.modelId??null,externalChannel:{connected:!0,configured:!0,type:"wechat-rpa",channelId:s.id,machineId:s.machineId,name:s.conversationName,canReply:s.allowReply!==!1,systemPrompt:null,wechatRpaSource:"wechat-channel",wechatRpaGroups:[{name:s.conversationName}],downloadAttachments:s.downloadMedia!==!1}}})}listLocalWeChatRpaSessionContexts(e){const n=new Map;for(const t of this.listWeChatRpaSessionContexts())n.set(t.sessionId,t);for(const t of oe())!t?.id||t.deletedAt||t.machineId&&e&&t.machineId!==e||n.has(t.id)||n.set(t.id,{sessionId:t.id,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,modelId:t.modelId,externalChannel:t.externalChannel??null});return n}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(n=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${n instanceof Error?n.message:String(n)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=Y.createServer((n,t)=>{this.handleIpc(n,t)}),this.server.requestTimeout=x,this.server.timeout=x,this.server.keepAliveTimeout=x,this.server.headersTimeout=x+5e3,await new Promise((n,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>n())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}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=O();R.mkdirSync(w.dirname(e),{recursive:!0}),R.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),R.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=O(),n=JSON.parse(R.readFileSync(e,"utf-8"));(n.url===this.ipcUrl||n.token===this.ipcToken)&&R.unlinkSync(e)}catch{}}getInjectedEnv(e,n,t,r){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:n??"",SHENNIAN_MANAGER_WORKDIR:I(t),SHENNIAN_MANAGER_MODEL:r,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:I(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,n){if(n){this.managedPolicies.set(e,n);const t=this.registry.getManager(e);t&&!t.managedRoomBound&&this.registry.upsertManager({...t,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,n){const t=this.registry.getManager(e),r=this.managedPolicies.get(e);return!t||!r||I(n)!==t.workDir?null:r}setManagerWorkerDefaults(e,n,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,defaultWorkerAgentType:n??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const n=this.registry.getManager(e);return{agentType:n?.defaultWorkerAgentType??null,modelId:n?.defaultWorkerModelId??null}}noteManagerAgentSession(e,n,t,r){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:n,workDir:I(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:r,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,n){if(!this.findWorker(e))return;const r={lastActivityAt:new Date().toISOString(),runId:n.runId};n.agentSessionId&&(r.agentSessionId=n.agentSessionId);const s=`${e}:${n.runId}`;if(n.state==="delta"&&n.text&&!n.thinking){const i=(this.workerTextAcc.get(s)??"")+n.text;this.workerTextAcc.set(s,i);const c=B(i);c&&(r.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(n.state==="final"||n.state==="error"||n.state==="aborted"){r.status=n.state;const i=B(this.workerTextAcc.get(s)??"");i&&(r.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else n.state==="start"&&(r.status="running");const o=this.registry.updateWorker(e,r);o&&(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.enqueueWorkerEvent(o.managedBy,o,n.state,n.runId,n.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const n=this.opts.getRuntime();if(e.method.includes("channel")||e.method.includes("wechat-rpa")){f(n,e.id,!1,{error:"feature_retired"});return}const t=e.params??{};try{const r=String(t.managerSessionId||t.sessionId||"");if(!r)throw new Error("sessionId is required");const s=this.registry.getManager(r),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",l=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${r}`),managerSessionId:r,sessionId:r,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(o){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"wechat-rpa",{includeSecret:!0})});return}if(i){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${r}`),managerSessionId:r,sessionId:r,workDir:I(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:U(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:H(t.source),pollIntervalMs:p(t.pollIntervalMs),recentLimit:p(t.recentLimit),idleSeconds:p(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(c){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(r);this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}if(l){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:r,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(r),f(n,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(r){f(n,e.id,!1,{error:r instanceof Error?r.message:String(r)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const n=this.opts.getRuntime();if(!n.client?.sendEvent)return;const t=this.registry.getManager(e),r=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");n.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:r}}})}async handleIpc(e,n){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(n,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),r=await Ce(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){u(n,410,{ok:!1,error:"feature_retired"});return}const s=String(r.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(n,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(r.agentType||r.agent||o.defaultWorkerAgentType||"codex");if(!we(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=I(String(r.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const l=String(r.message||"");if(!l)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:l.slice(0,120)}),h=String(r.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,i,c,l,null,h,s),u(n,200,{ok:!0,session:d});return}if(t.pathname==="/sessions/send"){const i=String(r.sessionId||""),c=String(r.message||""),l=r.enqueue===void 0?!0:!!r.enqueue,d=this.registry.getWorkerForManager(s,i);if(!d)throw new Error("Worker not found in this manager scope");const h=String(r.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));l?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h,s):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h,s),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(n,200,{ok:!0,queue:l});return}if(t.pathname==="/sessions/queue/edit"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${m()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||""),text:String(r.message||r.text||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${m()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${m()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(r.sessionId||""),c=Number(r.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=ie(i,{limit:Math.max(c*20,c)});u(n,200,{ok:!0,messages:Me(d,c),rawMessageCount:d.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(n,200,{ok:!0,path:w.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof r.replyTarget=="string"?this.registry.getReplyTarget(r.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(r.text||""),l=q(r.attachment),d=String(r.idempotencyKey||m()),h=String(r.channelId||""),g=String(r.conversationId||""),M=!i&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,k=i?.channelId||h||M?.channelId||"",v=i?.conversationId||g||M?.conversationId||"",P=k?this.channelRuntime.getChannelById(k):void 0,J=P&&(!!(i||h)||P.managedBy!=="session-sync");if(k&&J){if(!v)throw new Error("No external channel target is available for this Manager");const y=await this.channelRuntime.reply({managerSessionId:s,channelId:k,conversationId:v,messageId:i?.messageId??void 0,text:c,attachment:l,idempotencyKey:d});u(n,y.ok?200:400,y);return}const b=await this.tryDirectWeChatRpaReply(s,c,l);if(b){u(n,b.ok?200:400,b);return}let S;try{S=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:l,idempotencyKey:d})}catch(y){const W=y instanceof Error?y.message:String(y);if(!F(W))throw y;S={ok:!1,error:W}}if(S.ok){u(n,200,{ok:!0,payload:S.payload});return}if(!F(S.error||"")||!k||!v){u(n,400,{ok:!1,error:S.error||"External send failed"});return}u(n,400,{ok:!1,error:`No local external channel is configured for ${k}`});return}if(t.pathname==="/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(r.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof r.name=="string"?r.name:void 0,enabled:!!r.enabled,wsUrl:typeof r.wsUrl=="string"?r.wsUrl:void 0,token:typeof r.token=="string"?r.token:void 0,canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=L(r),c=p(r.limit)??10,l=typeof r.traceId=="string"?r.traceId:void 0;let d;try{d=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>he(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:r.download==="never"?"never":"auto",traceId:l,timeoutMs:p(r.timeoutMs)}))}catch(h){u(n,400,{ok:!1,...z(h,l)});return}u(n,200,{ok:!0,messages:d.messages,outDir:d.outDir,helperTracePath:d.helperTracePath,activityGuardPath:d.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=L(r),c=String(r.text||""),l=q(r.attachment),d=typeof r.traceId=="string"?r.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>$(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:l,traceId:d,timeoutMs:p(r.timeoutMs)}))}catch(g){u(n,400,{ok:!1,...z(g,d)});return}u(n,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=I(String(r.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(r.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof r.name=="string"?r.name:void 0,agentType:typeof r.agentType=="string"?r.agentType:void 0,agentSessionId:typeof r.agentSessionId=="string"?r.agentSessionId:null,modelId:typeof r.modelId=="string"?r.modelId:null,enabled:!!r.enabled,groups:U(r.groups),canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0,source:H(r.source),pollIntervalMs:p(r.pollIntervalMs),recentLimit:p(r.recentLimit),idleSeconds:p(r.idleSeconds),forceForeground:r.forceForeground===void 0?void 0:!!r.forceForeground,noRestore:r.noRestore===void 0?void 0:!!r.noRestore,downloadAttachments:r.downloadAttachments===void 0?void 0:!!r.downloadAttachments,downloadAttachmentsDir:typeof r.downloadAttachmentsDir=="string"?r.downloadAttachmentsDir:void 0,selfNickname:typeof r.selfNickname=="string"?r.selfNickname:void 0,selfTriggerMarker:typeof r.selfTriggerMarker=="string"?r.selfTriggerMarker:void 0,deferInitialPoll:r.deferInitialPoll===void 0?void 0:!!r.deferInitialPoll,privacyConsentAccepted:r.privacyConsentAccepted===void 0?void 0:!!r.privacyConsentAccepted,flowScriptPath:typeof r.flowScriptPath=="string"?r.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof r.idempotencyKey=="string"?r.idempotencyKey:null,replyId:typeof r.replyId=="string"?r.replyId:null,reason:typeof r.reason=="string"?r.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,...i});return}u(n,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(n,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,n,t,r,s,o,i){await this.opts.dispatchReq({type:"req",id:`manager-send-${m()}`,method:"chat.send",params:{...C(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}async dispatchChatEnqueue(e,n,t,r,s,o,i){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${m()}`,method:"chat.enqueue",params:{...C(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}async tryDirectWeChatRpaReply(e,n,t){const r=this.opts.getRuntime().sessions.get(e),s=r?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||D().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!n.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=r?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:me(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>$(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:n,attachment:t}))}}catch(l){return{ok:!1,error:l instanceof Error?l.message:String(l)}}}async sendManagedWeComReply(e){const n=ge(e.text);if(!n.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const r=[];for(const[s,o]of n.entries()){const i=await t.sendReq({type:"req",id:`external-send-${m()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:n.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};r.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${m()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:n.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};r.push(s.payload)}return{ok:!0,payload:r.length===1?r[0]:r}}enqueueWorkerEvent(e,n,t,r,s){this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:n.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:r??null,summary:s||n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"})&&this.drainManagerInbox(e)}handleExternalMessage(e,n){const t=this.channelRuntime.getChannelById(n.channelId)??this.channelRuntime.getManagerChannel(e,n.channelType),r=this.channelRuntime.getChannelStatusById(n.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),c=t?.agentSessionId??s?.agentSessionId??null,l=t?.modelId||s?.modelId||"",d=xe(n.attachments,n.channelType),h=ve(n);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:c,modelId:l,text:h,messageId:n.messageId,attachments:d,externalChannel:De(r),replyTarget:n.replyTarget})}async dispatchExternalMessage(e){const n=typeof e.messageId=="string"&&e.messageId.trim()?e.messageId.trim():void 0;await this.opts.dispatchReq({type:"req",id:`external-enqueue-${m()}`,method:"chat.enqueue",params:{...C(),sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",queueMessageId:n,clientMessageId:n,attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),n=this.registry.load();for(const t of Object.values(n.workers)){if(t.status!=="running")continue;const r=e-Date.parse(t.createdAt);if(r<10*6e4)continue;const s=t.healthNotifiedAt?Date.parse(t.healthNotifiedAt):0;if(s&&e-s<10*6e4)continue;const o=n.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),c=`\u5DF2\u8FD0\u884C ${Math.floor(r/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
11
11
|
|
|
12
12
|
\u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
|
|
13
13
|
- \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
|
|
14
14
|
- \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${t.lastActivityAt}
|
|
15
15
|
- \u6700\u8FD1 ${i} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002
|
|
16
16
|
|
|
17
|
-
\u8BF7\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u7B49\u5F85\u3001\u8BE2\u95EE\u7528\u6237\u3001\u505C\u6B62 worker\u3001\u6216\u521B\u5EFA\u5176\u4ED6 worker \u534F\u52A9\u3002`;this.registry.updateWorker(t.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:c}),this.registry.enqueueInboxEvent({managerSessionId:o.sessionId,workerSessionId:t.sessionId,kind:"worker.health",priority:"normal",runId:t.runId??null,summary:c})&&this.drainManagerInbox(o.sessionId)}}async interruptAndResumeManager(e,n,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${
|
|
17
|
+
\u8BF7\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u7B49\u5F85\u3001\u8BE2\u95EE\u7528\u6237\u3001\u505C\u6B62 worker\u3001\u6216\u521B\u5EFA\u5176\u4ED6 worker \u534F\u52A9\u3002`;this.registry.updateWorker(t.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:c}),this.registry.enqueueInboxEvent({managerSessionId:o.sessionId,workerSessionId:t.sessionId,kind:"worker.health",priority:"normal",runId:t.runId??null,summary:c})&&this.drainManagerInbox(o.sessionId)}}async interruptAndResumeManager(e,n,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${m()}`,method:"chat.abort",params:{...C(),sessionId:e.sessionId}});const i=t?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${t}
|
|
18
18
|
|
|
19
|
-
${n}`:n;await this.dispatchManagerContinuation(e,i,!1,r)}bindManagerAdapterEvents(e,n){n.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const r=this.registry.getManager(e);r&&this.noteManagerAgentSession(e,t.agentSessionId,r.workDir,r.modelId)}t.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:n})}async drainManagerInbox(e){const n=this.registry.getManager(e);if(!n||n.deliveringInboxEventIds?.length)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const r=t.find(l=>l.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(n.status!=="idle"&&!r||s?.currentRunId&&!r)return;const o=r?[r]:t.slice(0,20),i=ke(o),c=Se(e,o);try{r&&s?.currentRunId?(this.registry.upsertManager({...n,status:"interrupting",deliveringInboxEventIds:o.map(h=>h.id)}),await this.interruptAndResumeManager(n,i,r.kind,c)):(this.registry.upsertManager({...n,status:"running",deliveringInboxEventIds:o.map(h=>h.id)}),await this.dispatchManagerContinuation(n,i,!0,c)),await Promise.resolve();const l=this.registry.getManager(e),d=!!this.opts.getRuntime().sessions.get(e)?.currentRunId;l?.deliveringInboxEventIds?.length&&!d&&this.registry.upsertManager({...l,status:"idle",deliveringInboxEventIds:[]})}catch(l){this.registry.upsertManager({...n,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${l instanceof Error?l.message:String(l)}`)}}acknowledgeManagerInboxDelivery(e){const n=this.registry.getManager(e),t=n?.deliveringInboxEventIds??[];!n||t.length===0||(this.registry.removeInboxEvents(e,t),this.registry.upsertManager({...n,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load();for(const n of Object.values(e.managers))this.opts.getRuntime().sessions.get(n.sessionId)?.currentRunId||((n.status!=="idle"||n.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...n,status:"idle",deliveringInboxEventIds:[]}),this.registry.listInboxEvents(n.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(n.sessionId)}))}async dispatchManagerContinuation(e,n,t=!1,r){const s=e.agentType??"manager",o=s==="manager"?e.modelId:e.providerModelId??void 0,i={...C(),sessionId:e.sessionId,text:n,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:o,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,clientMessageId:r};await this.opts.dispatchReq({type:"req",id:`manager-${t?"enqueue":"send"}-${
|
|
19
|
+
${n}`:n;await this.dispatchManagerContinuation(e,i,!1,r)}bindManagerAdapterEvents(e,n){n.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const r=this.registry.getManager(e);r&&this.noteManagerAgentSession(e,t.agentSessionId,r.workDir,r.modelId)}t.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:n})}async drainManagerInbox(e){const n=this.registry.getManager(e);if(!n||n.deliveringInboxEventIds?.length)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const r=t.find(l=>l.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(n.status!=="idle"&&!r||s?.currentRunId&&!r)return;const o=r?[r]:t.slice(0,20),i=ke(o),c=Se(e,o);try{r&&s?.currentRunId?(this.registry.upsertManager({...n,status:"interrupting",deliveringInboxEventIds:o.map(h=>h.id)}),await this.interruptAndResumeManager(n,i,r.kind,c)):(this.registry.upsertManager({...n,status:"running",deliveringInboxEventIds:o.map(h=>h.id)}),await this.dispatchManagerContinuation(n,i,!0,c)),await Promise.resolve();const l=this.registry.getManager(e),d=!!this.opts.getRuntime().sessions.get(e)?.currentRunId;l?.deliveringInboxEventIds?.length&&!d&&this.registry.upsertManager({...l,status:"idle",deliveringInboxEventIds:[]})}catch(l){this.registry.upsertManager({...n,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${l instanceof Error?l.message:String(l)}`)}}acknowledgeManagerInboxDelivery(e){const n=this.registry.getManager(e),t=n?.deliveringInboxEventIds??[];!n||t.length===0||(this.registry.removeInboxEvents(e,t),this.registry.upsertManager({...n,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load();for(const n of Object.values(e.managers))this.opts.getRuntime().sessions.get(n.sessionId)?.currentRunId||((n.status!=="idle"||n.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...n,status:"idle",deliveringInboxEventIds:[]}),this.registry.listInboxEvents(n.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(n.sessionId)}))}async dispatchManagerContinuation(e,n,t=!1,r){const s=e.agentType??"manager",o=s==="manager"?e.modelId:e.providerModelId??void 0,i={...C(),sessionId:e.sessionId,text:n,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:o,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:r};await this.opts.dispatchReq({type:"req",id:`manager-${t?"enqueue":"send"}-${m()}`,method:t?"chat.enqueue":"chat.send",params:i})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(n=>fe(n,void 0,e,"manager")).join(`
|
|
20
20
|
|
|
21
|
-
`).trim()}}function U(a){return Array.isArray(a)?a.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function H(a){return a==="wechat-channel"||a==="macos-flow"||a==="macos-probe"||a==="windows-visual-flow"||a==="wechat-rpa-lab"||a==="fixture-jsonl"?a:void 0}function xe(a,e){const n=a.map(t=>j(t,e)).filter(t=>t!=null);return n.length?n:void 0}function Ee(a,e,n,t){const r=e||"",s=new Set;return a.map((i,c)=>{const l=j(i,t),d=ne({type:i.type,name:l?.name||i.name,path:l?.path,mimeType:l?.mimeType||i.mimeType,availability:i.availability,providerError:i.providerError||Te(i)},c),h=l?.path||"";if(h){const
|
|
22
|
-
${h}`;return s.has(
|
|
21
|
+
`).trim()}}function U(a){return Array.isArray(a)?a.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function H(a){return a==="wechat-channel"||a==="macos-flow"||a==="macos-probe"||a==="windows-visual-flow"||a==="wechat-rpa-lab"||a==="fixture-jsonl"?a:void 0}function xe(a,e){const n=a.map(t=>j(t,e)).filter(t=>t!=null);return n.length?n:void 0}function Ee(a,e,n,t){const r=e||"",s=new Set;return a.map((i,c)=>{const l=j(i,t),d=ne({type:i.type,name:l?.name||i.name,path:l?.path,mimeType:l?.mimeType||i.mimeType,availability:i.availability,providerError:i.providerError||Te(i)},c),h=l?.path||"";if(h){const M=`${d}
|
|
22
|
+
${h}`;return s.has(M)||r.includes(h)?"":(s.add(M),`${n}: ${d}`)}const g=d;return s.has(g)?"":(s.add(g),`${n}: ${d}`)}).filter(i=>!r.includes(i)).join(`
|
|
23
23
|
`)}function ve(a){const n=(a.channelType==="wechat-rpa"?be(a.sender.name):a.sender.name||a.sender.id)||(a.channelType==="wechat-rpa"?"\u5BF9\u65B9":a.sender.id)||"\u5BF9\u65B9",t=Ee(a.attachments,a.text,n,a.channelType),s=[a.text?te({senderName:n,senderId:a.sender.id,text:a.text}):t?"":`${n}:`,t].filter(Boolean).join(`
|
|
24
24
|
`);return[ee({conversationName:a.conversationName||a.conversationId,messages:[]}),s].filter(Boolean).join(`
|
|
25
|
-
`)}function be(a){const e=a?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function Te(a){return!a.localPath||a.availability&&a.availability!=="edge-local"||N(a.localPath)?"":"edge-local-unavailable"}function j(a,e){return a.localPath&&Ne(a,e)&&N(a.localPath)?{path:a.localPath,name:a.name||w.basename(a.localPath)||"attachment",mimeType:a.mimeType||K(a)}:a.url&&Pe(a)?{path:a.url,name:a.name||a.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:a.mimeType||K(a)}:a.thumbnailPath&&!G(a,e)&&N(a.thumbnailPath)?{path:a.thumbnailPath,name:a.name?`${a.name}-preview.png`:w.basename(a.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function De(a){return a?a.type!=="wechat-rpa"?a:{configured:a.configured,connected:a.connected,type:a.type,channelId:a.channelId,name:a.name,canReply:a.canReply,systemPrompt:a.systemPrompt,wechatRpaSource:a.wechatRpaSource,wechatRpaGroups:a.wechatRpaGroups,pollIntervalMs:a.pollIntervalMs,recentLimit:a.recentLimit,idleSeconds:a.idleSeconds,forceForeground:a.forceForeground,noRestore:a.noRestore,downloadAttachments:a.downloadAttachments,selfNickname:a.selfNickname,wechatRpaPrivacyConsentAccepted:a.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:a.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:a.wechatRpaPreflightChecks,wechatRpaRuntimeState:a.wechatRpaRuntimeState,wechatRpaLastMessageAt:a.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:a.wechatRpaPendingReplyCount,wechatRpaLastError:a.wechatRpaLastError}:null}function Ne(a,e){return a.providerError||a.availability&&a.availability!=="edge-local"?!1:G(a,e)?a.materializationKind==="original-file"&&a.isOriginal===!0&&a.mimeKindMatches===!0&&!We(a.localPath):!0}function Pe(a){return!a.providerError&&(!a.availability||a.availability==="server-url")}function G(a,e){if(e!=="wechat-rpa")return!1;const n=String(a.type||"").toLowerCase();return n==="file"||n==="video"||n==="video-file"}function We(a){const e=String(a||"").replace(/\\/g,"/"),n=w.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(n)}function N(a){try{return
|
|
25
|
+
`)}function be(a){const e=a?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function Te(a){return!a.localPath||a.availability&&a.availability!=="edge-local"||N(a.localPath)?"":"edge-local-unavailable"}function j(a,e){return a.localPath&&Ne(a,e)&&N(a.localPath)?{path:a.localPath,name:a.name||w.basename(a.localPath)||"attachment",mimeType:a.mimeType||K(a)}:a.url&&Pe(a)?{path:a.url,name:a.name||a.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:a.mimeType||K(a)}:a.thumbnailPath&&!G(a,e)&&N(a.thumbnailPath)?{path:a.thumbnailPath,name:a.name?`${a.name}-preview.png`:w.basename(a.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function De(a){return a?a.type!=="wechat-rpa"?a:{configured:a.configured,connected:a.connected,type:a.type,channelId:a.channelId,name:a.name,canReply:a.canReply,systemPrompt:a.systemPrompt,wechatRpaSource:a.wechatRpaSource,wechatRpaGroups:a.wechatRpaGroups,pollIntervalMs:a.pollIntervalMs,recentLimit:a.recentLimit,idleSeconds:a.idleSeconds,forceForeground:a.forceForeground,noRestore:a.noRestore,downloadAttachments:a.downloadAttachments,selfNickname:a.selfNickname,wechatRpaPrivacyConsentAccepted:a.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:a.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:a.wechatRpaPreflightChecks,wechatRpaRuntimeState:a.wechatRpaRuntimeState,wechatRpaLastMessageAt:a.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:a.wechatRpaPendingReplyCount,wechatRpaLastError:a.wechatRpaLastError}:null}function Ne(a,e){return a.providerError||a.availability&&a.availability!=="edge-local"?!1:G(a,e)?a.materializationKind==="original-file"&&a.isOriginal===!0&&a.mimeKindMatches===!0&&!We(a.localPath):!0}function Pe(a){return!a.providerError&&(!a.availability||a.availability==="server-url")}function G(a,e){if(e!=="wechat-rpa")return!1;const n=String(a.type||"").toLowerCase();return n==="file"||n==="video"||n==="video-file"}function We(a){const e=String(a||"").replace(/\\/g,"/"),n=w.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(n)}function N(a){try{return R.statSync(a).isFile()}catch{return!1}}function K(a){return a.type==="image"?"image/*":a.type==="video"?"video/*":a.type==="audio"?"audio/*":"application/octet-stream"}function p(a){const e=Number(a);return Number.isFinite(e)?e:void 0}function z(a,e){const n=E(a,"reasonCode")||$e(a),t=E(a,"outDir"),r=E(a,"helperTracePath"),s=E(a,"activityGuardPath");return{error:a instanceof Error?a.message:String(a||n),reasonCode:n,...t?{outDir:t}:{},...r?{helperTracePath:r}:{},...s?{activityGuardPath:s}:{},...e?{traceId:e}:{}}}function E(a,e){if(!a||typeof a!="object")return"";const n=a[e];return typeof n=="string"&&n.trim()?n.trim():""}function $e(a){const e=a instanceof Error?a.message:String(a||"");return/^([a-z][a-z0-9_]*)(?::|\b)/i.exec(e.trim())?.[1]||"wechat_tool_failed"}export{Xe as ManagerRuntimeService,Qe as getManagerRuntimeService,Ve as setManagerRuntimeService};
|