|
|
@@ -46,3 +46,38 @@ it('does not grant temporary helpers or mixed profiles a shared desktop',()=>{
|
|
|
expect(()=>service.create('owner',command)).toThrow('持久隔离成员')
|
|
|
}finally{nodes.close();store.close();rmSync(home,{recursive:true,force:true})}
|
|
|
})
|
|
|
+it('changes only persistent local VM members and leaves unrelated running chats untouched',()=>{
|
|
|
+ const home=mkdtempSync(join(tmpdir(),'yaoyao-shared-scope-')),store=new WorkspaceStore(home),nodes=new WorkspaceNodes(store,loadServerConfig({HERMES_YAOYAO_HOME:home}),{} as any),service=new SharedComputers(store,{} as any,nodes,{} as any)
|
|
|
+ try{
|
|
|
+ const vm=store.createAgent('owner',{name:'虚拟机成员',execution:'computer',profile:'default'})
|
|
|
+ const chat=store.createAgent('owner',{name:'正在聊天',profile:'server'})
|
|
|
+ const archived=store.createAgent('owner',{name:'已归档',execution:'computer',profile:'default'})
|
|
|
+ store.put('owner','agent',archived.id,{...archived,archived:true})
|
|
|
+ const helper=store.createAgent('owner',{name:'临时助手',execution:'computer',profile:'default'},{createdByAgentId:vm.id,createdFromRunId:randomUUID(),temporaryGoalId:randomUUID()})
|
|
|
+ const remote=store.createAgent('owner',{name:'远端成员',execution:'computer',profile:'default'})
|
|
|
+ store.put('owner','agent',remote.id,{...remote,nodeId:randomUUID()})
|
|
|
+ const unaffected=[chat,archived,helper,remote].map(a=>store.require<WorkspaceAgent>('owner','agent',a.id))
|
|
|
+ for(const agent of unaffected)store.put('owner','turn',agent.id,{agentId:agent.id,status:'running'})
|
|
|
+ service.setLocalVmMode('owner','shared','runner')
|
|
|
+ const linked=store.require<WorkspaceAgent>('owner','agent',vm.id)
|
|
|
+ expect(linked.computerEnvironmentId).toBeTruthy()
|
|
|
+ for(const agent of unaffected)expect(store.require('owner','agent',agent.id)).toEqual(agent)
|
|
|
+ service.setLocalVmMode('owner','shared','runner')
|
|
|
+ expect(store.require('owner','agent',vm.id)).toEqual(linked)
|
|
|
+ // Explicit per-Agent execution changes must still guard an ordinary chat.
|
|
|
+ expect(()=>service.assertLocalVmIdle('owner',[chat.id])).toThrow('正在聊天')
|
|
|
+ for(const status of ['queued','running','waiting','uncertain','cancelling']){
|
|
|
+ store.put('owner','turn','active-vm',{agentId:vm.id,status})
|
|
|
+ expect(()=>service.setLocalVmMode('owner','per-bot','runner')).toThrow('请先停止「虚拟机成员」')
|
|
|
+ expect(store.require('owner','agent',vm.id)).toEqual(linked)
|
|
|
+ }
|
|
|
+ store.remove('owner','turn','active-vm')
|
|
|
+ // A controller acquired through another member still owns this same VM.
|
|
|
+ store.put('_system','computer-control','human',{owner:'owner',agentId:archived.id,environmentId:linked.computerEnvironmentId,expiresAt:Date.now()+30000})
|
|
|
+ expect(()=>service.setLocalVmMode('owner','per-bot','runner')).toThrow('交还')
|
|
|
+ store.remove('_system','computer-control','human')
|
|
|
+ service.setLocalVmMode('owner','per-bot','runner')
|
|
|
+ expect(store.require<WorkspaceAgent>('owner','agent',vm.id).computerEnvironmentId).toBeUndefined()
|
|
|
+ for(const agent of unaffected)expect(store.require('owner','agent',agent.id)).toEqual(agent)
|
|
|
+ }finally{nodes.close();store.close();rmSync(home,{recursive:true,force:true})}
|
|
|
+})
|