shennian 0.4.66 → 0.4.68
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 +51 -46
- package/dist/src/agent-env.js +3 -3
- package/dist/src/agents/acp/manifests.d.ts +2 -2
- package/dist/src/agents/adapter.d.ts +1 -1
- package/dist/src/agents/codex.js +5 -5
- package/dist/src/agents/command-spec.d.ts +2 -0
- package/dist/src/agents/command-spec.js +3 -3
- package/dist/src/agents/runtime-compatibility.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/manager/registry.d.ts +10 -0
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +4 -2
- package/dist/src/manager/runtime.js +6 -6
- package/dist/src/manager/worker-ipc.d.ts +5 -3
- package/dist/src/manager/worker-ipc.js +1 -1
- package/dist/src/room/activation-service.d.ts +8 -2
- package/dist/src/room/activation-service.js +10 -5
- package/dist/src/room/managed-context.d.ts +3 -1
- package/dist/src/room/managed-context.js +1 -1
- package/dist/src/room/managed-prompt-composer.js +1 -1
- package/dist/src/room/managed-room-ipc.d.ts +2 -0
- package/dist/src/room/managed-room-ipc.js +2 -2
- package/dist/src/schedules/personal-context.d.ts +0 -1
- package/dist/src/schedules/personal-context.js +1 -1
- package/dist/src/session/handlers/chat-adapter-config.js +1 -1
- package/dist/src/session/handlers/chat-recovery.js +1 -1
- package/dist/src/session/handlers/chat.js +1 -3
- package/dist/src/session/manager-event-authority.d.ts +3 -0
- package/dist/src/session/manager-event-authority.js +1 -0
- package/dist/src/session/manager.d.ts +2 -2
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/queue-helpers.d.ts +5 -0
- package/dist/src/session/queue-helpers.js +8 -8
- package/dist/src/session/queue-message.d.ts +4 -1
- package/dist/src/session/queue-message.js +1 -1
- package/dist/src/session/queue-storage.d.ts +1 -0
- package/dist/src/session/queue-storage.js +2 -2
- package/dist/src/session/queue-types.d.ts +8 -0
- package/dist/src/session/queue.d.ts +3 -3
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/queued-chat-dispatch.js +1 -1
- package/dist/src/session/turn-trigger.d.ts +5 -0
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as fe}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ve}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Ie}from"./schedules/server-client.js";import{Command as Se}from"commander";import R from"chalk";import ye from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as we,getConfigPath as Ce,getShennianDir as be,resolveShennianPath as H,normalizeAutoUpgradePolicy as ke}from"./config/index.js";import{CliRelayClient as Re}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Pe,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Ae,runSmartStart as Te}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as De,clearDaemonLauncher as oe,findRunningDaemonProcessIds as Me,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as _e,stopPreparedRemoteAccessService as xe,isIsolatedDaemonE2eInstance as $e,getDaemonStatus as Ne,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as te}from"./commands/daemon.js";import{registerRuntimeCommand as Ve}from"./commands/runtime.js";import{registerAgentCommand as Le}from"./commands/agent.js";import{registerManagerCommand as je}from"./commands/manager.js";import{handleUpgradeStart as He,registerUpgradeCommand as Be}from"./commands/upgrade.js";import{registerToolsCommand as Fe}from"./commands/tools.js";import{registerIntegrationCommand as Ge}from"./commands/integration.js";import{registerRoomCommand as qe}from"./commands/room.js";import{SessionManager as ze}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Je}from"./session/handlers/project-fs.js";import{SERVERS as B,regionToUrl as Ke,urlToRegion as We}from"./region.js";import{clearUpgradeAttempt as Ye,getCurrentVersion as ae,handleStartupCrashCheck as Qe,readUpgradeAttempt as Ze}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Xe}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as eo}from"./upgrade/updater-bootstrap.js";import{launchUpdater as oo,markUpdaterHealthy as no}from"./upgrade/updater-launcher.js";import{detectAgents as ro}from"./agents/detect.js";import{augmentProcessPath as ie}from"./env-path.js";import{acquireDaemonInstanceLock as to}from"./commands/daemon-instance-lock.js";ie();const F=ae();import{getCachedAgentInfos as ao,resolveAgentInfos as io}from"./agents/model-registry.js";import{initCliLogReporter as so,reportLog as P,reportUpgradeAggregateMetric as co}from"./log-reporter.js";import{isNativeFusionEnabled as lo}from"./native-fusion/config.js";import{NativeSessionFusionService as mo}from"./native-fusion/service.js";import{startDaemonLogRetention as po}from"./daemon-log.js";import{startDaemonIpcServer as se}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as go,resolveManagedRoomIpcPaths as uo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as ho}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as fo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as vo}from"./room/session-registry.js";import{HttpRoomBindingClient as Io}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as So}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as yo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as wo}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Co}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as bo,isCurrentRegistration as ko}from"./room/registration-authority.js";import{RoomActivationService as Ro,resolveManagedAgentPolicy as Po}from"./room/activation-service.js";import{RoomActivationAudit as Eo}from"./room/activation-audit.js";import{RoomAttachmentCache as Ao}from"./room/attachment-cache.js";import{RoomAttachmentUploader as To}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Uo}from"./room/managed-context.js";import{createManagedRoomExecutor as Do}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ce,SESSION_SYNC_ADVERTISED_CAPABILITIES as de}from"@shennian/wire";import{getDaemonInstallationId as Mo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as _o}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as le,ensurePersonalDataEpoch as xo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as $o,DeletionSyncTerminalError as No}from"./session/deletion-sync-state.js";const me=be(),Oo=H("daemon.pid"),Vo=H("daemon.instance.lock");function Lo(){try{const
|
|
2
|
-
`),process.exitCode=1}function Ho(t){const s=[`trigger=${t.trigger??"unknown"}`,`phase=${t.phase}`,`code=${t.code??"unknown"}`,`attempt=${t.reconnectAttempt}`];return t.reason&&s.push(`reason=${t.reason}`),t.error&&s.push(`error=${t.error}`),s.join(" ")}const i=new Se;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),Ge(i),qe(i),fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",ye.hostname()).action(async t=>{const s=m(),p=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Te(p,t.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async t=>{const s=H("env.json");try{const e=JSON.parse(j.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ie()}catch{}Pe(X(process.env,m()));const p=po();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=De(),I=to({lockPath:Vo,instanceId:c});I.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(I.ownerPid?` (PID ${I.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Lo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),I.release(),process.exit(0)}catch{}}catch{}const q=$e(process.env,me)?[]:Me(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),I.release(),process.exit(0)),re(process.pid,c,{version:F}),te(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",I.release);let $=null;try{$=eo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Qe();if(E){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{oo({fromVersion:F,targetVersion:E.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:E.jobId},{readiness:$}),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{xo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const d=m(),g=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??d.serverUrl??B.cn.url,h=ae(),y=await ro({serverUrl:g,authToken:d.machineToken??d.accessToken}),w=y.map(e=>e.type),N=ao(y),u=new vo,A=new yo,C=new Uo(u),v=new Io(g),ge=new Ao({actionClient:v}),ue=new To({actionClient:v});let S=null,r;const z=Do({authority:C,actionClient:v,attachmentCache:ge,attachmentUploader:ue,observations:A,getMachineCredentials:()=>({machineToken:m().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>Z({serverUrl:g,machineToken:m().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const a=u.getByRegistryKey(e)?.binding;a&&u.setBinding({registryKey:e,bindingId:a.bindingId,roomId:a.roomId,agentIdentityId:a.agentIdentityId,agentMembershipId:a.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await se({paths:go(),handlers:ho({daemonVersion:h,protocolVersion:fo,getStatus:()=>Ne(),getAgents:()=>y,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!O)throw new Error("upgrade_unavailable");return O(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 k;try{k=await se({paths:uo(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=ve(n?.context);if(u.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await Z({serverUrl:g,machineToken:m().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let J=!1;const T=()=>{J||(J=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",T),process.once("SIGTERM",T),!d.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",T),process.removeListener("SIGTERM",T);const K=`${jo(g)}/relay/machine`,U=d.machineToken;if(!U)return;try{await _o({serverUrl:g,machineToken:U,machineId:d.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}d.machineId&&so(g,d.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${h}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let D=null,M=null,_=0,f=Ze();const W=ke(d.autoUpgrade),l=new Re({serverUrl:K,machineToken:U,cliVersion:h,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:de,daemonInstallationId:Mo(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),io(y,{serverUrl:d.serverUrl??g,authToken:d.machineToken??d.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),f&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&no(o.jobId,o.to),Ye(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),co({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),l.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"&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&l.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&l.getState()==="connected"){if(o instanceof No)return;const a=o instanceof $o?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},a).unref?.()}}};n()},onDisconnected:e=>{_+=1,r?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Ho(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||r?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),r.handleReq(e)}});D=lo()?new mo(l):null,M=new wo({registry:u,observations:A,actionClient:v,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Co().hasSession(e),onMessages:async(e,n,o,a)=>{await S?.processMessages(e,n,o,a)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new Xe({currentVersion:h,machineId:d.machineId??"unknown-machine",serverUrl:d.serverUrl??g,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:le,advertisedCapabilities:de,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(l,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:a=>r.prepareForUpgrade(a),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(W),r=new ze(l,D,h,x,new So({registry:u,observations:A,bindingClient:v,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>_e(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{xe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Je(U)),O=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,a=n.confirmedMajor===!0;return await He(l,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:a,prepareForUpgrade:he=>r.prepareForUpgrade(he),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},a=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:a,...r.prepareForUpgrade(a)}},L=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new Ro({registry:u,observations:A,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=m().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const a=await v.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!ko(u,e)||!bo(e,a))throw new Error("managed_room_binding_unavailable");return{roomName:a.room.name,memberCount:a.room.memberCount,members:a.room.members,agentsByIdentityId:a.agent?{[a.agentIdentityId]:a.agent}:{},agentSystemPromptsByIdentityId:a.agent?{[a.agentIdentityId]:a.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n)=>{const o=m().machineToken;if(!o)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:o,activationCauseMessageId:n})},audit:new Eo,resolveManagedAgentPolicy:e=>Po({...e,runtime:y.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Ie({serverUrl:g,machineToken:m().machineToken,req:e,client:l,queue:r.getScheduleQueue(),registry:u}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(u.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await v.status({machineToken:m().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer(e=>S.prepareDirectConversationTurn(e)),r.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(me,{recursive:!0}),re(process.pid,c,{version:h}),te(process.pid,void 0,c),l.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(R.gray(`
|
|
3
|
-
Disconnecting...`)),P({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await
|
|
1
|
+
import{registerScheduleCommand as fe}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ve}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Ie}from"./schedules/server-client.js";import{Command as Se}from"commander";import R from"chalk";import ye from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as we,getConfigPath as Ce,getShennianDir as be,resolveShennianPath as H,normalizeAutoUpgradePolicy as ke}from"./config/index.js";import{CliRelayClient as Re}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Pe,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Ae,runSmartStart as Te}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as De,clearDaemonLauncher as oe,findRunningDaemonProcessIds as Me,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as _e,stopPreparedRemoteAccessService as xe,isIsolatedDaemonE2eInstance as $e,getDaemonStatus as Ne,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as te}from"./commands/daemon.js";import{registerRuntimeCommand as Ve}from"./commands/runtime.js";import{registerAgentCommand as Le}from"./commands/agent.js";import{registerManagerCommand as je}from"./commands/manager.js";import{handleUpgradeStart as He,registerUpgradeCommand as Be}from"./commands/upgrade.js";import{registerToolsCommand as Fe}from"./commands/tools.js";import{registerIntegrationCommand as Ge}from"./commands/integration.js";import{registerRoomCommand as qe}from"./commands/room.js";import{SessionManager as ze}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Je}from"./session/handlers/project-fs.js";import{SERVERS as B,regionToUrl as Ke,urlToRegion as We}from"./region.js";import{clearUpgradeAttempt as Ye,getCurrentVersion as ae,handleStartupCrashCheck as Qe,readUpgradeAttempt as Ze}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Xe}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as eo}from"./upgrade/updater-bootstrap.js";import{launchUpdater as oo,markUpdaterHealthy as no}from"./upgrade/updater-launcher.js";import{detectAgents as ro}from"./agents/detect.js";import{augmentProcessPath as ie}from"./env-path.js";import{acquireDaemonInstanceLock as to}from"./commands/daemon-instance-lock.js";ie();const F=ae();import{getCachedAgentInfos as ao,resolveAgentInfos as io}from"./agents/model-registry.js";import{initCliLogReporter as so,reportLog as P,reportUpgradeAggregateMetric as co}from"./log-reporter.js";import{isNativeFusionEnabled as lo}from"./native-fusion/config.js";import{NativeSessionFusionService as mo}from"./native-fusion/service.js";import{startDaemonLogRetention as po}from"./daemon-log.js";import{startDaemonIpcServer as se}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as go,resolveManagedRoomIpcPaths as uo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as ho}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as fo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as vo}from"./room/session-registry.js";import{HttpRoomBindingClient as Io}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as So}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as yo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as wo}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Co}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as bo,isCurrentRegistration as ko}from"./room/registration-authority.js";import{RoomActivationService as Ro,resolveManagedAgentPolicy as Po}from"./room/activation-service.js";import{RoomActivationAudit as Eo}from"./room/activation-audit.js";import{RoomAttachmentCache as Ao}from"./room/attachment-cache.js";import{RoomAttachmentUploader as To}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Uo}from"./room/managed-context.js";import{createManagedRoomExecutor as Do}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ce,SESSION_SYNC_ADVERTISED_CAPABILITIES as de}from"@shennian/wire";import{getDaemonInstallationId as Mo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as _o}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as le,ensurePersonalDataEpoch as xo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as $o,DeletionSyncTerminalError as No}from"./session/deletion-sync-state.js";const me=be(),Oo=H("daemon.pid"),Vo=H("daemon.instance.lock");function Lo(){try{const a=j.readFileSync(Oo,"utf-8").trim();if(a.startsWith("{")){const p=JSON.parse(a),c=Number(p.pid);return Number.isInteger(c)&&c>0?c:null}const s=parseInt(a,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function jo(a){return a.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 Ho(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Se;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),Ge(i),qe(i),fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",ye.hostname()).action(async a=>{const s=m(),p=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Te(p,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=H("env.json");try{const e=JSON.parse(j.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ie()}catch{}Pe(X(process.env,m()));const p=po();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=De(),S=to({lockPath:Vo,instanceId:c});S.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(S.ownerPid?` (PID ${S.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Lo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),S.release(),process.exit(0)}catch{}}catch{}const q=$e(process.env,me)?[]:Me(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),S.release(),process.exit(0)),re(process.pid,c,{version:F}),te(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",S.release);let $=null;try{$=eo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Qe();if(E){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{oo({fromVersion:F,targetVersion:E.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:E.jobId},{readiness:$}),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{xo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const d=m(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??d.serverUrl??B.cn.url,h=ae(),y=await ro({serverUrl:g,authToken:d.machineToken??d.accessToken}),w=y.map(e=>e.type),N=ao(y),u=new vo,A=new yo,C=new Uo(u),v=new Io(g),ge=new Ao({actionClient:v}),ue=new To({actionClient:v});let I=null,t;const z=Do({authority:C,actionClient:v,attachmentCache:ge,attachmentUploader:ue,observations:A,getMachineCredentials:()=>({machineToken:m().machineToken}),managerExecutor:e=>t.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>Z({serverUrl:g,machineToken:m().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const r=u.getByRegistryKey(e)?.binding;r&&u.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),t?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await se({paths:go(),handlers:ho({daemonVersion:h,protocolVersion:fo,getStatus:()=>Ne(),getAgents:()=>y,completeAgentAuthentication:e=>t.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!O)throw new Error("upgrade_unavailable");return O(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 k;try{k=await se({paths:uo(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=ve(n?.context);if(u.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await Z({serverUrl:g,machineToken:m().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let J=!1;const T=()=>{J||(J=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",T),process.once("SIGTERM",T),!d.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",T),process.removeListener("SIGTERM",T);const K=`${jo(g)}/relay/machine`,U=d.machineToken;if(!U)return;try{await _o({serverUrl:g,machineToken:U,machineId:d.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}d.machineId&&so(g,d.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${h}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let D=null,M=null,_=0,f=Ze();const W=ke(d.autoUpgrade),l=new Re({serverUrl:K,machineToken:U,cliVersion:h,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:de,daemonInstallationId:Mo(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),io(y,{serverUrl:d.serverUrl??g,authToken:d.machineToken??d.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),f&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:t.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await t.synchronizeSessionDeletions(),await t.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&no(o.jobId,o.to),Ye(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),co({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),l.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"&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&l.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&l.getState()==="connected"){if(o instanceof No)return;const r=o instanceof $o?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},r).unref?.()}}};n()},onDisconnected:e=>{_+=1,t?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Ho(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||t?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),t.handleReq(e)}});D=lo()?new mo(l):null,M=new wo({registry:u,observations:A,actionClient:v,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Co().hasSession(e),onMessages:async(e,n,o,r)=>{await I?.processMessages(e,n,o,r)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?t?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):t?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new Xe({currentVersion:h,machineId:d.machineId??"unknown-machine",serverUrl:d.serverUrl??g,getIdleState:()=>t.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:le,advertisedCapabilities:de,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(l,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:r=>t.prepareForUpgrade(r),cancelUpgradePreparation:()=>t.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>t.getUpgradeIdleState().idle})}});x.setPolicy(W),t=new ze(l,D,h,x,new So({registry:u,observations:A,bindingClient:v,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>_e(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{xe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Je(U)),O=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,r=n.confirmedMajor===!0;return await He(l,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:r,prepareForUpgrade:he=>t.prepareForUpgrade(he),cancelUpgradePreparation:()=>t.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...t.prepareForUpgrade(r)}},L=()=>(t.cancelUpgradePreparation(),{cancelled:!0}),I=new Ro({registry:u,observations:A,enqueueConversationBatch:e=>t.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=m().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const r=await v.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!ko(u,e)||!bo(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,n,o)=>{const r=m().machineToken;if(!r)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:r,activationCauseMessageId:n,allowedActions:o})},audit:new Eo,resolveManagedAgentPolicy:e=>Po({...e,runtime:y.find(n=>n.type===e.registration.runtimeAdapter)})}),t.scheduleHandler=e=>Ie({serverUrl:g,machineToken:m().machineToken,req:e,client:l,queue:t.getScheduleQueue(),registry:u}),t.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(u.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await v.status({machineToken:m().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},t.setManagedRoomTurnPreparer(e=>I.prepareConversationTurn(e)),t.setManagedAgentDirectTurnPreparer((e,n)=>I.prepareDirectConversationTurn(e,n)),t.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?I.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?I.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:I.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),j.mkdirSync(me,{recursive:!0}),re(process.pid,c,{version:h}),te(process.pid,void 0,c),l.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(R.gray(`
|
|
3
|
+
Disconnecting...`)),P({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await t.cleanup(),x.stop(),D?.stop(),M?.stop(),l.disconnect(),await Promise.allSettled([b.close(),k.close()]),ee(process.pid,c),oe(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{Q()}),process.on("SIGTERM",()=>{Q()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>G()),i.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()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>G());const pe=i.command("config").description("View or modify configuration");pe.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${Ce()}`);const a=m(),s=Ee(a,X(process.env,a));if(a.serverUrl){const p=We(a.serverUrl);console.log(`Region: ${p} (${B[p].label})`)}console.log(JSON.stringify(s,null,2))}),pe.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((a,s)=>{const p=m();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(R.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;p.serverUrl=Ke(c),we(p),console.log(R.green(`\u2713 Server set to ${B[c].label}`)),console.log(R.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(R.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ae(i),Ue(i),Oe(i),Ve(i),Le(i),je(i),Be(i),Fe(i),i.parse();
|
|
@@ -21,6 +21,14 @@ export type ManagedWorkerRecord = {
|
|
|
21
21
|
healthNotifiedAt?: string | null;
|
|
22
22
|
lastHealthSummary?: string | null;
|
|
23
23
|
role?: 'managed_worker' | 'project_session';
|
|
24
|
+
/** Origins indexed by Provider run id; legacy records omit this and stay private. */
|
|
25
|
+
runRoomOrigins?: Record<string, ManagedWorkerRoomOrigin>;
|
|
26
|
+
};
|
|
27
|
+
export type ManagedWorkerRoomOrigin = {
|
|
28
|
+
registryKey: string;
|
|
29
|
+
registrationEpoch: number;
|
|
30
|
+
bindingId: string;
|
|
31
|
+
roomId: string;
|
|
24
32
|
};
|
|
25
33
|
export type ManagerRecord = {
|
|
26
34
|
sessionId: string;
|
|
@@ -54,6 +62,7 @@ export type ManagerInboxEvent = {
|
|
|
54
62
|
kind: 'worker.final' | 'worker.error' | 'worker.aborted' | 'worker.health';
|
|
55
63
|
priority: 'normal' | 'high';
|
|
56
64
|
runId?: string | null;
|
|
65
|
+
roomOrigin?: ManagedWorkerRoomOrigin | null;
|
|
57
66
|
summary: string;
|
|
58
67
|
createdAt: string;
|
|
59
68
|
updatedAt: string;
|
|
@@ -106,6 +115,7 @@ export declare class ManagerRegistry {
|
|
|
106
115
|
summary?: string | null;
|
|
107
116
|
}): ManagedWorkerRecord;
|
|
108
117
|
updateWorker(sessionId: string, patch: Partial<ManagedWorkerRecord>): ManagedWorkerRecord | undefined;
|
|
118
|
+
clearWorkerRunOrigins(): void;
|
|
109
119
|
listWorkers(managerSessionId: string, opts?: {
|
|
110
120
|
includeManagers?: boolean;
|
|
111
121
|
runningSessionIds?: Set<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs";import
|
|
1
|
+
import c from"node:fs";import I from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as h,sessionPreviewFromPayload as k}from"@shennian/wire";import{resolveShennianPath as v}from"../config/index.js";import{readMessages as p}from"../session/store.js";const u=v("manager-registry.json");function l(){return new Date().toISOString()}function w(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class W{load(){try{const e=JSON.parse(c.readFileSync(u,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return w()}}save(e){c.mkdirSync(I.dirname(u),{recursive:!0}),c.writeFileSync(u,JSON.stringify(e,null,2))}upsertManager(e){const s=this.load(),r=s.managers[e.sessionId],t=l(),n={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,controllerKind:e.controllerKind??r?.controllerKind??"managed_session",status:e.status??r?.status??"idle",deliveringInboxEventIds:e.deliveringInboxEventIds??r?.deliveringInboxEventIds??[],managedWorkerSessionIds:r?.managedWorkerSessionIds??[],attachedExternalChannels:r?.attachedExternalChannels??[],createdAt:r?.createdAt??t,updatedAt:t};return s.managers[e.sessionId]=n,this.save(s),n}getManager(e){return this.load().managers[e]}enqueueInboxEvent(e){const s=this.load(),r=s.managers[e.managerSessionId],t=s.workers[e.workerSessionId];if(!r||!t||t.managedBy!==r.sessionId||t.workDir!==r.workDir)return null;const n=l(),a=s.inbox[e.managerSessionId]??[],o=a.findIndex(g=>g.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?g.kind==="worker.health":g.kind===e.kind&&g.runId===e.runId)),i=o>=0?{...a[o],...e,updatedAt:n}:{...e,id:`mie_${m()}`,createdAt:n,updatedAt:n};return o>=0?a[o]=i:a.push(i),s.inbox[e.managerSessionId]=a,this.save(s),i}listInboxEvents(e){return[...this.load().inbox[e]??[]].sort((s,r)=>s.createdAt.localeCompare(r.createdAt))}removeInboxEvents(e,s){if(!s.length)return;const r=this.load(),t=new Set(s);r.inbox[e]=(r.inbox[e]??[]).filter(n=>!t.has(n.id)),this.save(r)}addWorker(e){const s=this.load(),r=s.managers[e.managerSessionId];if(!r)throw new Error("Manager runtime is not registered");const t=e.sessionId??`sess_worker_${m()}`,n=l(),a={sessionId:t,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:n,updatedAt:n,lastActivityAt:n,title:e.title??null,summary:e.summary??null,agentSessionId:null,runId:null};return s.workers[t]=a,r.managedWorkerSessionIds.includes(t)||r.managedWorkerSessionIds.push(t),r.updatedAt=n,this.save(s),a}updateWorker(e,s){const r=this.load(),t=r.workers[e];if(!t)return;const n={...t,...s,updatedAt:l()};return r.workers[e]=n,this.save(r),n}clearWorkerRunOrigins(){const e=this.load();let s=!1;for(const r of Object.values(e.workers))r.runRoomOrigins&&(delete r.runRoomOrigins,s=!0);for(const r of Object.values(e.inbox))for(const t of r)t.roomOrigin&&(delete t.roomOrigin,s=!0);s&&this.save(e)}listWorkers(e,s={}){const r=this.load(),t=r.managers[e];return t?Object.values(r.workers).filter(n=>n.managedBy===t.sessionId).filter(n=>n.workDir===t.workDir).filter(n=>s.includeManagers||n.agentType!=="manager").map(n=>this.decorateManagedWorker(n,s.runningSessionIds)).sort((n,a)=>a.lastActivityAt.localeCompare(n.lastActivityAt)):[]}getWorkerForManager(e,s){const r=this.load(),t=r.managers[e],n=r.workers[s];if(t&&n)return n.managedBy!==t.sessionId||n.workDir!==t.workDir||n.agentType==="manager"?void 0:this.decorateManagedWorker(n)}retireManager(e){const s=this.load(),r=Object.values(s.workers).filter(t=>t.managedBy===e);delete s.managers[e],delete s.inbox[e];for(const[t,n]of Object.entries(s.replyTargets))n.managerSessionId===e&&delete s.replyTargets[t];return this.save(s),r}completeRetiredWorker(e,s){const r=this.load(),t=r.workers[s];!t||t.managedBy!==e||(delete r.workers[s],this.save(r))}decorateManagedWorker(e,s){const r=s?.has(e.sessionId)?"running":e.status,t=this.readTranscriptSummary(e.sessionId,e.summary??null),n=t.lastMessagePreview??e.lastMessagePreview??e.summary??null,a=e.userGoal??t.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??a??n??e.summary??e.sessionId,userGoal:a,lastMessagePreview:n,summary:e.summary??n,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}readTranscriptSummary(e,s){const r=p(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:s};const t=[...r].sort((o,i)=>o.ts-i.ts),n=t.find(o=>o.role==="user"),a=[...t].reverse().find(o=>y(o));return{userGoal:n?f(y(n),160):null,lastMessagePreview:f(y(a),180)??s}}createReplyTarget(e){const s=this.load(),r={replyTarget:`rt_${m()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:l()};return s.replyTargets[r.replyTarget]=r,this.save(s),r}getReplyTarget(e){return this.load().replyTargets[e]}getLatestReplyTargetForManager(e){return Object.values(this.load().replyTargets).filter(s=>s.managerSessionId===e).sort((s,r)=>r.createdAt.localeCompare(s.createdAt))[0]}}function y(d){return!d||h(d.payload)?null:k(d.payload,180)}function f(d,e){return d?d.length>e?`${d.slice(0,e)}...`:d:null}export{W as ManagerRegistry};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentEvent, AgentAdapter } from '../agents/adapter.js';
|
|
2
2
|
import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from '@shennian/wire';
|
|
3
|
-
import { ManagerRegistry } from './registry.js';
|
|
3
|
+
import { ManagerRegistry, type ManagedWorkerRoomOrigin } from './registry.js';
|
|
4
4
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
5
5
|
import { RetiredChannelRuntime as ChannelRuntime, type ChannelRuntimeOptions, type WeChatAutomationLane } from './retired-external-channel-compat.js';
|
|
6
6
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
@@ -30,6 +30,8 @@ export declare class ManagerRuntimeService {
|
|
|
30
30
|
private healthTimer;
|
|
31
31
|
private startPromise;
|
|
32
32
|
private workerTextAcc;
|
|
33
|
+
private pendingWorkerOrigins;
|
|
34
|
+
prepareWorkerTurnOrigin(sessionId: string, origin?: ManagedWorkerRoomOrigin): void;
|
|
33
35
|
constructor(opts: ManagerRuntimeServiceOptions);
|
|
34
36
|
start(): Promise<void>;
|
|
35
37
|
private doStart;
|
|
@@ -67,7 +69,7 @@ export declare class ManagerRuntimeService {
|
|
|
67
69
|
private findWorker;
|
|
68
70
|
handleAppReq(req: ReqFrame): Promise<void>;
|
|
69
71
|
private handleIpc;
|
|
70
|
-
executeScopedWorkerAction(managerSessionId: string, action: ManagedManagerAction, params: Record<string, JsonValue
|
|
72
|
+
executeScopedWorkerAction(managerSessionId: string, action: ManagedManagerAction, params: Record<string, JsonValue>, roomOrigin?: ManagedWorkerRoomOrigin): Promise<JsonValue>;
|
|
71
73
|
private dispatchChatEnqueue;
|
|
72
74
|
private appendManagerVisibleMarkdown;
|
|
73
75
|
private enqueueWorkerEvent;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
`).trim()}function $(d){return d==="worker.final"?"completed":d==="worker.error"?"failed":d==="worker.aborted"?"stopped":"progress"}function U(d,e){return`manager-inbox:${_("sha256").update(d).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function j(d){const e=[];let t=0;for await(const r of d){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>H)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function G(d,e,t,r){d.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function S(){return F()?I.join(R.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function F(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ue{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=N(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=x.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=p,this.server.timeout=p,this.server.keepAliveTimeout=p,this.server.headersTimeout=p+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});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.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=S();l.mkdirSync(I.dirname(e),{recursive:!0}),l.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),l.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=S(),t=JSON.parse(l.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&l.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:g(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:g(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||g(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&g(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const t=this.registry.retireManager(e);this.managedPolicies.delete(e);const r=this.opts.getRuntime(),n=[],s=async(i,h)=>{const o=[];try{r.runJournal?.markSessionAborted(i)}catch(c){o.push(c)}try{r.recoveryBarrier?.end(i)}catch(c){o.push(c)}try{r.chatQueue?.purgeSession(i)}catch(c){o.push(c)}const a=r.sessions.get(i);let u=!a;if(a){if(a.managedRoomContextToken)try{r.revokeManagedRoomContextToken?.(a.managedRoomContextToken),a.managedRoomContextToken=null}catch(c){o.push(c)}try{await a.adapter.stop(),u=!0}catch(c){o.push(c)}}if(a&&u&&o.length===0&&(r.sessions.delete(i),a.heartbeatTimer&&clearInterval(a.heartbeatTimer),a.adapter.removeAllListeners()),u&&o.length===0)try{h?.()}catch(c){o.push(c),n.push(c)}else n.push(...o)};await s(e);for(const i of t)await s(i.sessionId,()=>this.registry.completeRetiredWorker(e,i.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return t.map(i=>i.sessionId)}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:g(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};t.agentSessionId&&(n.agentSessionId=t.agentSessionId);const s=`${e}:${t.runId}`;let i=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const o=(this.workerTextAcc.get(s)??"")+t.text;this.workerTextAcc.set(s,o);const a=k(o);a&&(n.summary=a.length>160?`${a.slice(0,160)}...`:a)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const o=k(this.workerTextAcc.get(s)??"");o&&(n.summary=o.length>240?`${o.slice(0,240)}...`:o,i=t.message||o),this.workerTextAcc.delete(s)}else t.state==="start"&&(n.status="running");const h=this.registry.updateWorker(e,n);h&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(h.managedBy,h,t.state,t.runId,i)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){G(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){m(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await j(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){m(t,410,{ok:!1,error:"feature_retired"});return}const s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){m(t,s.status,s.body);return}m(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){m(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r){const n={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},n[t],r,e);if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,s,i,h){if((await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${y()}`,method:"chat.enqueue",params:{...M(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:s,modelId:i,managedWorkerParentSessionId:h}}))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(W(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,s){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,summary:s||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),h=this.registry.getManager(e);i&&h?.controllerKind!=="native_skill"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const s=e-Date.parse(n.createdAt);if(s<10*6e4)continue;const i=e-Date.parse(n.lastActivityAt),h=r.sessions.get(n.sessionId)?.currentRunId,o=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!h){if(!o&&i>=10*6e4){const v="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",f=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:v});f&&this.enqueueWorkerEvent(f.managedBy,f,"error",n.runId??void 0,v)}continue}const a=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(a&&e-a<10*6e4)continue;const u=t.managers[n.managedBy];if(!u)continue;const c=Math.max(0,Math.floor(i/6e4)),b=k(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),w=`\u5DF2\u8FD0\u884C ${Math.floor(s/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
1
|
+
import N from"node:http";import{createHash as _,randomBytes as T,randomUUID as k}from"node:crypto";import m from"node:fs";import E from"node:os";import M from"node:path";import{ManagerRegistry as W}from"./registry.js";import{appendMessage as O}from"../session/store.js";import{requestSessionIndexPublish as D}from"../session/index-publisher.js";import{RetiredChannelRuntime as q}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as C}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as w}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as A}from"./worker-ipc.js";import{deriveWorkerTitle as B,formatWorkerMarkdown as H}from"./worker-markdown.js";import{markTrustedManagerEvent as S}from"../session/manager-event-authority.js";const $=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),y=12*6e4;let b=null;function ue(u){b=u}function ge(){return b}function p(u){return M.resolve(u||E.homedir())}function f(u,e,t){u.writeHead(e,{"content-type":"application/json; charset=utf-8"}),u.end(JSON.stringify(t))}function R(u){return u.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function U(u){return u==="worker.final"?"completed":u==="worker.error"?"failed":u==="worker.aborted"?"stopped":"progress"}function j(u,e){return`manager-inbox:${_("sha256").update(u).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function G(u){const e=[];let t=0;for await(const r of u){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>$)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function F(u,e,t,r){u.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function x(){return J()?M.join(E.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function J(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class de{opts;managedPolicies=new Map;registry=new W;channelRuntime;server=null;ipcUrl=null;ipcToken=T(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;pendingWorkerOrigins=new Map;prepareWorkerTurnOrigin(e,t){t?this.pendingWorkerOrigins.set(e,t):this.pendingWorkerOrigins.delete(e)}constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.registry.clearWorkerRunOrigins(),this.server=N.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=y,this.server.timeout=y,this.server.keepAliveTimeout=y,this.server.headersTimeout=y+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});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.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=x();m.mkdirSync(M.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=x(),t=JSON.parse(m.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&m.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:p(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:p(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||p(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&p(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const t=this.registry.retireManager(e);this.managedPolicies.delete(e);const r=this.opts.getRuntime(),n=[],i=async(s,d)=>{const a=[];try{r.runJournal?.markSessionAborted(s)}catch(l){a.push(l)}try{r.recoveryBarrier?.end(s)}catch(l){a.push(l)}try{r.chatQueue?.purgeSession(s)}catch(l){a.push(l)}const o=r.sessions.get(s);let c=!o;if(o){if(o.managedRoomContextToken)try{r.revokeManagedRoomContextToken?.(o.managedRoomContextToken),o.managedRoomContextToken=null}catch(l){a.push(l)}try{await o.adapter.stop(),c=!0}catch(l){a.push(l)}}if(o&&c&&a.length===0&&(r.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&a.length===0)try{d?.()}catch(l){a.push(l),n.push(l)}else n.push(...a)};await i(e);for(const s of t)await i(s.sessionId,()=>this.registry.completeRetiredWorker(e,s.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return t.map(s=>s.sessionId)}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const i=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:p(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:i?.agentType,providerModelId:i?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};if(t.agentSessionId&&(n.agentSessionId=t.agentSessionId),t.state==="start"){const a=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),a&&(n.runRoomOrigins={...r.runRoomOrigins??{},[t.runId]:a})}const i=`${e}:${t.runId}`;let s=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const a=(this.workerTextAcc.get(i)??"")+t.text;this.workerTextAcc.set(i,a);const o=R(a);o&&(n.summary=o.length>160?`${o.slice(0,160)}...`:o)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const a=R(this.workerTextAcc.get(i)??"");if(a&&(n.summary=a.length>240?`${a.slice(0,240)}...`:a,s=t.message||a),this.workerTextAcc.delete(i),r.runRoomOrigins?.[t.runId]){const o={...r.runRoomOrigins};delete o[t.runId],n.runRoomOrigins=o}}else t.state==="start"&&(n.status="running");const d=this.registry.updateWorker(e,n);d&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(d.managedBy,d,t.state,t.runId,s,r.runRoomOrigins?.[t.runId]??null)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){F(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){f(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await G(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){f(t,410,{ok:!1,error:"feature_retired"});return}const i=await A({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(i){f(t,i.status,i.body);return}f(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){f(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r,n){const i={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await A({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},i[t],r,e,{roomOrigin:n});if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,i,s,d,a){if((await this.opts.dispatchReq(S({type:"req",id:`manager-enqueue-${k()}`,method:"chat.enqueue",params:{...w(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:i,modelId:s,managedWorkerParentSessionId:d,managerWorkerRoomOrigin:a}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(O(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,i,s){const d=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,roomOrigin:s??null,summary:i||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),a=this.registry.getManager(e);d&&a?.controllerKind!=="native_skill"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const i=e-Date.parse(n.createdAt);if(i<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),d=r.sessions.get(n.sessionId)?.currentRunId,a=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!d){if(!a&&s>=10*6e4){const v="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",I=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:v});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,v)}continue}const o=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(o&&e-o<10*6e4)continue;const c=t.managers[n.managedBy];if(!c)continue;const l=Math.max(0,Math.floor(s/6e4)),g=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),h=`\u5DF2\u8FD0\u884C ${Math.floor(i/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
3
3
|
|
|
4
4
|
\u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
|
|
5
|
-
- \u6700\u8FD1\u6587\u672C\uFF1A${
|
|
5
|
+
- \u6700\u8FD1\u6587\u672C\uFF1A${g||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
|
|
6
6
|
- \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
|
|
7
|
-
- \u6700\u8FD1 ${
|
|
7
|
+
- \u6700\u8FD1 ${l} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:h}),this.registry.enqueueInboxEvent({managerSessionId:c.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,roomOrigin:n.runRoomOrigins?.[n.runId??""]??null,summary:h})&&c.controllerKind!=="native_skill"&&this.drainManagerInbox(c.sessionId)}}async interruptAndResumeManager(e,t,r,n){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${k()}`,method:"chat.abort",params:{...w(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r,n)}bindManagerAdapterEvents(e,t){t.on("agentEvent",r=>{if(r.state==="start"&&r.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,r.agentSessionId,n.workDir,n.modelId)}r.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(r.state==="final"||r.state==="error"||r.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,status:t})}async drainManagerInbox(e){const t=this.registry.getManager(e);if(!t||t.deliveringInboxEventIds?.length)return;const r=this.registry.listInboxEvents(e);if(!r.length)return;const n=r.find(g=>g.priority==="high"),i=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||i?.currentRunId&&!n)return;const d=(n??r[0]).roomOrigin??null,a=JSON.stringify(d),o=n?[n]:r.filter(g=>JSON.stringify(g.roomOrigin??null)===a).slice(0,20),c=o.map(g=>{const h=this.registry.getWorkerForManager(e,g.workerSessionId);return H({kind:U(g.kind),sessionId:g.workerSessionId,title:h?.title||B(h?.userGoal||h?.summary||g.summary),body:g.summary})}).join(`
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
`),
|
|
11
|
+
`),l=j(e,o);try{n&&i?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:o.map(h=>h.id)}),await this.interruptAndResumeManager(t,c,l,d)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:o.map(h=>h.id)}),await this.dispatchManagerContinuation(t,c,!0,l,d)),this.registry.removeInboxEvents(e,o.map(h=>h.id));const g=this.registry.getManager(e);g&&this.registry.upsertManager({...g,deliveringInboxEventIds:[]})}catch(g){const h=this.registry.getManager(e);h&&this.registry.upsertManager({...h,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${g instanceof Error?g.message:String(g)}`)}}acknowledgeManagerInboxDelivery(e){const t=this.registry.getManager(e),r=t?.deliveringInboxEventIds??[];!t||r.length===0||(this.registry.removeInboxEvents(e,r),this.registry.upsertManager({...t,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),t=this.opts.getRuntime();for(const r of Object.values(e.managers)){if(t.sessions.get(r.sessionId)?.currentRunId)continue;(r.status!=="idle"||r.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...r,status:"idle",deliveringInboxEventIds:[]});const i=this.registry.listInboxEvents(r.sessionId).filter(s=>s.kind==="worker.health"&&!t.sessions.get(s.workerSessionId)?.currentRunId).map(s=>s.id);this.registry.removeInboxEvents(r.sessionId,i),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n,i){const s=e.agentType??"manager",d=s==="manager"?e.modelId:e.providerModelId??void 0,a={...w(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:d,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n,...i?{managerEventRoomOrigin:i}:{}};if((await this.opts.dispatchReq(S({type:"req",id:`manager-${r?"enqueue":"send"}-${k()}`,method:"chat.enqueue",params:a})))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(t=>C(t,void 0,e,"manager")).join(`
|
|
12
12
|
|
|
13
|
-
`).trim()}}export{
|
|
13
|
+
`).trim()}}export{de as ManagerRuntimeService,ge as getManagerRuntimeService,ue as setManagerRuntimeService};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AgentType } from '@shennian/wire';
|
|
2
2
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
3
|
-
import { ManagerRegistry } from './registry.js';
|
|
3
|
+
import { ManagerRegistry, type ManagedWorkerRoomOrigin } from './registry.js';
|
|
4
4
|
import type { LocalReqDispatcher } from './runtime.js';
|
|
5
5
|
export type WorkerIpcResponse = {
|
|
6
6
|
status: number;
|
|
@@ -10,8 +10,10 @@ type WorkerIpcHost = {
|
|
|
10
10
|
registry: ManagerRegistry;
|
|
11
11
|
getRuntime: () => SessionManagerRuntime;
|
|
12
12
|
dispatchReq: LocalReqDispatcher;
|
|
13
|
-
dispatchChatEnqueue: (sessionId: string, agentType: AgentType, workDir: string, text: string, agentSessionId: string | null, modelId?: string, managedWorkerParentSessionId?: string) => Promise<void>;
|
|
13
|
+
dispatchChatEnqueue: (sessionId: string, agentType: AgentType, workDir: string, text: string, agentSessionId: string | null, modelId?: string, managedWorkerParentSessionId?: string, roomOrigin?: ManagedWorkerRoomOrigin) => Promise<void>;
|
|
14
14
|
appendManagerVisibleMarkdown: (managerSessionId: string, markdown: string, id: string) => void;
|
|
15
15
|
};
|
|
16
|
-
export declare function handleWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>, headerManagerSessionId: string
|
|
16
|
+
export declare function handleWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>, headerManagerSessionId: string, trustedOrigin?: {
|
|
17
|
+
roomOrigin?: ManagedWorkerRoomOrigin;
|
|
18
|
+
}): Promise<WorkerIpcResponse | null>;
|
|
17
19
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import w from"node:path";import{createHash as M,randomUUID as p}from"node:crypto";import{AVAILABLE_BUILTIN_AGENT_TYPES as q,extractPayloadText as W,isAgentHiddenPayload as E,isToolPayload as T}from"@shennian/wire";import{loadConfig as _}from"../config/index.js";import{getPersonalSyncIdentity as S}from"../personal-sync/epoch.js";import{readMessages as x,recordSession as D}from"../session/store.js";import{deriveWorkerTitle as k,formatWorkerMarkdown as f,normalizeWorkerTitle as b}from"./worker-markdown.js";function h(e){return w.resolve(e||process.cwd())}function v(e){return e==="manager"||e==="pi"?!1:e.startsWith("custom:")?!0:q.includes(e)}function A(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function $(e,i){const s=[];for(const g of e)if(!E(g.payload))if(T(g.payload)){let c="tool",a="completed";try{const l=JSON.parse(g.payload);c=l.name||c,a=l.status||a}catch{}s.push({...g,payload:`[\u5DE5\u5177] ${c} \xB7 ${a}`})}else s.push({...g,payload:W(g.payload)});return s.slice(-Math.max(1,Math.min(i,500)))}async function O(e,i,s,g,c){if(i==="/skill/context"){const r=_(),o=String(s.host||""),n=String(s.hostSessionId||""),d=h(String(s.workDir||""));if(!r.machineId||!r.machineToken&&!r.accessToken)throw new Error("Shennian CLI must be paired and logged in");if(o!=="codex"&&o!=="claude")throw new Error("unsupported_native_agent_host");if(!n)throw new Error("native_agent_session_unavailable");if(!d||!w.isAbsolute(d))throw new Error("workDir is required");const m=`skill_manager_${M("sha256").update(r.machineId).update("\0").update(o).update("\0").update(n).update("\0").update(d).digest("hex").slice(0,32)}`;return e.registry.upsertManager({sessionId:m,agentSessionId:null,workDir:d,machineId:r.machineId,modelId:o,agentType:o,controllerKind:"native_skill",status:"idle"}),{status:200,body:{ok:!0,managerSessionId:m}}}if(!i.startsWith("/sessions/")&&i!=="/memory/path")return null;const a=String(s.managerSessionId||g||"");if(!a)throw new Error("managerSessionId is required");const l=e.registry.getManager(a);if(!l)throw new Error("Manager runtime is not registered");if(i==="/sessions/list"){const r=new Set([...e.getRuntime().sessions.entries()].filter(([,n])=>!!n.currentRunId).map(([n])=>n));return{status:200,body:{ok:!0,sessions:e.registry.listWorkers(a,{runningSessionIds:r})}}}if(i==="/memory/path")return{status:200,body:{ok:!0,path:w.join(l.workDir,".shennian")}};if(i==="/sessions/start"){const r=String(s.agentType||s.agent||l.defaultWorkerAgentType||"codex");if(!v(r))throw new Error(`Unsupported manager worker agent: ${r}`);const o=h(String(s.workDir||l.workDir));if(o!==l.workDir)throw new Error("Manager can only start workers in the same workDir");const n=String(s.message||"");if(!n)throw new Error("message is required");const d=s.title?b(String(s.title)):k(n),m=e.registry.addWorker({managerSessionId:a,agentType:r,workDir:o,title:d,summary:n.slice(0,120)}),I=String(s.modelId||(r===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));D({sessionId:m.sessionId,agentType:r,workDir:o,agentSessionId:null,modelId:I||null,title:d,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(m.sessionId,r,o,n,null,I||void 0,a,c?.roomOrigin);const y=f({kind:"created",sessionId:m.sessionId,title:d,body:n});return e.appendManagerVisibleMarkdown(a,y,`worker-created:${m.sessionId}`),{status:200,body:{ok:!0,session:m,markdown:y}}}const u=String(s.sessionId||""),t=e.registry.getWorkerForManager(a,u);if(!t||t.role!=="managed_worker"||t.managedBy!==a)throw new Error("Worker not found in this manager scope");if(i==="/sessions/send"){const r=String(s.message||"");if(!r.trim())throw new Error("message is required");const o=s.enqueue===void 0?!0:!!s.enqueue,n=String(s.modelId||(t.agentType===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(t.sessionId,t.agentType,t.workDir,r,t.agentSessionId??null,n||void 0,a,c?.roomOrigin);const d=f({kind:"message",sessionId:u,title:t.title||k(t.userGoal||t.summary||r),body:r});return e.appendManagerVisibleMarkdown(a,d,`worker-message:${p()}`),{status:200,body:{ok:!0,markdown:d,queued:!0,directRequested:!o}}}if(i==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}};if(i==="/sessions/queue/edit"||i==="/sessions/queue/delete"){const r=i.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${r?"edit":"delete"}-${p()}`,method:r?"chat.queue.edit":"chat.queue.delete",params:{...S(),sessionId:u,queueMessageId:String(s.queueMessageId||s.messageId||""),...r?{text:String(s.message||s.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}}}if(i==="/sessions/stop"||i==="/sessions/terminate"){if(t.status==="final"||t.status==="error"||t.status==="aborted"){const r=t.status==="final"?"completed":t.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,status:t.status,markdown:f({kind:r,sessionId:u,title:t.title||k(t.userGoal||t.summary||""),body:t.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{...S(),sessionId:u}}),e.registry.updateWorker(u,{status:"aborted"}),{status:200,body:{ok:!0,markdown:f({kind:"stopped",sessionId:u,title:t.title||k(t.userGoal||t.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(i==="/sessions/read"){const r=Number(s.limit||200),o=x(u,{limit:Math.max(r*20,r)});return{status:200,body:{ok:!0,messages:$(o,r),rawMessageCount:o.length}}}if(i==="/sessions/wait"){const r=Math.max(1e3,Math.min(Number(s.timeoutMs||6e5),6e5)),o=Date.now()+r;let n;for(;Date.now()<o&&(n=e.registry.listInboxEvents(a).find(m=>m.workerSessionId===u),!n);)await new Promise(m=>setTimeout(m,1e3));if(!n)return{status:200,body:{ok:!0,timedOut:!0,status:e.registry.getWorkerForManager(a,u)?.status}};const d=f({kind:A(n.kind),sessionId:u,title:t.title||k(t.userGoal||t.summary||n.summary),body:n.summary});return e.registry.removeInboxEvents(a,[n.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:d,event:n}}}return null}export{O as handleWorkerIpcRoute};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type AgentType, type ManagedAgentPermissionConfirmation, type ManagedAgentPermissionPolicy, type ManagedAgentRuntimeProtection, type RoomMessageView, type SessionMode } from '@shennian/wire';
|
|
2
2
|
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
3
|
+
import type { ManagedRoomAction } from './managed-context.js';
|
|
4
|
+
import type { ManagedWorkerRoomOrigin } from '../manager/registry.js';
|
|
3
5
|
import type { ManagedRoomEnqueueParams, ManagedRoomQueueContext, PreparedManagedRoomTurn } from '../session/queue.js';
|
|
4
6
|
import type { RoomHostSessionObservationStore } from './host-session-observations.js';
|
|
5
7
|
import { type RoomSessionRegistration, type RoomSessionRegistry } from './session-registry.js';
|
|
@@ -38,7 +40,7 @@ export declare class RoomActivationService {
|
|
|
38
40
|
constructor(deps: {
|
|
39
41
|
registry: RoomSessionRegistry;
|
|
40
42
|
observations: RoomHostSessionObservationStore;
|
|
41
|
-
issueManagedRoomContext: (registryKey: string, activationCauseMessageId?: string) => string;
|
|
43
|
+
issueManagedRoomContext: (registryKey: string, activationCauseMessageId?: string, allowedActions?: ManagedRoomAction[]) => string;
|
|
42
44
|
now?: () => Date;
|
|
43
45
|
audit?: Pick<RoomActivationAudit, 'record'>;
|
|
44
46
|
enqueueConversationBatch: (inputs: RoomConversationQueueInput[]) => Promise<void>;
|
|
@@ -52,7 +54,11 @@ export declare class RoomActivationService {
|
|
|
52
54
|
});
|
|
53
55
|
processMessages(roomId: string, messages: RoomMessageView[], registrations: RoomSessionRegistration[], context?: RoomActivationContext): Promise<void>;
|
|
54
56
|
prepareConversationTurn(contexts: ManagedRoomQueueContext[]): Promise<PreparedManagedRoomTurn>;
|
|
55
|
-
prepareDirectConversationTurn(sessionId: string
|
|
57
|
+
prepareDirectConversationTurn(sessionId: string, options?: {
|
|
58
|
+
roomOrigin?: ManagedWorkerRoomOrigin;
|
|
59
|
+
allowedRoomActions?: ManagedRoomAction[];
|
|
60
|
+
managerEvent?: boolean;
|
|
61
|
+
}): Promise<PreparedManagedRoomTurn | null>;
|
|
56
62
|
private prepareManagedTurn;
|
|
57
63
|
private audit;
|
|
58
64
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as
|
|
2
|
-
${i.attachments.map(
|
|
3
|
-
`)}`:"";return{speaker:i.author.displayName,body:`${
|
|
1
|
+
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as P}from"@shennian/wire";import{getBuiltinAgentVersion as S,resolveBuiltinCommand as B}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as K,resolveCustomRuntimeExecutableFingerprint as x}from"../agents/custom-managed-runtime.js";import{loadConfig as $}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as W}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as q}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as D}from"./activation-policy.js";import{buildManagedAgentPermissionPrompt as E,composeManagedRoomPrompt as R}from"./managed-prompt-composer.js";import{isCurrentRegistration as f}from"./registration-authority.js";const F=4,N=6e4,O=6,V=5*6e4;class ce{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,i,t,s={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],g=[],u=new Map,m=new Set,c=[...i].sort((r,a)=>J(r.sequence,a.sequence));for(const r of t){const a=this.deps.registry.getByRegistryKey(r.registryKey);!a?.binding||!f(this.deps.registry,r)||a.binding.roomId!==e||a.binding.status!=="active"&&a.binding.status!=="paused"&&a.binding.activationQueue.length}for(const r of t){const a=this.deps.registry.getByRegistryKey(r.registryKey);if(!a?.binding||!f(this.deps.registry,r)||a.binding.roomId!==e||a.binding.activationQueue.length===0&&!a.binding.activeActivation||(m.add(a.registryKey),a.binding.status!=="active"||a.binding.activationMode==="paused")||a.binding.activationQueue.length===0)continue;const l=this.deps.observations.getBySource(a);if(!l?.workspacePath||l.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(a.registryKey,s);for(const o of a.binding.activationQueue)d.push({registration:a,input:{params:{sessionId:a.nianSessionId,text:o.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(a.runtimeAdapter),sessionMode:s.agentsByIdentityId?.[a.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:s.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:s.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:s.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:l.workspacePath,agentSessionId:a.sourceSessionKey,queueMessageId:`room-activation-${a.binding.bindingId}-${o.causeMessageId}`,clientMessageId:`room-activation-${a.binding.bindingId}-${o.causeMessageId}`},context:{registryKey:a.registryKey,causeMessageId:o.causeMessageId,rootCauseMessageId:o.rootCauseMessageId,sequence:o.sequence,hopCount:o.hopCount}}})}for(const r of c){const a=new Set(D(r,t).map(l=>l.registration.registryKey));for(const l of t){const o=this.deps.registry.getByRegistryKey(l.registryKey);if(!o?.binding||!f(this.deps.registry,l)||o.binding.roomId!==e||o.binding.status!=="active"&&o.binding.status!=="paused"||s.agentsByIdentityId?.[o.binding.agentIdentityId]?.configurationPending||BigInt(r.sequence)<=BigInt(o.binding.lastActivatedSequence))continue;const I=u.get(o.registryKey)??[...o.binding.mentionContext??[]];if(u.set(o.registryKey,I),a.has(o.registryKey)){const p=(r.activationCause?.hopCount??0)+(r.author.principalType==="agent"?1:0),h=r.activationCause?.rootCauseMessageId??r.id;if(p>F)this.audit(o,r.id,h,p,"suppressed","max_hops");else{const v=this.deps.registry.claimActivation({registryKey:o.registryKey,causeMessageId:r.id,windowMs:N,maxActivations:O,pauseMs:V,now:this.deps.now?.()});if(!v.allowed){v.newlyPaused&&this.audit(o,r.id,h,p,"suppressed","rate_limited"),g.push({registryKey:o.registryKey,sequence:r.sequence});continue}const b=this.deps.observations.getBySource(o);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const M=w([r]),C=o.binding.activationMode==="mention_only"?Q([...I.map(_=>_.deliveryText),M]):M;this.latestContexts.set(o.registryKey,s),d.push({registration:o,input:{params:{sessionId:o.nianSessionId,text:C,agentType:y(o.runtimeAdapter),sessionMode:s.agentsByIdentityId?.[o.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:s.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:s.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:s.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:o.sourceSessionKey,queueMessageId:`room-activation-${o.binding.bindingId}-${r.id}`,clientMessageId:`room-activation-${o.binding.bindingId}-${r.id}`},context:{registryKey:o.registryKey,causeMessageId:r.id,rootCauseMessageId:h,sequence:r.sequence,hopCount:p}}}),o.binding.activationMode==="mention_only"&&u.set(o.registryKey,[])}}else o.binding.activationMode==="mention_only"&&r.kind!=="system"&&r.author.membershipId!==o.binding.agentMembershipId&&(I.some(p=>p.sequence===r.sequence)||I.push({sequence:r.sequence,deliveryText:w([r])}));g.push({registryKey:o.registryKey,sequence:r.sequence})}}await this.deps.enqueueConversationBatch(d.map(r=>r.input));for(const r of m)this.deps.registry.retireLegacyActivationState(r,this.deps.now?.()??new Date);for(const[r,a]of u)this.deps.registry.getByRegistryKey(r)?.lifecycleState!=="closed"&&this.deps.registry.replaceMentionContext(r,a,this.deps.now?.()??new Date);for(const r of d)this.audit(r.registration,r.input.context.causeMessageId,r.input.context.rootCauseMessageId,r.input.context.hopCount,"queued","conversation_queue");for(const r of g)this.deps.registry.getByRegistryKey(r.registryKey)?.lifecycleState!=="closed"&&this.deps.registry.advanceCursor({registryKey:r.registryKey,cursor:"activated",sequence:r.sequence})}async prepareConversationTurn(e){const i=e.at(-1);if(!i)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(i.registryKey);if(!t)throw new Error("managed_room_binding_unavailable");const s=await this.prepareManagedTurn(t,i.causeMessageId);return this.audit(t,i.causeMessageId,i.rootCauseMessageId,i.hopCount,"activated","conversation_queue_dispatch"),s}async prepareDirectConversationTurn(e,i={}){const t=i.roomOrigin?this.deps.registry.getByRegistryKey(i.roomOrigin.registryKey):this.deps.registry.getByNianSessionId(e);return t?.nianSessionId!==e||!t||i.roomOrigin&&(t.registrationEpoch!==i.roomOrigin.registrationEpoch||t.binding?.bindingId!==i.roomOrigin.bindingId||t.binding.roomId!==i.roomOrigin.roomId)?null:this.prepareManagedTurn(t,void 0,i)}async prepareManagedTurn(e,i,t){if(!f(this.deps.registry,e)||!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const s=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!s)throw new Error("managed_room_prompt_context_unavailable");const d=s.agentsByIdentityId?.[e.binding.agentIdentityId];if(d?.configurationPending)throw new Error("managed_agent_configuration_pending");if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const g=d.sessionMode==="manager"?"manager":"standard",u=g==="manager"?{...d.managerConfig,defaultWorkerAgentType:d.managerConfig?.defaultWorkerAgentType??y(e.runtimeAdapter),defaultWorkerModelId:d.managerConfig?.defaultWorkerModelId??null}:null,m=this.deps.observations.getBySource(e);if(!m?.workspacePath||m.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const c=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:m.workspacePath}):await U({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:m.workspacePath});if(!f(this.deps.registry,e))throw new Error("managed_room_binding_unavailable");const r=H(s,e,c),a=this.deps.issueManagedRoomContext(e.registryKey,i,t?.allowedRoomActions);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(a))throw new Error("managed_room_context_unavailable");return{agentType:y(e.runtimeAdapter),workDir:c.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:t?G({managerEvent:t.managerEvent===!0,roomSendAllowed:t.allowedRoomActions?.includes("send")===!0,agentSystemPrompt:s.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],managedAgentPolicy:c,runtimeAdapter:y(e.runtimeAdapter)}):r.systemPrompt,sessionMode:g,managerConfig:u,managerDefaultWorkerAgentType:u?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:u?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:a,managedAgentPolicy:c,managedRoomPromptSnapshot:r.snapshot}}audit(e,i,t,s,d,g){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:i,rootCauseMessageId:t,hopCount:s,outcome:d,reason:g,at:this.deps.now?.()})}}function w(n){const e=n.map(i=>{const t=i.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",s=i.attachments.length>0?`
|
|
2
|
+
${i.attachments.map(L).join(`
|
|
3
|
+
`)}`:"";return{speaker:i.author.displayName,body:`${t}${s}`}});return A(e)}function L(n){return`\u9644\u4EF6\uFF1A${`${j(n.name)} \xB7 ${z(n.byteSize)}`}\uFF5C${n.mimeType}\uFF5CattachmentId=${n.attachmentId}`}function j(n){return String(n||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function z(n){const e=Number(n);if(!Number.isFinite(e)||e<0)return`${n} B`;if(e<1024)return`${e} B`;const i=["KB","MB","GB","TB"];let t=e,s=-1;do t/=1024,s+=1;while(t>=1024&&s<i.length-1);const d=t>=100?0:t>=10?1:2;return`${t.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${i[s]}`}function Q(n){return A(n.map(X))}function X(n){const e=n.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const i=n.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(i)return{speaker:i[1],body:i[2]};const t=n.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(t)return{speaker:t[1],body:t[2]};const s=n.match(/^([^\n]+)\n([\s\S]*)$/);return s?{speaker:s[1],body:s[2]}:{speaker:"\u7FA4\u6210\u5458",body:n.trim()}}function A(n){if(n.length===1){const[e]=n;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
|
|
4
4
|
${e.body}`}return`[\u7FA4\u6D88\u606F]
|
|
5
|
-
${
|
|
5
|
+
${n.map(e=>`<${e.speaker}>
|
|
6
6
|
${e.body}`).join(`
|
|
7
7
|
|
|
8
|
-
`)}`}function
|
|
8
|
+
`)}`}function G(n){const e=E({requestedPolicy:n.managedAgentPolicy.requestedPolicy,enforcement:n.managedAgentPolicy.enforcement,resolvedWorkDir:n.managedAgentPolicy.adapterPolicy.workDir}),i=n.managerEvent&&n.roomSendAllowed?`\u8FD9\u662F\u7531\u5F53\u524D\u7FA4\u804A\u4EFB\u52A1\u521B\u5EFA\u7684 Worker \u89E6\u53D1\u7684\u79C1\u6709 Manager \u7EED\u8F6E\u3002\u5148\u4F7F\u7528 Manager \u5DE5\u5177\u8BFB\u53D6\u5E76\u6838\u9A8C Worker \u7ED3\u679C\u3002\u53EA\u628A\u9762\u5411\u7528\u6237\u7684\u6700\u7EC8\u8FDB\u5C55\u6216\u7ED3\u8BBA\u53D1\u9001\u5230\u8BE5\u4EFB\u52A1\u539F\u59CB\u7FA4\u804A\uFF0C\u4E0D\u53D1\u9001 Worker \u539F\u59CB\u6D3B\u52A8\u3001\u5DE5\u5177\u65E5\u5FD7\u3001\u5185\u90E8\u6807\u8BC6\u6216\u8C03\u5EA6\u7EC6\u8282\u3002\u9700\u8981\u53D1\u5E03\u65F6\u5FC5\u987B\u663E\u5F0F\u4F7F\u7528${n.runtimeAdapter==="codex"?" shennian_room_send \u5DE5\u5177":" shennian room send \u547D\u4EE4"}\uFF1B\u53EA\u5728\u5F53\u524D\u5185\u90E8\u5BF9\u8BDD\u4E2D\u751F\u6210\u6587\u5B57\u4E0D\u4F1A\u53D1\u9001\u5230\u7FA4\u804A\u3002`:n.managerEvent?"\u8FD9\u662F Worker \u89E6\u53D1\u7684\u79C1\u6709 Manager \u7EED\u8F6E\u3002\u53EF\u4EE5\u4F7F\u7528 Manager \u5DE5\u5177\u8BFB\u53D6\u5E76\u6838\u9A8C Worker \u7ED3\u679C\uFF0C\u4F46\u4E0D\u5F97\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u56DE\u590D\u4EFB\u4F55 Room \u5185\u5BB9\u3002":"\u8FD9\u662F Owner \u4E0E Manager \u7684\u79C1\u6709\u5BF9\u8BDD\u3002\u53EF\u4EE5\u4F7F\u7528 Manager \u5DE5\u5177\u7BA1\u7406 Worker\uFF0C\u4F46\u4E0D\u5F97\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u56DE\u590D\u4EFB\u4F55 Room \u5185\u5BB9\u3002",t=n.agentSystemPrompt?.trim();return[`[\u795E\u5FF5\u53D7\u7BA1\u79C1\u6709\u7EED\u8F6E\u89C4\u5219]
|
|
9
|
+
${i}
|
|
10
|
+
\u4E0D\u5F97\u731C\u6D4B\u6216\u5C55\u793A Room\u3001Agent\u3001Session\u3001Binding\u3001Membership\u3001Machine \u6807\u8BC6\u6216\u4EFB\u4F55\u51ED\u636E\u3002`,e,t?`[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]
|
|
11
|
+
${t}`:""].filter(Boolean).join(`
|
|
12
|
+
|
|
13
|
+
`)}function H(n,e,i){const t=e.binding?n.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!t)throw new Error("managed_room_prompt_incomplete");return R({runtimeAdapter:y(e.runtimeAdapter),agent:{displayName:t.displayName,description:t.description,systemPrompt:n.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],configurationVersion:t.configurationVersion},room:{name:n.roomName,activationMode:e.binding.activationMode,members:n.members},runtimePermissions:{requestedPolicy:i.requestedPolicy,enforcement:i.enforcement,resolvedWorkDir:i.adapterPolicy.workDir}})}async function U(n){const e=y(n.registration.runtimeAdapter),i=k(e)?B(e):null,t=e.startsWith("custom:")?$().customAgents?.[e.slice(7)]:void 0;t&&K(e,t);const s=n.runtime?.version??(i?S(e,i):t?.caps.version),d=n.runtime?{platform:n.runtime.runtimePlatform??process.platform,isWsl:n.runtime.runtimeIsWsl??!1,executableFingerprint:n.runtime.executableFingerprint}:i?W(i):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:t?x(t):void 0},g={agentType:e,runtimeVersion:s,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},u=q(g),m=Z({confirmation:n.permissionConfirmation,policy:n.permissionPolicy,runtimeAdapter:e,protection:u}),c=await T({...g,policy:n.permissionPolicy,workDir:n.workDir,hostFullAccessConfirmed:m&&n.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:m&&n.permissionConfirmation?.limitedProtection});if(!c.ok){const r=c.unsupportedCapabilities?.length?`:${c.unsupportedCapabilities.join(",")}`:"";throw new Error(`${c.errorCode}${r}`)}return c.value}function Z(n){const{confirmation:e,policy:i,runtimeAdapter:t,protection:s}=n,d=P(i);return!!(e&&e.policyVersion===i.version&&e.runtimeAdapter===t&&e.runtimeVersion===s.runtimeVersion&&e.platform===s.platform&&e.executableFingerprint===s.executableFingerprint&&(e.executionMode??"restricted")===d&&e.hostFullAccess===(d==="native-full"||i.fileAccess==="host-full")&&e.limitedProtection===(d==="restricted"&&s.level!=="hard"))}function y(n){return n==="claude_code"?"claude":n}function J(n,e){const i=BigInt(n),t=BigInt(e);return i<t?-1:i>t?1:0}export{ce as RoomActivationService,Z as managedAgentPermissionConfirmationMatches,U as resolveManagedAgentPolicy};
|
|
@@ -5,6 +5,8 @@ type ManagedRoomGrant = {
|
|
|
5
5
|
tokenHash: string;
|
|
6
6
|
registryKey: string;
|
|
7
7
|
registrationEpoch: number;
|
|
8
|
+
bindingId: string;
|
|
9
|
+
roomId: string;
|
|
8
10
|
credentialHash: string;
|
|
9
11
|
allowedActions: ReadonlySet<ManagedRoomAction>;
|
|
10
12
|
activationCauseMessageId?: string;
|
|
@@ -33,7 +35,7 @@ export declare class ManagedRoomContextAuthority {
|
|
|
33
35
|
}): string;
|
|
34
36
|
resolve(input: {
|
|
35
37
|
token: string;
|
|
36
|
-
action: ManagedRoomAction;
|
|
38
|
+
action: ManagedRoomAction | 'manager';
|
|
37
39
|
machineCredential: string;
|
|
38
40
|
}): {
|
|
39
41
|
grant: ManagedRoomGrant;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d from"node:crypto";const h="SHENNIAN_MANAGED_ROOM_CONTEXT";class i extends Error{code;constructor(e,t){super(t),this.code=e,this.name="ManagedRoomContextError"}}class m{registry;now;grants=new Map;constructor(e,t=Date.now){this.registry=e,this.now=t}issue(e){const t=this.registry.getByRegistryKey(e.registryKey);if(!t?.binding||t.lifecycleState==="closed")throw new i("managed_room_context_unavailable","Managed Room context is unavailable.");if(t.binding.status!=="active"&&t.binding.status!=="paused")throw new i("managed_room_binding_unavailable","Managed Room binding is unavailable.");if(!t.binding.agentMembershipId)throw new i("managed_room_membership_unavailable","Managed Room membership is unavailable.");const n=e.machineCredential.trim();if(!n)throw new i("machine_not_authorized","This machine is not authorized yet.");const a=`mrc_${d.randomBytes(32).toString("base64url")}`,o=s(a);return this.grants.set(o,{tokenHash:o,registryKey:t.registryKey,registrationEpoch:t.registrationEpoch,bindingId:t.binding.bindingId,roomId:t.binding.roomId,credentialHash:s(n),allowedActions:new Set(e.allowedActions??["status","read","send","attachment.get","pause"]),activationCauseMessageId:e.activationCauseMessageId,expiresAt:this.now()+Math.min(Math.max(e.ttlMs??10*6e4,1e3),30*6e4)}),this.prune(),a}resolve(e){const t=s(e.token),n=this.grants.get(t);if(!n||n.expiresAt<=this.now())throw this.grants.delete(t),new i("managed_room_context_expired","Managed Room context expired.");if(!g(n.credentialHash,s(e.machineCredential)))throw new i("managed_room_context_invalid","Managed Room context is invalid.");if(e.action!=="manager"&&!n.allowedActions.has(e.action))throw new i("managed_room_action_forbidden","This action is not allowed in the managed Room context.");const a=this.registry.getByRegistryKey(n.registryKey),o=a?.binding;if(!a||!o||a.registrationEpoch!==n.registrationEpoch||o.bindingId!==n.bindingId||o.roomId!==n.roomId||a.lifecycleState==="closed"||o.status!=="active"&&o.status!=="paused"||!o.agentMembershipId)throw this.grants.delete(t),new i("managed_room_context_revoked","Managed Room context was revoked.");return{grant:n,registration:a,binding:o}}revokeToken(e){this.grants.delete(s(e))}revokeSession(e){for(const[t,n]of this.grants)n.registryKey===e&&this.grants.delete(t)}prune(){const e=this.now();for(const[t,n]of this.grants)n.expiresAt<=e&&this.grants.delete(t)}}function s(r){return d.createHash("sha256").update(r,"utf8").digest("hex")}function g(r,e){return d.timingSafeEqual(Buffer.from(r,"hex"),Buffer.from(e,"hex"))}export{h as MANAGED_ROOM_CONTEXT_ENV,m as ManagedRoomContextAuthority,i as ManagedRoomContextError};
|
|
@@ -18,7 +18,7 @@ ${o.length?o.join(`
|
|
|
18
18
|
${u?`\u53EA\u5728\u5185\u90E8\u5BF9\u8BDD\u4E2D\u751F\u6210\u6587\u5B57\u4E0D\u7B49\u4E8E\u5DF2\u7ECF\u56DE\u590D\u7FA4\u804A\uFF0C\u5B8C\u6210\u7FA4\u804A\u53D1\u9001\u5FC5\u987B\u8C03\u7528\u5F53\u524D\u7FA4\u804A\u5DE5\u5177\u3002
|
|
19
19
|
\u672C\u5730\u9644\u4EF6\u4E0A\u4F20\u5F53\u524D\u4E0D\u53EF\u7528\uFF1B\u9700\u8981\u53D1\u9001\u957F\u6587\u6863\u65F6\uFF0C\u5148\u53D1\u9001\u7B80\u77ED\u6458\u8981\u5E76\u8BF4\u660E\u6B64\u9650\u5236\u3002`:`\u53EA\u5728\u5185\u90E8\u5BF9\u8BDD\u4E2D\u751F\u6210\u6587\u5B57\u4E0D\u7B49\u4E8E\u5DF2\u7ECF\u56DE\u590D\u7FA4\u804A\uFF0C\u5B8C\u6210\u7FA4\u804A\u53D1\u9001\u5FC5\u987B\u6267\u884C Shennian CLI\u3002
|
|
20
20
|
\u957F\u6587\u6863\u4F18\u5148\u4F5C\u4E3A\u9644\u4EF6\u53D1\u9001\uFF0C\u5E76\u9644\u7B80\u77ED\u7EAF\u6587\u672C\u8BF4\u660E\u3002`}
|
|
21
|
-
\u6536\u5230\u9700\u8981\u5904\u7406\u7684\u4EFB\u52A1\
|
|
21
|
+
\u6536\u5230\u9700\u8981\u5904\u7406\u7684\u4EFB\u52A1\u65F6\uFF0C\u5148\u5728\u7FA4\u91CC\u7B80\u77ED\u786E\u8BA4\u4E00\u6B21\uFF0C\u5E76\u8BF4\u660E\u63A5\u4E0B\u6765\u505A\u4EC0\u4E48\uFF0C\u518D\u5F00\u59CB\u6267\u884C\uFF1B\u5B8C\u6210\u540E\u518D\u56DE\u590D\u7ED3\u679C\u3002\u53EA\u6709\u4EFB\u52A1\u660E\u663E\u8017\u65F6\u4E14\u51FA\u73B0\u503C\u5F97\u544A\u77E5\u7528\u6237\u7684\u5B9E\u9645\u8FDB\u5C55\u65F6\uFF0C\u624D\u5728\u4E2D\u9014\u540C\u6B65\u8FDB\u5C55\u3002\u7B80\u5355\u95EE\u9898\u53EF\u76F4\u63A5\u56DE\u7B54\uFF0C\u4E0D\u9700\u8981\u5148\u786E\u8BA4\u3002
|
|
22
22
|
Room \u6B63\u6587\u5F53\u524D\u6309\u7EAF\u6587\u672C\u663E\u793A\uFF1B\u53D1\u9001\u65F6\u4F7F\u7528\u77ED\u6BB5\u843D\u548C\u81EA\u7136\u6362\u884C\uFF0C\u4E0D\u8981\u4F7F\u7528 Markdown \u6807\u8BB0\u3002`,l,a?`[\u4F1A\u8BDD\u6A21\u5F0F]
|
|
23
23
|
${a}`:"",i?`[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]
|
|
24
24
|
${i}`:"",c,`[\u7FA4\u6D88\u606F\u683C\u5F0F]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ScheduleAction } from '@shennian/wire';
|
|
2
2
|
import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
|
|
3
3
|
import type { JsonValue } from '../daemon-ipc/json.js';
|
|
4
|
+
import type { ManagedWorkerRoomOrigin } from '../manager/registry.js';
|
|
4
5
|
import { ManagedRoomContextAuthority } from './managed-context.js';
|
|
5
6
|
import type { HttpRoomActionClient } from './room-action-client.js';
|
|
6
7
|
import type { RoomAttachmentCache } from './attachment-cache.js';
|
|
@@ -36,6 +37,7 @@ export type ManagedRoomExecutor = (params: JsonValue | undefined, options?: Mana
|
|
|
36
37
|
export type ManagedManagerAction = 'manager.sessions.list' | 'manager.sessions.start' | 'manager.sessions.send' | 'manager.sessions.queue' | 'manager.sessions.queue.edit' | 'manager.sessions.queue.delete' | 'manager.sessions.stop' | 'manager.sessions.read' | 'manager.memory.path';
|
|
37
38
|
export type ManagedManagerExecutor = (input: {
|
|
38
39
|
managerSessionId: string;
|
|
40
|
+
roomOrigin?: ManagedWorkerRoomOrigin;
|
|
39
41
|
action: ManagedManagerAction;
|
|
40
42
|
params: Record<string, JsonValue>;
|
|
41
43
|
}) => Promise<JsonValue>;
|