shennian 0.4.71 → 0.4.72
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 +36 -31
- package/dist/src/agents/claude.js +4 -4
- package/dist/src/agents/model-registry/discovery.js +3 -3
- package/dist/src/agents/model-registry/parsers.js +4 -4
- package/dist/src/agents/model-registry/service.d.ts +6 -2
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/model-registry/types.d.ts +9 -1
- package/dist/src/agents/model-registry/types.js +1 -1
- package/dist/src/commands/agent-readiness.js +2 -2
- package/dist/src/commands/manager.js +1 -1
- package/dist/src/commands/worker.d.ts +11 -0
- package/dist/src/commands/worker.js +2 -0
- package/dist/src/daemon-ipc/agent-readiness.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +6 -6
- package/dist/src/manager/registry.d.ts +48 -2
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +2 -2
- package/dist/src/manager/runtime.js +6 -6
- package/dist/src/manager/worker-ipc.d.ts +7 -0
- package/dist/src/manager/worker-ipc.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/queue.js +1 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +6 -0
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as ve}from"./commands/schedule.js";import{resolvePersonalScheduleContext as Ie}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Se}from"./schedules/server-client.js";import{Command as ye}from"commander";import P from"chalk";import we from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as Ce,getConfigPath as be,getShennianDir as ke,resolveShennianPath as H,normalizeAutoUpgradePolicy as Re}from"./config/index.js";import{CliRelayClient as Pe}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Ae,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Te,runSmartStart as De}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as Me,clearDaemonLauncher as oe,findRunningDaemonProcessIds as _e,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as xe,stopPreparedRemoteAccessService as $e,isIsolatedDaemonE2eInstance as Ne,getDaemonStatus as te,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as ae}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
|
|
2
|
-
`),process.exitCode=1}function qo(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 ye;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),Ge(i),qe(i),ve(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",we.hostname()).action(async a=>{const s=m(),p=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await De(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);se()}catch{}Ae(X(process.env,m()));const p=go();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=Me(),y=ao({lockPath:Bo,instanceId:c});y.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(y.ownerPid?` (PID ${y.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Fo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),y.release(),process.exit(0)}catch{}}catch{}const q=Ne(process.env,pe)?[]:_e(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),y.release(),process.exit(0)),re(process.pid,c,{version:F}),ae(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",y.release);let $=null;try{oo()||($=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{no({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{Vo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=m(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??B.cn.url,f=ie(),v=await ro({serverUrl:g,authToken:l.machineToken??l.accessToken}),w=v.map(e=>e.type),N=io(v),u=new wo,T=new ko,C=new xo(u),I=new Co(g),ue=new Mo({actionClient:I}),fe=new _o({actionClient:I});let S=null,r;const z=$o({authority:C,actionClient:I,attachmentCache:ue,attachmentUploader:fe,observations:T,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 t=u.getByRegistryKey(e)?.binding;t&&u.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await ce({paths:uo(),handlers:{...ho({daemonVersion:f,protocolVersion:yo,getStatus:()=>te(),getAgents:()=>v,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()}}),...vo({getStatus:()=>te(),discover:async()=>{if(!r)throw new Error("daemon_starting");const e=await Io(!0);return v.splice(0,v.length,...e.detectedAgents),d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:e.agentList,agents:e.agents}}),e.agents.map(n=>{const o=e.detectedAgents.find(t=>t.type===n.type);return{...n,runtimePlatform:o?.runtimePlatform,runtimeIsWsl:o?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return So(e,{ignoreNativeSession:(n,o)=>R?.ignoreNativeSource(n,o)})}})}});let k;try{k=await ce({paths:fo(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=Ie(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 D=()=>{J||(J=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",D),process.once("SIGTERM",D),!l.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",D),process.removeListener("SIGTERM",D);const K=`${Go(g)}/relay/machine`,U=l.machineToken;if(!U)return;try{await Oo({serverUrl:g,machineToken:U,machineId:l.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)}l.machineId&&co(g,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${f}) agents: ${w.join(",")}`),A({level:"info",wsEvent:"daemon.start",metadata:{version:f,agents:w}});let R=null,M=null,_=0,h=Ze();const W=Re(l.autoUpgrade),d=new Pe({serverUrl:K,machineToken:U,cliVersion:f,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:de,sessionSyncCapabilities:le,daemonInstallationId:No(),dataEpoch:me,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),A({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),so(v,{serverUrl:l.serverUrl??g,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),h&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:h.from,to:h.to,...h.jobId?{jobId:h.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),h){const o=h;h=null,o.jobId&&to(o.jobId,o.to),Ye(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),lo({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.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"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&d.getState()==="connected"&&R?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&d.getState()==="connected"){if(o instanceof jo)return;const t=o instanceof Lo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{_+=1,r?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${qo(e)}`),A({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}`),A({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),r.handleReq(e)}});R=mo()?new po(d):null,M=new Ro({registry:u,observations:T,actionClient:I,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Po().hasSession(e),onMessages:async(e,n,o,t)=>{await S?.processMessages(e,n,o,t)},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:f,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??g,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:de,dataEpoch:me,advertisedCapabilities:le,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:f,prepareForUpgrade:t=>r.prepareForUpgrade(t),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(d,R,f,x,new bo({registry:u,observations:T,bindingClient:I,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>xe(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),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,t=n.confirmedMajor===!0;return await He(d,`local-upgrade-${Date.now()}`,o,{currentVersion:f,confirmedMajor:t,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:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},L=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new To({registry:u,observations:T,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 t=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Eo(u,e)||!Ao(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o)=>{const t=m().machineToken;if(!t)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:t,activationCauseMessageId:n,allowedActions:o})},audit:new Uo,resolveManagedAgentPolicy:e=>Do({...e,runtime:v.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Se({serverUrl:g,machineToken:m().machineToken,req:e,client:d,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 I.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,n)=>S.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):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(pe,{recursive:!0}),re(process.pid,c,{version:f}),ae(process.pid,void 0,c),d.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(P.gray(`
|
|
3
|
-
Disconnecting...`)),A({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await r.cleanup(),x.stop(),R?.stop(),M?.stop(),d.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 ge=i.command("config").description("View or modify configuration");ge.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${be()}`);const a=m(),s=Ee(a,X(process.env,a));if(a.serverUrl){const p=
|
|
1
|
+
import{registerScheduleCommand as ve}from"./commands/schedule.js";import{resolvePersonalScheduleContext as Ie}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Se}from"./schedules/server-client.js";import{Command as ye}from"commander";import P from"chalk";import we from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as Ce,getConfigPath as be,getShennianDir as ke,resolveShennianPath as H,normalizeAutoUpgradePolicy as Re}from"./config/index.js";import{CliRelayClient as Pe}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Ae,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Te,runSmartStart as De}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as Me,clearDaemonLauncher as oe,findRunningDaemonProcessIds as _e,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as xe,stopPreparedRemoteAccessService as $e,isIsolatedDaemonE2eInstance as Ne,getDaemonStatus as te,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as ae}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{registerWorkerCommand as He}from"./commands/worker.js";import{handleUpgradeStart as Be,registerUpgradeCommand as Fe}from"./commands/upgrade.js";import{registerToolsCommand as Ge}from"./commands/tools.js";import{registerIntegrationCommand as qe}from"./commands/integration.js";import{registerRoomCommand as ze}from"./commands/room.js";import{SessionManager as We}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Je}from"./session/handlers/project-fs.js";import{SERVERS as B,regionToUrl as Ke,urlToRegion as Ye}from"./region.js";import{clearUpgradeAttempt as Qe,getCurrentVersion as ie,handleStartupCrashCheck as Ze,readUpgradeAttempt as Xe}from"./upgrade/engine.js";import{AutoUpgradeScheduler as eo}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as oo}from"./upgrade/updater-bootstrap.js";import{isDesktopDistribution as no}from"./runtime-distribution.js";import{launchUpdater as to,markUpdaterHealthy as ro}from"./upgrade/updater-launcher.js";import{detectAgents as ao}from"./agents/detect.js";import{augmentProcessPath as se}from"./env-path.js";import{acquireDaemonInstanceLock as io}from"./commands/daemon-instance-lock.js";se();const F=ie();import{getCachedAgentInfos as so,resolveAgentInfos as co}from"./agents/model-registry.js";import{initCliLogReporter as lo,reportLog as A,reportUpgradeAggregateMetric as mo}from"./log-reporter.js";import{isNativeFusionEnabled as po}from"./native-fusion/config.js";import{NativeSessionFusionService as go}from"./native-fusion/service.js";import{startDaemonLogRetention as uo}from"./daemon-log.js";import{startDaemonIpcServer as ce}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as fo,resolveManagedRoomIpcPaths as ho}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as vo}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as Io}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as So}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as yo}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as wo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as Co}from"./room/session-registry.js";import{HttpRoomBindingClient as bo}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as ko}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Ro}from"./room/host-session-observations.js";import{RoomMirrorSyncService as Po}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Ao}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as Eo,isCurrentRegistration as To}from"./room/registration-authority.js";import{RoomActivationService as Do,resolveManagedAgentPolicy as Uo}from"./room/activation-service.js";import{RoomActivationAudit as Mo}from"./room/activation-audit.js";import{RoomAttachmentCache as _o}from"./room/attachment-cache.js";import{RoomAttachmentUploader as xo}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as $o}from"./room/managed-context.js";import{createManagedRoomExecutor as No}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as de,SESSION_SYNC_ADVERTISED_CAPABILITIES as le}from"@shennian/wire";import{getDaemonInstallationId as Oo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as Vo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as me,ensurePersonalDataEpoch as Lo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as jo,DeletionSyncTerminalError as Ho}from"./session/deletion-sync-state.js";const pe=ke(),Bo=H("daemon.pid"),Fo=H("daemon.instance.lock");function Go(){try{const a=j.readFileSync(Bo,"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 qo(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 zo(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 ye;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),qe(i),ze(i),ve(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",we.hostname()).action(async a=>{const s=m(),p=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await De(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);se()}catch{}Ae(X(process.env,m()));const p=uo();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=Me(),y=io({lockPath:Fo,instanceId:c});y.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(y.ownerPid?` (PID ${y.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Go();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),y.release(),process.exit(0)}catch{}}catch{}const q=Ne(process.env,pe)?[]:_e(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),y.release(),process.exit(0)),re(process.pid,c,{version:F}),ae(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",y.release);let $=null;try{no()||($=oo())}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Ze();if(E){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{to({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{Lo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const l=m(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??l.serverUrl??B.cn.url,f=ie(),v=await ao({serverUrl:g,authToken:l.machineToken??l.accessToken}),w=v.map(e=>e.type),N=so(v),u=new Co,T=new Ro,C=new $o(u),I=new bo(g),ue=new _o({actionClient:I}),fe=new xo({actionClient:I});let S=null,r;const z=No({authority:C,actionClient:I,attachmentCache:ue,attachmentUploader:fe,observations:T,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 t=u.getByRegistryKey(e)?.binding;t&&u.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await ce({paths:fo(),handlers:{...vo({daemonVersion:f,protocolVersion:wo,getStatus:()=>te(),getAgents:()=>v,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()}}),...Io({getStatus:()=>te(),discover:async()=>{if(!r)throw new Error("daemon_starting");const e=await So(!0);return v.splice(0,v.length,...e.detectedAgents),d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:e.agentList,agents:e.agents}}),e.agents.map(n=>{const o=e.detectedAgents.find(t=>t.type===n.type);return{...n,runtimePlatform:o?.runtimePlatform,runtimeIsWsl:o?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return yo(e,{ignoreNativeSession:(n,o)=>R?.ignoreNativeSource(n,o)})}})}});let k;try{k=await ce({paths:ho(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=Ie(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 W=!1;const D=()=>{W||(W=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",D),process.once("SIGTERM",D),!l.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",D),process.removeListener("SIGTERM",D);const J=`${qo(g)}/relay/machine`,U=l.machineToken;if(!U)return;try{await Vo({serverUrl:g,machineToken:U,machineId:l.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)}l.machineId&&lo(g,l.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${J}... (v${f}) agents: ${w.join(",")}`),A({level:"info",wsEvent:"daemon.start",metadata:{version:f,agents:w}});let R=null,M=null,_=0,h=Xe();const K=Re(l.autoUpgrade),d=new Pe({serverUrl:J,machineToken:U,cliVersion:f,agentList:w,autoUpgradePolicy:K,sessionSyncProtocolVersion:de,sessionSyncCapabilities:le,daemonInstallationId:Oo(),dataEpoch:me,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),A({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),co(v,{serverUrl:l.serverUrl??g,authToken:l.machineToken??l.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),h&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:h.from,to:h.to,...h.jobId?{jobId:h.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),h){const o=h;h=null,o.jobId&&ro(o.jobId,o.to),Qe(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),mo({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.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"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===_&&d.getState()==="connected"&&R?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&d.getState()==="connected"){if(o instanceof Ho)return;const t=o instanceof jo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{_+=1,r?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${zo(e)}`),A({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}`),A({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),r.handleReq(e)}});R=po()?new go(d):null,M=new Po({registry:u,observations:T,actionClient:I,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Ao().hasSession(e),onMessages:async(e,n,o,t)=>{await S?.processMessages(e,n,o,t)},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 eo({currentVersion:f,machineId:l.machineId??"unknown-machine",serverUrl:l.serverUrl??g,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:de,dataEpoch:me,advertisedCapabilities:le,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:f,prepareForUpgrade:t=>r.prepareForUpgrade(t),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(K),r=new We(d,R,f,x,new ko({registry:u,observations:T,bindingClient:I,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>xe(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),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,t=n.confirmedMajor===!0;return await Be(d,`local-upgrade-${Date.now()}`,o,{currentVersion:f,confirmedMajor:t,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:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},L=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new Do({registry:u,observations:T,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 t=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!To(u,e)||!Eo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o)=>{const t=m().machineToken;if(!t)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:t,activationCauseMessageId:n,allowedActions:o})},audit:new Mo,resolveManagedAgentPolicy:e=>Uo({...e,runtime:v.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Se({serverUrl:g,machineToken:m().machineToken,req:e,client:d,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 I.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,n)=>S.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):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(pe,{recursive:!0}),re(process.pid,c,{version:f}),ae(process.pid,void 0,c),d.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(P.gray(`
|
|
3
|
+
Disconnecting...`)),A({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await r.cleanup(),x.stop(),R?.stop(),M?.stop(),d.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 ge=i.command("config").description("View or modify configuration");ge.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${be()}`);const a=m(),s=Ee(a,X(process.env,a));if(a.serverUrl){const p=Ye(a.serverUrl);console.log(`Region: ${p} (${B[p].label})`)}console.log(JSON.stringify(s,null,2))}),ge.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(P.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;p.serverUrl=Ke(c),Ce(p),console.log(P.green(`\u2713 Server set to ${B[c].label}`)),console.log(P.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(P.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Te(i),Ue(i),Oe(i),Ve(i),Le(i),je(i),He(i),Fe(i),Ge(i),i.parse();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \
|
|
1
|
+
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u660E\u786E\u7684\u65B0\u76EE\u6807\u9ED8\u8BA4\u521B\u5EFA\u65B0\u7684 worker\uFF0C\u4E0D\u8981\u4E3A\u4E86\u590D\u7528\u800C\u5148\u679A\u4E3E\u5386\u53F2\uFF0C\u4E5F\u4E0D\u8981\u628A\u4E0D\u540C\u76EE\u6807\u6DF7\u8FDB\u65E7 worker\u3002\n- \u53EA\u6709\u7528\u6237\u660E\u786E\u7EED\u63A5\u3001\u7EA0\u504F\u3001\u8865\u5145\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u6216\u7CFB\u7EDF\u6B63\u5728\u6062\u590D\u5148\u524D\u5B89\u6392\u65F6\uFF0C\u624D\u7528 sessions list \u67E5\u770B\u5F53\u524D Manager \u81EA\u5DF1\u521B\u5EFA\u7684 worker\uFF0C\u5E76\u7528 sessions read \u6838\u5BF9\u540E\u590D\u7528\u3002\u4E0D\u8981\u5C1D\u8BD5\u53D1\u73B0\u6216\u63A7\u5236\u540C\u9879\u76EE\u4E2D\u5C5E\u4E8E\u65E7 Manager\u3001\u5176\u4ED6 Manager \u6216\u666E\u901A\u4F1A\u8BDD\u7684 Session\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u8865\u5145\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u91CD\u590D\u521B\u5EFA\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- \u521B\u5EFA worker \u65F6\u5FC5\u987B\u63D0\u4F9B\u7B80\u77ED\u3001\u5177\u4F53\u3001\u53EF\u533A\u5206\u7684 --title\uFF0C\u540C\u65F6\u628A\u5B8C\u6574\u4EFB\u52A1\u653E\u8FDB --message\uFF1B\u4E0D\u8981\u628A\u6574\u6BB5\u4EFB\u52A1\u6216 Session ID \u5F53\u6807\u9898\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u7531\u5F53\u524D Manager \u521B\u5EFA\u3001\u4E14\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u548C\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684 worker\uFF1B\u4E0D\u8981\u8DE8 Manager\u3001\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5916\u90E8\u6D88\u606F\u662F\u5426\u9700\u8981\u56DE\u590D\u3001\u8FFD\u95EE\u3001\u5FFD\u7565\u3001\u8F6C\u4EA4\u5185\u90E8\u8D1F\u8D23\u4EBA\uFF0C\u6216\u521B\u5EFA/\u6307\u6D3E worker\uFF0C\u7531\u4F60\u6839\u636E\u9879\u76EE\u4E0A\u4E0B\u6587\u663E\u5F0F\u5224\u65AD\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <discover\u8FD4\u56DE\u7684type> --title <\u77ED\u6807\u9898> --message <\u5B8C\u6574\u4EFB\u52A1>\uFF08\u9ED8\u8BA4\u4F7F\u7528\u5F53\u524D Manager \u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF09\n- shennian manager sessions send --session-id <id> --message <text>\n- shennian manager sessions queue list --session-id <id> --json\n- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>\n- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>\n- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09\n- shennian manager sessions read --session-id <id> --limit 200 --json\n- shennian manager sessions wait --session-id <id> --timeout <1-600>\uFF08\u53D7\u7BA1 Manager \u901A\u5E38\u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u5524\u9192\uFF0C\u4E0D\u4E3B\u52A8\u8F6E\u8BE2\uFF09\n- shennian manager memory path\n\n\u5982\u679C\u5BBF\u4E3B\u76F4\u63A5\u63D0\u4F9B\u4E86 shennian_manager_* \u5DE5\u5177\uFF0C\u4F18\u5148\u4F7F\u7528\u8FD9\u4E9B\u5DE5\u5177\u5B8C\u6210\u540C\u540D\u64CD\u4F5C\uFF0C\u4E0D\u8981\u518D\u901A\u8FC7 Shell \u8C03\u7528\u547D\u4EE4\u3002\u9650\u5236\u6743\u9650\u53EA\u7EA6\u675F Provider \u81EA\u8EAB\u7684\u6587\u4EF6\u3001Shell\u3001\u7F51\u7EDC\u548C\u666E\u901A\u5DE5\u5177\u80FD\u529B\uFF0C\u4E0D\u4F1A\u64A4\u9500\u795E\u5FF5\u6CE8\u5165\u7684 Manager Worker \u7BA1\u7406\u80FD\u529B\uFF1B\u4E0D\u5F97\u628A\u201C\u9650\u5236\u6743\u9650\u201D\u8BEF\u89E3\u4E3A\u4E0D\u80FD\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u505C\u6B62 Worker\u3002\n\n\u53EF\u521B\u5EFA\u7684 worker Agent \u4EE5\u5F53\u524D daemon discovery \u8FD4\u56DE\u4E14 supportsWorker=true \u7684 Adapter \u4E3A\u51C6\uFF0C\u5305\u62EC\u901A\u8FC7 Agent Protocol \u63A5\u5165\u5E76\u901A\u8FC7\u5951\u7EA6\u7684 Runtime\uFF1B\u4E0D\u8981\u7EF4\u62A4\u6216\u731C\u6D4B Provider \u540D\u79F0\u767D\u540D\u5355\u3002sessions send \u59CB\u7EC8\u5148\u628A worker \u6D88\u606F\u4EA4\u7ED9\u672C\u673A durable queue\uFF1Aworker \u6B63\u5FD9\u65F6\u7B49\u5F85\uFF0C\u7A7A\u95F2\u65F6\u7ACB\u5373\u7531\u540C\u4E00\u8C03\u5EA6\u5668\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\n\n\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002";
|
|
2
2
|
export declare function buildManagerPrompt(userText: string): string;
|
|
@@ -13,9 +13,9 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
|
|
|
13
13
|
- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002
|
|
14
14
|
- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002
|
|
15
15
|
- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002
|
|
16
|
-
- \
|
|
17
|
-
- \
|
|
18
|
-
- \
|
|
16
|
+
- \u660E\u786E\u7684\u65B0\u76EE\u6807\u9ED8\u8BA4\u521B\u5EFA\u65B0\u7684 worker\uFF0C\u4E0D\u8981\u4E3A\u4E86\u590D\u7528\u800C\u5148\u679A\u4E3E\u5386\u53F2\uFF0C\u4E5F\u4E0D\u8981\u628A\u4E0D\u540C\u76EE\u6807\u6DF7\u8FDB\u65E7 worker\u3002
|
|
17
|
+
- \u53EA\u6709\u7528\u6237\u660E\u786E\u7EED\u63A5\u3001\u7EA0\u504F\u3001\u8865\u5145\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u6216\u7CFB\u7EDF\u6B63\u5728\u6062\u590D\u5148\u524D\u5B89\u6392\u65F6\uFF0C\u624D\u7528 sessions list \u67E5\u770B\u5F53\u524D Manager \u81EA\u5DF1\u521B\u5EFA\u7684 worker\uFF0C\u5E76\u7528 sessions read \u6838\u5BF9\u540E\u590D\u7528\u3002\u4E0D\u8981\u5C1D\u8BD5\u53D1\u73B0\u6216\u63A7\u5236\u540C\u9879\u76EE\u4E2D\u5C5E\u4E8E\u65E7 Manager\u3001\u5176\u4ED6 Manager \u6216\u666E\u901A\u4F1A\u8BDD\u7684 Session\u3002
|
|
18
|
+
- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u8865\u5145\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u91CD\u590D\u521B\u5EFA\u3002
|
|
19
19
|
- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002
|
|
20
20
|
- \u521B\u5EFA worker \u65F6\u5FC5\u987B\u63D0\u4F9B\u7B80\u77ED\u3001\u5177\u4F53\u3001\u53EF\u533A\u5206\u7684 --title\uFF0C\u540C\u65F6\u628A\u5B8C\u6574\u4EFB\u52A1\u653E\u8FDB --message\uFF1B\u4E0D\u8981\u628A\u6574\u6BB5\u4EFB\u52A1\u6216 Session ID \u5F53\u6807\u9898\u3002
|
|
21
21
|
- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002
|
|
@@ -29,18 +29,18 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
|
|
|
29
29
|
|
|
30
30
|
\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A
|
|
31
31
|
- shennian manager sessions list --json
|
|
32
|
-
- shennian manager sessions start --agent <
|
|
32
|
+
- shennian manager sessions start --agent <discover\u8FD4\u56DE\u7684type> --title <\u77ED\u6807\u9898> --message <\u5B8C\u6574\u4EFB\u52A1>\uFF08\u9ED8\u8BA4\u4F7F\u7528\u5F53\u524D Manager \u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF09
|
|
33
33
|
- shennian manager sessions send --session-id <id> --message <text>
|
|
34
34
|
- shennian manager sessions queue list --session-id <id> --json
|
|
35
35
|
- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>
|
|
36
36
|
- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>
|
|
37
37
|
- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09
|
|
38
38
|
- shennian manager sessions read --session-id <id> --limit 200 --json
|
|
39
|
-
- shennian manager sessions wait --session-id <id> --timeout <1-600>\uFF08\
|
|
39
|
+
- shennian manager sessions wait --session-id <id> --timeout <1-600>\uFF08\u53D7\u7BA1 Manager \u901A\u5E38\u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u5524\u9192\uFF0C\u4E0D\u4E3B\u52A8\u8F6E\u8BE2\uFF09
|
|
40
40
|
- shennian manager memory path
|
|
41
41
|
|
|
42
42
|
\u5982\u679C\u5BBF\u4E3B\u76F4\u63A5\u63D0\u4F9B\u4E86 shennian_manager_* \u5DE5\u5177\uFF0C\u4F18\u5148\u4F7F\u7528\u8FD9\u4E9B\u5DE5\u5177\u5B8C\u6210\u540C\u540D\u64CD\u4F5C\uFF0C\u4E0D\u8981\u518D\u901A\u8FC7 Shell \u8C03\u7528\u547D\u4EE4\u3002\u9650\u5236\u6743\u9650\u53EA\u7EA6\u675F Provider \u81EA\u8EAB\u7684\u6587\u4EF6\u3001Shell\u3001\u7F51\u7EDC\u548C\u666E\u901A\u5DE5\u5177\u80FD\u529B\uFF0C\u4E0D\u4F1A\u64A4\u9500\u795E\u5FF5\u6CE8\u5165\u7684 Manager Worker \u7BA1\u7406\u80FD\u529B\uFF1B\u4E0D\u5F97\u628A\u201C\u9650\u5236\u6743\u9650\u201D\u8BEF\u89E3\u4E3A\u4E0D\u80FD\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u505C\u6B62 Worker\u3002
|
|
43
43
|
|
|
44
|
-
\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC
|
|
44
|
+
\u53EF\u521B\u5EFA\u7684 worker Agent \u4EE5\u5F53\u524D daemon discovery \u8FD4\u56DE\u4E14 supportsWorker=true \u7684 Adapter \u4E3A\u51C6\uFF0C\u5305\u62EC\u901A\u8FC7 Agent Protocol \u63A5\u5165\u5E76\u901A\u8FC7\u5951\u7EA6\u7684 Runtime\uFF1B\u4E0D\u8981\u7EF4\u62A4\u6216\u731C\u6D4B Provider \u540D\u79F0\u767D\u540D\u5355\u3002sessions send \u59CB\u7EC8\u5148\u628A worker \u6D88\u606F\u4EA4\u7ED9\u672C\u673A durable queue\uFF1Aworker \u6B63\u5FD9\u65F6\u7B49\u5F85\uFF0C\u7A7A\u95F2\u65F6\u7ACB\u5373\u7531\u540C\u4E00\u8C03\u5EA6\u5668\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002
|
|
45
45
|
|
|
46
46
|
\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002`;function n(e){return e}export{s as MANAGER_SYSTEM_PROMPT,n as buildManagerPrompt};
|
|
@@ -45,7 +45,7 @@ export type ManagerRecord = {
|
|
|
45
45
|
/** This Manager Session originated from a managed Room and every synthetic
|
|
46
46
|
* continuation must reacquire that Room's current trusted context. */
|
|
47
47
|
managedRoomBound?: boolean;
|
|
48
|
-
controllerKind?: 'managed_session' | '
|
|
48
|
+
controllerKind?: 'managed_session' | 'ephemeral';
|
|
49
49
|
status: ManagerStatus;
|
|
50
50
|
/** Event ids currently being transferred. Queue durable ACK ends inbox
|
|
51
51
|
* ownership; Provider start is intentionally not a second acknowledgement. */
|
|
@@ -55,6 +55,26 @@ export type ManagerRecord = {
|
|
|
55
55
|
createdAt: string;
|
|
56
56
|
updatedAt: string;
|
|
57
57
|
};
|
|
58
|
+
export type EphemeralWorkerAction = 'read' | 'send' | 'wait' | 'stop';
|
|
59
|
+
export type EphemeralWorkerGrant = {
|
|
60
|
+
refDigest: string;
|
|
61
|
+
managerSessionId: string;
|
|
62
|
+
workerSessionId: string;
|
|
63
|
+
workDir: string;
|
|
64
|
+
machineId: string;
|
|
65
|
+
accountNamespaceId: string | null;
|
|
66
|
+
actions: EphemeralWorkerAction[];
|
|
67
|
+
createdAt: string;
|
|
68
|
+
expiresAt: string;
|
|
69
|
+
revokedAt?: string | null;
|
|
70
|
+
};
|
|
71
|
+
export type WorkerRefResolution = {
|
|
72
|
+
ok: true;
|
|
73
|
+
grant: EphemeralWorkerGrant;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
reason: 'invalid' | 'expired' | 'revoked';
|
|
77
|
+
};
|
|
58
78
|
export type ManagerInboxEvent = {
|
|
59
79
|
id: string;
|
|
60
80
|
managerSessionId: string;
|
|
@@ -83,6 +103,7 @@ type RegistryFile = {
|
|
|
83
103
|
workers: Record<string, ManagedWorkerRecord>;
|
|
84
104
|
replyTargets: Record<string, ReplyTargetRecord>;
|
|
85
105
|
inbox: Record<string, ManagerInboxEvent[]>;
|
|
106
|
+
workerGrants: Record<string, EphemeralWorkerGrant>;
|
|
86
107
|
};
|
|
87
108
|
export declare class ManagerRegistry {
|
|
88
109
|
load(): RegistryFile;
|
|
@@ -98,11 +119,36 @@ export declare class ManagerRegistry {
|
|
|
98
119
|
defaultWorkerAgentType?: AgentType | null;
|
|
99
120
|
defaultWorkerModelId?: string | null;
|
|
100
121
|
managedRoomBound?: boolean;
|
|
101
|
-
controllerKind?: 'managed_session' | '
|
|
122
|
+
controllerKind?: 'managed_session' | 'ephemeral';
|
|
102
123
|
status?: ManagerStatus;
|
|
103
124
|
deliveringInboxEventIds?: string[];
|
|
104
125
|
}): ManagerRecord;
|
|
105
126
|
getManager(sessionId: string): ManagerRecord | undefined;
|
|
127
|
+
issueWorkerRef(input: {
|
|
128
|
+
managerSessionId: string;
|
|
129
|
+
workerSessionId: string;
|
|
130
|
+
workDir: string;
|
|
131
|
+
machineId: string;
|
|
132
|
+
accountNamespaceId?: string | null;
|
|
133
|
+
actions?: EphemeralWorkerAction[];
|
|
134
|
+
ttlMs?: number;
|
|
135
|
+
}): {
|
|
136
|
+
workerRef: string;
|
|
137
|
+
grant: EphemeralWorkerGrant;
|
|
138
|
+
};
|
|
139
|
+
resolveWorkerRef(input: {
|
|
140
|
+
workerRef: string;
|
|
141
|
+
action: EphemeralWorkerAction;
|
|
142
|
+
machineId: string;
|
|
143
|
+
accountNamespaceId?: string | null;
|
|
144
|
+
}): EphemeralWorkerGrant | undefined;
|
|
145
|
+
resolveWorkerRefDetailed(input: {
|
|
146
|
+
workerRef: string;
|
|
147
|
+
action: EphemeralWorkerAction;
|
|
148
|
+
machineId: string;
|
|
149
|
+
accountNamespaceId?: string | null;
|
|
150
|
+
}): WorkerRefResolution;
|
|
151
|
+
revokeWorkerRefsForWorker(workerSessionId: string): void;
|
|
106
152
|
enqueueInboxEvent(input: Omit<ManagerInboxEvent, 'id' | 'createdAt' | 'updatedAt'>): ManagerInboxEvent | null;
|
|
107
153
|
listInboxEvents(managerSessionId: string): ManagerInboxEvent[];
|
|
108
154
|
removeInboxEvents(managerSessionId: string, eventIds: readonly string[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import g from"node:fs";import h from"node:path";import{createHash as w,randomBytes as v,randomUUID as u}from"node:crypto";import{isAgentHiddenPayload as A,sessionPreviewFromPayload as S}from"@shennian/wire";import{resolveShennianPath as p}from"../config/index.js";import{readMessages as T}from"../session/store.js";const m=p("manager-registry.json");function d(){return new Date().toISOString()}function b(){return{managers:{},workers:{},replyTargets:{},inbox:{},workerGrants:{}}}function k(a){return w("sha256").update(a).digest("hex")}function I(a,e){try{g.chmodSync(a,e)}catch(s){if(!(s instanceof Error&&"code"in s&&s.code==="ENOENT"))throw s}}class E{load(){try{const e=JSON.parse(g.readFileSync(m,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{},workerGrants:e.workerGrants??{}}}catch{return b()}}save(e){const s=h.dirname(m);for(let r=0;r<3;r+=1)try{g.mkdirSync(s,{recursive:!0,mode:448}),process.platform!=="win32"&&I(s,448),g.writeFileSync(m,JSON.stringify(e,null,2),{mode:384}),process.platform!=="win32"&&I(m,384);return}catch(o){if(!(o instanceof Error&&"code"in o&&o.code==="ENOENT")||r===2)throw o}}upsertManager(e){const s=this.load(),r=s.managers[e.sessionId],o=d(),t={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??o,updatedAt:o};return s.managers[e.sessionId]=t,this.save(s),t}getManager(e){return this.load().managers[e]}issueWorkerRef(e){const s=this.load(),r=s.managers[e.managerSessionId],o=s.workers[e.workerSessionId];if(!r||r.controllerKind!=="ephemeral")throw new Error("ephemeral_controller_required");if(!o||o.managedBy!==r.sessionId||o.workDir!==e.workDir)throw new Error("worker_scope_mismatch");const t=`wr_${v(32).toString("base64url")}`,n=k(t),i=d(),l={refDigest:n,managerSessionId:r.sessionId,workerSessionId:o.sessionId,workDir:e.workDir,machineId:e.machineId,accountNamespaceId:e.accountNamespaceId??null,actions:e.actions??["read","send","wait","stop"],createdAt:i,expiresAt:new Date(Date.now()+(e.ttlMs??720*60*6e4)).toISOString(),revokedAt:null};return s.workerGrants[n]=l,this.save(s),{workerRef:t,grant:l}}resolveWorkerRef(e){const s=this.resolveWorkerRefDetailed(e);return s.ok?s.grant:void 0}resolveWorkerRefDetailed(e){if(!/^wr_[A-Za-z0-9_-]{40,}$/.test(e.workerRef))return{ok:!1,reason:"invalid"};const s=this.load(),r=s.workerGrants[k(e.workerRef)];if(!r)return{ok:!1,reason:"invalid"};if(r.revokedAt)return{ok:!1,reason:"revoked"};if(Date.parse(r.expiresAt)<=Date.now())return{ok:!1,reason:"expired"};if(r.machineId!==e.machineId)return{ok:!1,reason:"invalid"};if(r.accountNamespaceId!==(e.accountNamespaceId??null))return{ok:!1,reason:"invalid"};if(!r.actions.includes(e.action))return{ok:!1,reason:"invalid"};const o=s.managers[r.managerSessionId],t=s.workers[r.workerSessionId];return!o||o.controllerKind!=="ephemeral"?{ok:!1,reason:"revoked"}:!t||t.managedBy!==o.sessionId||t.workDir!==r.workDir?{ok:!1,reason:"revoked"}:{ok:!0,grant:r}}revokeWorkerRefsForWorker(e){const s=this.load(),r=d();let o=!1;for(const t of Object.values(s.workerGrants))t.workerSessionId!==e||t.revokedAt||(t.revokedAt=r,o=!0);o&&this.save(s)}enqueueInboxEvent(e){const s=this.load(),r=s.managers[e.managerSessionId],o=s.workers[e.workerSessionId];if(!r||!o||o.managedBy!==r.sessionId||o.workDir!==r.workDir)return null;const t=d(),n=s.inbox[e.managerSessionId]??[],i=n.findIndex(c=>c.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?c.kind==="worker.health":c.kind===e.kind&&c.runId===e.runId)),l=i>=0?{...n[i],...e,updatedAt:t}:{...e,id:`mie_${u()}`,createdAt:t,updatedAt:t};return i>=0?n[i]=l:n.push(l),s.inbox[e.managerSessionId]=n,this.save(s),l}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(),o=new Set(s);r.inbox[e]=(r.inbox[e]??[]).filter(t=>!o.has(t.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 o=e.sessionId??`sess_worker_${u()}`,t=d(),n={sessionId:o,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:t,updatedAt:t,lastActivityAt:t,title:e.title??null,summary:e.summary??null,agentSessionId:null,runId:null};return s.workers[o]=n,r.managedWorkerSessionIds.includes(o)||r.managedWorkerSessionIds.push(o),r.updatedAt=t,this.save(s),n}updateWorker(e,s){const r=this.load(),o=r.workers[e];if(!o)return;const t={...o,...s,updatedAt:d()};return r.workers[e]=t,this.save(r),t}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 o of r)o.roomOrigin&&(delete o.roomOrigin,s=!0);s&&this.save(e)}listWorkers(e,s={}){const r=this.load(),o=r.managers[e];return o?Object.values(r.workers).filter(t=>t.managedBy===o.sessionId).filter(t=>t.workDir===o.workDir).filter(t=>s.includeManagers||t.agentType!=="manager").map(t=>this.decorateManagedWorker(t,s.runningSessionIds)).sort((t,n)=>n.lastActivityAt.localeCompare(t.lastActivityAt)):[]}getWorkerForManager(e,s){const r=this.load(),o=r.managers[e],t=r.workers[s];if(o&&t)return t.managedBy!==o.sessionId||t.workDir!==o.workDir||t.agentType==="manager"?void 0:this.decorateManagedWorker(t)}retireManager(e){const s=this.load(),r=Object.values(s.workers).filter(t=>t.managedBy===e),o=d();for(const t of Object.values(s.workerGrants))t.managerSessionId===e&&!t.revokedAt&&(t.revokedAt=o);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(),o=r.workers[s];if(!o||o.managedBy!==e)return;const t=d();for(const n of Object.values(r.workerGrants))n.workerSessionId===s&&!n.revokedAt&&(n.revokedAt=t);delete r.workers[s],this.save(r)}decorateManagedWorker(e,s){const r=s?.has(e.sessionId)?"running":e.status,o=this.readTranscriptSummary(e.sessionId,e.summary??null),t=o.lastMessagePreview??e.lastMessagePreview??e.summary??null,n=e.userGoal??o.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??n??t??e.summary??e.sessionId,userGoal:n,lastMessagePreview:t,summary:e.summary??t,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}readTranscriptSummary(e,s){const r=T(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:s};const o=[...r].sort((i,l)=>i.ts-l.ts),t=o.find(i=>i.role==="user"),n=[...o].reverse().find(i=>f(i));return{userGoal:t?y(f(t),160):null,lastMessagePreview:y(f(n),180)??s}}createReplyTarget(e){const s=this.load(),r={replyTarget:`rt_${u()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:d()};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 f(a){return!a||A(a.payload)?null:S(a.payload,180)}function y(a,e){return a?a.length>e?`${a.slice(0,e)}...`:a:null}export{E as ManagerRegistry};
|
|
@@ -50,12 +50,12 @@ export declare class ManagerRuntimeService {
|
|
|
50
50
|
defaultWorkerAgentType?: AgentType | null;
|
|
51
51
|
defaultWorkerModelId?: string | null;
|
|
52
52
|
managedRoomBound?: boolean;
|
|
53
|
-
controllerKind?: 'managed_session' | '
|
|
53
|
+
controllerKind?: 'managed_session' | 'ephemeral';
|
|
54
54
|
status?: 'idle' | 'running' | 'interrupting';
|
|
55
55
|
}): void;
|
|
56
56
|
setManagedAgentPolicy(sessionId: string, policy: CompiledManagedAgentPolicy | null): void;
|
|
57
57
|
getManagedAgentPolicyForWorker(managerSessionId: string, workDir: string): CompiledManagedAgentPolicy | null;
|
|
58
|
-
|
|
58
|
+
isExternalWorkerController(managerSessionId: string, workDir: string): boolean;
|
|
59
59
|
isManagedWorkerSession(sessionId: string): boolean;
|
|
60
60
|
isManagedRoomBoundManager(sessionId: string): boolean;
|
|
61
61
|
retireManagerAndWorkers(managerSessionId: string): Promise<string[]>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import N from"node:http";import{createHash as _,randomBytes as T,randomUUID as k}from"node:crypto";import
|
|
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
|
|
1
|
+
import N from"node:http";import{createHash as _,randomBytes as T,randomUUID as k}from"node:crypto";import p from"node:fs";import v 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{EphemeralWorkerError as B,handleEphemeralWorkerIpcRoute as H,handleWorkerIpcRoute as A}from"./worker-ipc.js";import{deriveWorkerTitle as $,formatWorkerMarkdown as U}from"./worker-markdown.js";import{markTrustedManagerEvent as S}from"../session/manager-event-authority.js";const j=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),y=12*6e4;let b=null;function ge(u){b=u}function le(){return b}function f(u){return M.resolve(u||v.homedir())}function m(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 G(u){return u==="worker.final"?"completed":u==="worker.error"?"failed":u==="worker.aborted"?"stopped":"progress"}function F(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 J(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>j)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function K(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 V()?M.join(v.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function V(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class he{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();p.mkdirSync(M.dirname(e),{recursive:!0}),p.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),p.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=x(),t=JSON.parse(p.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&p.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:f(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:f(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||f(t)!==r.workDir?null:n}isExternalWorkerController(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="ephemeral"&&f(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=[],a=async(s,g)=>{const i=[];try{r.runJournal?.markSessionAborted(s)}catch(l){i.push(l)}try{r.recoveryBarrier?.end(s)}catch(l){i.push(l)}try{r.chatQueue?.purgeSession(s)}catch(l){i.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){i.push(l)}try{await o.adapter.stop(),c=!0}catch(l){i.push(l)}}if(o&&c&&i.length===0&&(r.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&i.length===0)try{g?.()}catch(l){i.push(l),n.push(l)}else n.push(...i)};await a(e);for(const s of t)await a(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 a=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:f(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:a?.agentType,providerModelId:a?.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 i=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),i&&(n.runRoomOrigins={...r.runRoomOrigins??{},[t.runId]:i})}const a=`${e}:${t.runId}`;let s=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const i=(this.workerTextAcc.get(a)??"")+t.text;this.workerTextAcc.set(a,i);const o=R(i);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 i=R(this.workerTextAcc.get(a)??"");if(i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i,s=t.state==="final"?i:t.message||i),this.workerTextAcc.delete(a),r.runRoomOrigins?.[t.runId]){const o={...r.runRoomOrigins};delete o[t.runId],n.runRoomOrigins=o}}else t.state==="start"&&(n.status="running");const g=this.registry.updateWorker(e,n);g&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(g.managedBy,g,t.state,t.runId,s,r.runRoomOrigins?.[t.runId]??null)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){K(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 a={registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},s=await H(a,r.pathname,n)??await A(a,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){if(r instanceof B){m(t,r.status,{ok:!1,error:r.code,message:r.message,nextAction:r.nextAction});return}m(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r,n){const a={"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)},a[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,a,s,g,i){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:a,modelId:s,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:i}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind==="managed_session"&&(O(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,a,s){const g=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:a||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),i=this.registry.getManager(e);g&&i?.controllerKind==="managed_session"&&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 a=e-Date.parse(n.createdAt);if(a<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),g=r.sessions.get(n.sessionId)?.currentRunId,i=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!g){if(!i&&s>=10*6e4){const E="\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:E});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,E)}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)),d=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),h=`\u5DF2\u8FD0\u884C ${Math.floor(a/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${d||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
|
|
6
6
|
- \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
|
|
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
|
|
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==="managed_session"&&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(d=>d.priority==="high"),a=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||a?.currentRunId&&!n)return;const g=(n??r[0]).roomOrigin??null,i=JSON.stringify(g),o=n?[n]:r.filter(d=>JSON.stringify(d.roomOrigin??null)===i).slice(0,20),c=o.map(d=>{const h=this.registry.getWorkerForManager(e,d.workerSessionId);return U({kind:G(d.kind),sessionId:d.workerSessionId,title:h?.title||$(h?.userGoal||h?.summary||d.summary),body:d.summary})}).join(`
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
`),l=
|
|
11
|
+
`),l=F(e,o);try{n&&a?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:o.map(h=>h.id)}),await this.interruptAndResumeManager(t,c,l,g)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:o.map(h=>h.id)}),await this.dispatchManagerContinuation(t,c,!0,l,g)),this.registry.removeInboxEvents(e,o.map(h=>h.id));const d=this.registry.getManager(e);d&&this.registry.upsertManager({...d,deliveringInboxEventIds:[]})}catch(d){const h=this.registry.getManager(e);h&&this.registry.upsertManager({...h,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${d instanceof Error?d.message:String(d)}`)}}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 a=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,a),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n,a){const s=e.agentType??"manager",g=s==="manager"?e.modelId:e.providerModelId??void 0,i={...w(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:g,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n,...a?{managerEventRoomOrigin:a}:{}};if((await this.opts.dispatchReq(S({type:"req",id:`manager-${r?"enqueue":"send"}-${k()}`,method:"chat.enqueue",params:i})))?.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{he as ManagerRuntimeService,le as getManagerRuntimeService,ge as setManagerRuntimeService};
|
|
@@ -13,7 +13,14 @@ type WorkerIpcHost = {
|
|
|
13
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 class EphemeralWorkerError extends Error {
|
|
17
|
+
readonly code: string;
|
|
18
|
+
readonly nextAction: string;
|
|
19
|
+
readonly status: number;
|
|
20
|
+
constructor(code: string, message: string, nextAction: string, status?: number);
|
|
21
|
+
}
|
|
16
22
|
export declare function handleWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>, headerManagerSessionId: string, trustedOrigin?: {
|
|
17
23
|
roomOrigin?: ManagedWorkerRoomOrigin;
|
|
18
24
|
}): Promise<WorkerIpcResponse | null>;
|
|
25
|
+
export declare function handleEphemeralWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>): Promise<WorkerIpcResponse | null>;
|
|
19
26
|
export {};
|
|
@@ -1 +1 @@
|
|
|
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
|
+
import I from"node:fs";import q from"node:path";import{randomUUID as W}from"node:crypto";import{extractPayloadText as $,isAvailableAgentType as P,isAgentHiddenPayload as C,isToolPayload as U}from"@shennian/wire";import{loadConfig as O}from"../config/index.js";import{detectAgents as j}from"../agents/detect.js";import{getCachedAgentInfos as N}from"../agents/model-registry/service.js";import{getPersonalSyncIdentity as T}from"../personal-sync/epoch.js";import{readMessages as G,recordSession as K}from"../session/store.js";import{deriveWorkerTitle as p,formatWorkerMarkdown as h,normalizeWorkerTitle as L}from"./worker-markdown.js";class g extends Error{code;nextAction;status;constructor(a,r,i,w=400){super(r),this.code=a,this.nextAction=i,this.status=w}}function x(e){return q.resolve(e||process.cwd())}function B(e){const a=x(e);let r;try{r=I.statSync(a)}catch{throw new g("worker_workdir_invalid","The Worker working directory does not exist.","Choose an existing local project directory and retry worker start.")}if(!r.isDirectory())throw new g("worker_workdir_invalid","The Worker working directory is not a directory.","Choose an existing local project directory and retry worker start.");const i=I.realpathSync.native(a);try{I.accessSync(i,I.constants.R_OK|I.constants.X_OK)}catch{throw new g("worker_workdir_inaccessible","The Worker working directory is not readable and searchable by this user.","Choose a project directory this Shennian daemon user can access and retry worker start.")}return i}function M(e){return P(e)&&e!=="manager"&&e!=="pi"&&e!=="openclaw"}function z(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function F(e,a){const r=[];for(const i of e)if(!C(i.payload))if(U(i.payload)){let w="tool",u="completed";try{const c=JSON.parse(i.payload);w=c.name||w,u=c.status||u}catch{}r.push({...i,payload:`[\u5DE5\u5177] ${w} \xB7 ${u}`})}else r.push({...i,payload:$(i.payload)});return r.slice(-Math.max(1,Math.min(a,500)))}async function b(e,a,r,i,w){if(!a.startsWith("/sessions/")&&a!=="/memory/path")return null;const u=String(r.managerSessionId||i||"");if(!u)throw new Error("managerSessionId is required");const c=e.registry.getManager(u);if(!c)throw new Error("Manager runtime is not registered");if(a==="/sessions/list"){const t=new Set([...e.getRuntime().sessions.entries()].filter(([,d])=>!!d.currentRunId).map(([d])=>d));return{status:200,body:{ok:!0,sessions:e.registry.listWorkers(u,{runningSessionIds:t})}}}if(a==="/memory/path")return{status:200,body:{ok:!0,path:q.join(c.workDir,".shennian")}};if(a==="/sessions/start"){const t=String(r.agentType||r.agent||c.defaultWorkerAgentType||"codex");if(!M(t))throw new Error(`Unsupported manager worker agent: ${t}`);const o=x(String(r.workDir||c.workDir));if(o!==c.workDir)throw new Error("Manager can only start workers in the same workDir");const d=String(r.message||"");if(!d)throw new Error("message is required");const m=r.title?L(String(r.title)):p(d),n=e.registry.addWorker({managerSessionId:u,agentType:t,workDir:o,title:m,summary:d.slice(0,120)}),k=String(r.modelId||(t===c.defaultWorkerAgentType?c.defaultWorkerModelId??"":""));K({sessionId:n.sessionId,agentType:t,workDir:o,agentSessionId:null,modelId:k||null,title:m,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(n.sessionId,t,o,d,null,k||void 0,u,w?.roomOrigin);const f=h({kind:"created",sessionId:n.sessionId,title:m,body:d});return e.appendManagerVisibleMarkdown(u,f,`worker-created:${n.sessionId}`),{status:200,body:{ok:!0,session:n,markdown:f}}}const l=String(r.sessionId||""),s=e.registry.getWorkerForManager(u,l);if(!s||s.role!=="managed_worker"||s.managedBy!==u)throw new Error("Worker not found in this manager scope");if(a==="/sessions/send"){const t=String(r.message||"");if(!t.trim())throw new Error("message is required");const o=r.enqueue===void 0?!0:!!r.enqueue,d=String(r.modelId||(s.agentType===c.defaultWorkerAgentType?c.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(s.sessionId,s.agentType,s.workDir,t,s.agentSessionId??null,d||void 0,u,w?.roomOrigin);const m=h({kind:"message",sessionId:l,title:s.title||p(s.userGoal||s.summary||t),body:t});return e.appendManagerVisibleMarkdown(u,m,`worker-message:${W()}`),{status:200,body:{ok:!0,markdown:m,queued:!0,directRequested:!o}}}if(a==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(l)}};if(a==="/sessions/queue/edit"||a==="/sessions/queue/delete"){const t=a.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${t?"edit":"delete"}-${W()}`,method:t?"chat.queue.edit":"chat.queue.delete",params:{...T(),sessionId:l,queueMessageId:String(r.queueMessageId||r.messageId||""),...t?{text:String(r.message||r.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(l)}}}if(a==="/sessions/stop"||a==="/sessions/terminate"){if(s.status==="final"||s.status==="error"||s.status==="aborted"){const t=s.status==="final"?"completed":s.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,status:s.status,markdown:h({kind:t,sessionId:l,title:s.title||p(s.userGoal||s.summary||""),body:s.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${W()}`,method:"chat.abort",params:{...T(),sessionId:l}}),e.registry.updateWorker(l,{status:"aborted"}),{status:200,body:{ok:!0,markdown:h({kind:"stopped",sessionId:l,title:s.title||p(s.userGoal||s.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(a==="/sessions/read"){const t=Number(r.limit||200),o=G(l,{limit:Math.max(t*20,t)});return{status:200,body:{ok:!0,messages:F(o,t),rawMessageCount:o.length}}}if(a==="/sessions/wait"){const t=Math.max(1e3,Math.min(Number(r.timeoutMs||6e5),6e5)),o=Date.now()+t;let d;for(;Date.now()<o&&(d=e.registry.listInboxEvents(u).find(n=>n.workerSessionId===l),!d);)await new Promise(n=>setTimeout(n,1e3));if(!d){const n=e.registry.getWorkerForManager(u,l);if(n&&(n.status==="final"||n.status==="error"||n.status==="aborted")){const k=n.status==="final"?"completed":n.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,timedOut:!1,alreadyTerminal:!0,status:n.status,markdown:h({kind:k,sessionId:l,title:n.title||p(n.userGoal||n.summary||""),body:n.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return{status:200,body:{ok:!0,timedOut:!0,code:"wait_timed_out",status:n?.status??"running",nextAction:"Call worker read or worker wait again later with the same workerRef."}}}const m=h({kind:z(d.kind),sessionId:l,title:s.title||p(s.userGoal||s.summary||d.summary),body:d.summary});return e.registry.removeInboxEvents(u,[d.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:m,event:d}}}return null}const H={"/workers/read":{action:"read",managerPath:"/sessions/read"},"/workers/send":{action:"send",managerPath:"/sessions/send"},"/workers/wait":{action:"wait",managerPath:"/sessions/wait"},"/workers/stop":{action:"stop",managerPath:"/sessions/stop"}};async function oe(e,a,r){if(!a.startsWith("/workers/"))return null;const i=O();if(!i.machineId||!i.machineToken&&!i.accessToken)throw new g("auth_required","Shennian CLI must be paired and logged in.","Pair this installation with shennian pair, then retry the same worker command.",401);if(a==="/workers/start"){const t=String(r.message||"").trim();if(!t)throw new g("worker_message_required","A Worker task message is required.","Provide --message or --message-file and retry worker start.");const o=String(r.agentType||r.agent||"");if(!o||!M(o))throw new g("runtime_unavailable",`Unsupported Worker Agent: ${o||"(empty)"}`,"Run shennian agent discover --compact --json and select a Worker-capable Agent type.");const d=await j({serverUrl:i.serverUrl,authToken:i.machineToken??i.accessToken}),m=N(d).find(y=>y.type===o);if(!m||m.supportsExternalWorkerTarget!==!0)throw new g("runtime_unavailable",`Worker Agent is not available on this machine: ${o}`,"Run shennian agent discover --compact --json and select a discovered Agent with supportsExternalWorkerTarget=true.");if(m.authState==="required"||m.authState==="error")throw new g("auth_required",`Worker Agent requires provider sign-in: ${o}`,"Sign in with the provider\u2019s own CLI on this computer, refresh discovery, and retry worker start.");const n=typeof r.modelId=="string"?r.modelId.trim():"";if(n&&m.models.length>0&&!m.models.some(y=>y.id===n))throw new g("model_unavailable",`The selected model was not returned by discovery for ${o}.`,`Run shennian agent discover --agent ${o} --json and choose a returned model id.`);const k=B(String(r.workDir||process.cwd())),f=`ephemeral_controller_${W()}`;e.registry.upsertManager({sessionId:f,agentSessionId:null,workDir:k,machineId:i.machineId,modelId:"manager",agentType:"manager",controllerKind:"ephemeral",status:"idle"});let v;try{v=await b(e,"/sessions/start",{managerSessionId:f,agentType:o,workDir:k,title:r.title,modelId:n||void 0,message:t},"")}catch(y){const A=e.registry.retireManager(f);for(const D of A)e.registry.completeRetiredWorker(f,D.sessionId);throw y}const S=v.body.session,_=typeof S?.sessionId=="string"?S.sessionId:"";if(!_)throw new Error("worker_start_invalid_response");const{workerRef:E,grant:R}=e.registry.issueWorkerRef({managerSessionId:f,workerSessionId:_,workDir:k,machineId:i.machineId,accountNamespaceId:i.accountNamespaceId??null});return{status:200,body:{ok:!0,workerId:_,workerRef:E,resolvedWorkDir:k,agentType:o,status:typeof S?.status=="string"?S.status:"running",expiresAt:R.expiresAt}}}const w=H[a];if(!w)throw new g("worker_command_unavailable","Unknown external Worker command.","Use worker start, read, send, wait, or stop. External Worker history cannot be listed.",404);const u=String(r.workerRef||r.ref||""),c=e.registry.resolveWorkerRefDetailed({workerRef:u,action:w.action,machineId:i.machineId,accountNamespaceId:i.accountNamespaceId??null});if(!c.ok){const t=c.reason==="expired"?"worker_ref_expired":c.reason==="revoked"?"worker_ref_revoked":"worker_ref_invalid";throw new g(t,`The Worker reference is ${c.reason} or belongs to another local scope.`,c.reason==="invalid"?"Use the exact workerRef returned by worker start. Lost references cannot be rediscovered.":"Start a new Worker if the task still needs to continue.",403)}const l=c.grant;let s;try{s=await b(e,w.managerPath,{...r,managerSessionId:l.managerSessionId,sessionId:l.workerSessionId},"")}catch(t){const o=t instanceof Error?t.message:String(t);throw o==="message is required"?new g("worker_message_required","A non-empty Worker message is required.","Provide --message or --message-file and retry worker send."):o.includes("not found")||o.includes("not registered")?new g("worker_ref_revoked","The Worker is no longer available in this controller scope.","Start a new Worker if the task still needs to continue.",410):new g("worker_command_failed",o,"Read the Worker state, correct the request, and retry once.")}if(!s)throw new g("worker_command_unavailable","The requested Worker operation is unavailable.","Use worker start, read, send, wait, or stop.",404);return{status:s.status,body:{...s.body,workerId:l.workerSessionId}}}export{g as EphemeralWorkerError,oe as handleEphemeralWorkerIpcRoute,b as handleWorkerIpcRoute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import b from"node:path";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as E}from"@shennian/wire";import{createAgent as N}from"../../agents/adapter.js";import{buildManagedAgentEnv as S}from"../../agents/config-status.js";import{ManagerModeAdapter as T}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as O}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as M}from"../../security/managed-agent-policy.js";const x="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",R=6e4,W=new O;async function L(e,r={}){try{return await P(e,r)}catch(t){const a=t instanceof Error?t.message:"";return{ok:!1,errorCode:a==="agent_validation_cleanup_failed"?a:v(t,"start")}}}async function P(e,r){if(!E(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(
|
|
1
|
+
import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import b from"node:path";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as E}from"@shennian/wire";import{createAgent as N}from"../../agents/adapter.js";import{buildManagedAgentEnv as S}from"../../agents/config-status.js";import{ManagerModeAdapter as T}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as O}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as M}from"../../security/managed-agent-policy.js";const x="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",R=6e4,W=new O;async function L(e,r={}){try{return await P(e,r)}catch(t){const a=t instanceof Error?t.message:"";return{ok:!1,errorCode:a==="agent_validation_cleanup_failed"?a:v(t,"start")}}}async function P(e,r){if(!E(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(!b.isAbsolute(e.workDir.trim()))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};let t;try{t=await(r.resolveWorkDir??U)(e.workDir)}catch(i){return{ok:!1,errorCode:V(i)}}if(!(r.validateWorkDir?await r.validateWorkDir(t):(await M(t,async()=>t)).ok))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};const s=(r.createAdapter??N)(e.runtimeAdapter);if(!s)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const o=e.sessionMode==="manager"?new T(s,e.runtimeAdapter):s;let f=`agent-validation-${C()}`,l,u="",m="",_=null,c=null;const D=new Promise(i=>{c=i}),p=i=>{i.agentSessionId&&(u=i.agentSessionId,r.ignoreNativeSession?.(e.runtimeAdapter,u)),i.state==="delta"&&!i.thinking&&i.text&&(m+=i.text),i.state==="final"?(i.text&&(m+=i.text),c?.({kind:"final"})):(i.state==="error"||i.state==="aborted")&&c?.({kind:"error",error:i.message??i.state})},w=i=>c?.({kind:"error",error:i});o.on("agentEvent",p),o.on("error",w);const h=async()=>{e.runtimeAdapter==="pi-coding-agent"&&(l=await(r.registerPiSession??I)(),f=l.sessionId),o.configure?.({sessionId:f,env:S(e.runtimeAdapter)});try{await o.start(f,t,null,{persistNativeSession:!1})}catch(n){return{ok:!1,errorCode:v(n,"start")}}const i=new Promise(n=>{_=setTimeout(()=>n({kind:"timeout"}),r.timeoutMs??R)}),k=o.send(x,e.modelId??void 0).then(()=>D).catch(n=>({kind:"error",error:n})),d=await Promise.race([k,i]);return d.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:d.kind==="error"?{ok:!1,errorCode:v(d.error,"response")}:m.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let A,g=!1;try{A=await h()}finally{_&&clearTimeout(_),o.removeListener("agentEvent",p),o.removeListener("error",w);try{await o.stop()}catch{l&&(g=!0)}finally{await l?.dispose().catch(()=>{g=!0})}u&&(r.cleanupNativeSession??((k,d,n)=>{W.execute(k,d,{workDir:n}).catch(()=>{})}))(e.runtimeAdapter,u,t)}return g?{ok:!1,errorCode:"agent_validation_cleanup_failed"}:A}async function Y(e,r){const t=j(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const a=await L(t,{ignoreNativeSession:(s,o)=>{e.nativeFusion?.ignoreNativeSource(s,o)}});e.client.sendRes(a.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:a.errorCode})}async function U(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await y.realpath(r);if(!(await y.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function V(e){const r=F(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function v(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid)|unknown model/i.test(t)?"agent_validation_model_unavailable":/quota|insufficient.*(?:credit|balance)|billing|rate.?limit|\b429\b/i.test(t)?"agent_validation_quota_exceeded":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":/ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|network|fetch failed|connection.*(?:failed|closed|refused)/i.test(t)?"agent_validation_network_failed":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function F(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function j(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",a=e.sessionMode==="manager"?"manager":"standard";return!E(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:a,modelId:typeof e.modelId=="string"?e.modelId:null}}export{Y as handleAgentWorkspaceValidation,L as validateAgentWorkspaceConfiguration};
|