|
@@ -10,8 +10,9 @@ import type {GatewayFrame,GatewayTarget} from '../../server/workspaceGateway.js'
|
|
|
import {ProfileComputerSession} from './profileSession.js'
|
|
import {ProfileComputerSession} from './profileSession.js'
|
|
|
import {UNCONFIGURED_COMPUTER_IMAGE} from '../../shared/runner.js'
|
|
import {UNCONFIGURED_COMPUTER_IMAGE} from '../../shared/runner.js'
|
|
|
import type {RunnerConfiguration} from '../../shared/runner.js'
|
|
import type {RunnerConfiguration} from '../../shared/runner.js'
|
|
|
-import {ContainerComputerProvider,COMPUTER_WORKSPACE,CUA_DRIVER,CUA_SOCKET,type ComputerSpecification} from '../computers/container.js'
|
|
|
|
|
|
|
+import {ContainerComputerProvider,ComputerError,COMPUTER_WORKSPACE,CUA_DRIVER,CUA_SOCKET,type ComputerSpecification} from '../computers/container.js'
|
|
|
import {ComputerPool,type ComputerLease} from '../computers/pool.js'
|
|
import {ComputerPool,type ComputerLease} from '../computers/pool.js'
|
|
|
|
|
+import {needsVmLease,softComputerFailure} from '../computers/computerFailures.js'
|
|
|
import {ComposeComputerProvider} from '../computers/compose.js'
|
|
import {ComposeComputerProvider} from '../computers/compose.js'
|
|
|
import {COMPOSE_DESKTOP_IMAGE,type DesktopRelay} from '../../shared/composeDesktops.js'
|
|
import {COMPOSE_DESKTOP_IMAGE,type DesktopRelay} from '../../shared/composeDesktops.js'
|
|
|
import {ComputerControls} from './control.js'
|
|
import {ComputerControls} from './control.js'
|
|
@@ -23,7 +24,7 @@ import {homedir,platform} from 'node:os'
|
|
|
|
|
|
|
|
export interface ComputerTarget {environmentId:string;ownerKey:string;agentId:string;hostAccess?:boolean;profileSession?:boolean;hermesRuntime?:boolean}
|
|
export interface ComputerTarget {environmentId:string;ownerKey:string;agentId:string;hostAccess?:boolean;profileSession?:boolean;hermesRuntime?:boolean}
|
|
|
export interface WorkerSession {agentId?:string;id:string;profile:string;ownerKey:string;environmentId:string;cwd:string;configuredCwd:string;history:any[];outcome?:'complete'|'failed'|'uncertain';vmExecution?:'profile';profileSessionId?:string;hermesRuntime?:boolean}
|
|
export interface WorkerSession {agentId?:string;id:string;profile:string;ownerKey:string;environmentId:string;cwd:string;configuredCwd:string;history:any[];outcome?:'complete'|'failed'|'uncertain';vmExecution?:'profile';profileSessionId?:string;hermesRuntime?:boolean}
|
|
|
-interface Live {workMarker?:string;paused?:boolean;desktop?:{width:number;height:number};pauseJob?:Promise<void>;segment?:number;toolCalls:Set<Promise<unknown>>;journal:Map<string,{name:string;result:unknown}>;proxyHandle?:ProxyHandle;session:WorkerSession;model:WorkerModel;contextConfig?:WorkerContextConfiguration;proxyEnv?:WorkerProxyEnvironment;lease:ComputerLease;controller:AbortController;worker?:HermesWorkerProcess;finishing?:Promise<void>;stopping?:Promise<void>;releasing?:Promise<void>;images:any[];running:boolean;received:boolean}
|
|
|
|
|
|
|
+interface Live {workMarker?:string;paused?:boolean;desktop?:{width:number;height:number};pauseJob?:Promise<void>;segment?:number;toolCalls:Set<Promise<unknown>>;journal:Map<string,{name:string;result:unknown}>;proxyHandle?:ProxyHandle;session:WorkerSession;model:WorkerModel;contextConfig?:WorkerContextConfiguration;proxyEnv?:WorkerProxyEnvironment;lease?:ComputerLease;acquiring?:Promise<ComputerLease>;controller:AbortController;worker?:HermesWorkerProcess;finishing?:Promise<void>;stopping?:Promise<void>;releasing?:Promise<void>;images:any[];running:boolean;received:boolean}
|
|
|
const object=(properties:Record<string,unknown>,required:string[])=>({type:'object',properties,required,additionalProperties:false})
|
|
const object=(properties:Record<string,unknown>,required:string[])=>({type:'object',properties,required,additionalProperties:false})
|
|
|
const text={type:'string'}
|
|
const text={type:'string'}
|
|
|
const TOOLS:WorkerTool[]=[
|
|
const TOOLS:WorkerTool[]=[
|
|
@@ -215,7 +216,6 @@ export class ComputerGateway {
|
|
|
if(this.live)throw new HttpError(409,'电脑通道已有会话','computer_session_busy')
|
|
if(this.live)throw new HttpError(409,'电脑通道已有会话','computer_session_busy')
|
|
|
await this.authorized()
|
|
await this.authorized()
|
|
|
if(method==='session.resume'&¶ms.recoverOnly===true){const session=this.runtime.session(String(params.session_id),this.meta,String(params.profile));this.recoverySession=session;if(session.profileSessionId){await this.openProfile(session,params);await this.profileSession!.stop()}const resource=this.runtime.pool.status(this.meta.ownerKey).find(item=>item.environmentId===this.meta.environmentId);if(resource?.holderIds.includes(this.workId))await this.runtime.pool.stopHolder(this.meta.ownerKey,this.meta.environmentId,this.workId);return {session_id:session.id,stored_session_id:session.id,running:false,info:{profile_name:session.profile}}}
|
|
if(method==='session.resume'&¶ms.recoverOnly===true){const session=this.runtime.session(String(params.session_id),this.meta,String(params.profile));this.recoverySession=session;if(session.profileSessionId){await this.openProfile(session,params);await this.profileSession!.stop()}const resource=this.runtime.pool.status(this.meta.ownerKey).find(item=>item.environmentId===this.meta.environmentId);if(resource?.holderIds.includes(this.workId))await this.runtime.pool.stopHolder(this.meta.ownerKey,this.meta.environmentId,this.workId);return {session_id:session.id,stored_session_id:session.id,running:false,info:{profile_name:session.profile}}}
|
|
|
- if(this.runtime.imageFor(this.meta)===UNCONFIGURED_COMPUTER_IMAGE)throw new HttpError(409,'请先在应用设置的本地虚拟机页面完成准备','computer_image_required')
|
|
|
|
|
const profile=String(params.profile),resolved=this.hermesManaged?{cwd:this.runtime.pool.definition(this.meta.ownerKey,this.meta.environmentId)?.cwd??COMPUTER_WORKSPACE,configuredCwd:'.'}:await (this.meta.profileSession?this.runtime.resolveWorkspace(profile,this.controller.signal):this.runtime.resolve(profile,this.controller.signal))
|
|
const profile=String(params.profile),resolved=this.hermesManaged?{cwd:this.runtime.pool.definition(this.meta.ownerKey,this.meta.environmentId)?.cwd??COMPUTER_WORKSPACE,configuredCwd:'.'}:await (this.meta.profileSession?this.runtime.resolveWorkspace(profile,this.controller.signal):this.runtime.resolve(profile,this.controller.signal))
|
|
|
if(this.meta.hostAccess){const configured=String(resolved.configuredCwd??'.');this.hostCwd=['.','auto','cwd'].includes(configured)?homedir():hostPath(homedir(),configured)}
|
|
if(this.meta.hostAccess){const configured=String(resolved.configuredCwd??'.');this.hostCwd=['.','auto','cwd'].includes(configured)?homedir():hostPath(homedir(),configured)}
|
|
|
await this.authorized();this.guard()
|
|
await this.authorized();this.guard()
|
|
@@ -223,18 +223,10 @@ export class ComputerGateway {
|
|
|
if(this.meta.profileSession)session.vmExecution='profile'
|
|
if(this.meta.profileSession)session.vmExecution='profile'
|
|
|
if(this.hermesManaged)session.hermesRuntime=true
|
|
if(this.hermesManaged)session.hermesRuntime=true
|
|
|
session.cwd=this.meta.environmentId!==this.meta.agentId?(this.runtime.pool.definition(this.meta.ownerKey,this.meta.environmentId)?.cwd??COMPUTER_WORKSPACE):resolved.cwd;session.configuredCwd=resolved.configuredCwd
|
|
session.cwd=this.meta.environmentId!==this.meta.agentId?(this.runtime.pool.definition(this.meta.ownerKey,this.meta.environmentId)?.cwd??COMPUTER_WORKSPACE):resolved.cwd;session.configuredCwd=resolved.configuredCwd
|
|
|
- await this.runtime.pool.configure(this.spec(session),()=>this.guard())
|
|
|
|
|
- await this.authorized()
|
|
|
|
|
- const controller=new AbortController(),lease=await this.runtime.pool.acquire(this.spec(session),this.workId,()=>{this.guard();if(controller.signal.aborted)throw new Error('cancelled')},controller.signal)
|
|
|
|
|
- if(this.closed){controller.abort();await this.runtime.pool.release(lease);throw new Error('电脑通道已关闭')}
|
|
|
|
|
- this.live={session,lease,controller,model:('model' in resolved?resolved.model:undefined) as WorkerModel,contextConfig:'contextConfig' in resolved?resolved.contextConfig as WorkerContextConfiguration:undefined,proxyEnv:'proxyEnv' in resolved?resolved.proxyEnv as WorkerProxyEnvironment:undefined,images:[],running:false,received:false,toolCalls:new Set(),journal:new Map()}
|
|
|
|
|
|
|
+ // Defer pool.configure/acquire until a computer_* (or skill) tool actually needs the VM.
|
|
|
|
|
+ const controller=new AbortController()
|
|
|
|
|
+ this.live={session,controller,model:('model' in resolved?resolved.model:undefined) as WorkerModel,contextConfig:'contextConfig' in resolved?resolved.contextConfig as WorkerContextConfiguration:undefined,proxyEnv:'proxyEnv' in resolved?resolved.proxyEnv as WorkerProxyEnvironment:undefined,images:[],running:false,received:false,toolCalls:new Set(),journal:new Map()}
|
|
|
this.runtime.save(session);this.runtime.attachGateway(this)
|
|
this.runtime.save(session);this.runtime.attachGateway(this)
|
|
|
- if(this.runtime.config.network==='public-proxy'){
|
|
|
|
|
- const live=this.live
|
|
|
|
|
- try{live.proxyHandle=await this.runtime.acquireProxy(this.spec(session),()=>{if(!this.runtime.pool.hasHolders(this.meta.ownerKey,this.meta.environmentId))throw new HttpError(403,'电脑任务授权已失效','computer_authorization_revoked')})}
|
|
|
|
|
- catch(error){await this.interrupt();throw error}
|
|
|
|
|
- }
|
|
|
|
|
- this.timer=setInterval(()=>{if(this.checking)return;this.checking=true;void this.authorized().then(()=>this.runtime.pool.renew(this.live!.lease)).catch(async()=>{await this.interrupt().catch(()=>{});if(!this.closed)this.onDisconnect()}).finally(()=>{this.checking=false})},5000);this.timer.unref()
|
|
|
|
|
if(params.toolsOnly===true)return {session_id:session.id,stored_session_id:session.id,running:false,info:{profile_name:profile,tools_only:true}}
|
|
if(params.toolsOnly===true)return {session_id:session.id,stored_session_id:session.id,running:false,info:{profile_name:profile,tools_only:true}}
|
|
|
if(this.meta.profileSession||this.hermesManaged){
|
|
if(this.meta.profileSession||this.hermesManaged){
|
|
|
try{const opened=await this.openProfile(session,params);return {...opened,session_id:session.id,stored_session_id:session.id}}
|
|
try{const opened=await this.openProfile(session,params);return {...opened,session_id:session.id,stored_session_id:session.id}}
|
|
@@ -276,7 +268,7 @@ export class ComputerGateway {
|
|
|
const encoded=method==='image.attach_bytes'?String(params.content_base64??''):String(params.data_url??'').split(',')[1]??''
|
|
const encoded=method==='image.attach_bytes'?String(params.content_base64??''):String(params.data_url??'').split(',')[1]??''
|
|
|
const bytes=Buffer.from(encoded,'base64');if(bytes.length>25*1024*1024)throw new HttpError(413,'附件超过 25 MiB','attachment_too_large')
|
|
const bytes=Buffer.from(encoded,'base64');if(bytes.length>25*1024*1024)throw new HttpError(413,'附件超过 25 MiB','attachment_too_large')
|
|
|
const relative=`.yaoyao-inputs/${randomUUID()}/${name}`
|
|
const relative=`.yaoyao-inputs/${randomUUID()}/${name}`
|
|
|
- await this.runtime.pool.use(live.lease,context=>this.runtime.provider.execute(this.spec(live.session),['python3','-c','import sys,pathlib; p=pathlib.Path(sys.argv[1]); p.parent.mkdir(parents=True,exist_ok=True); p.write_bytes(sys.stdin.buffer.read())',relative],{...context,input:bytes}))
|
|
|
|
|
|
|
+ await this.ensureLease(live);await this.runtime.pool.use(live.lease!,context=>this.runtime.provider.execute(this.spec(live.session),['python3','-c','import sys,pathlib; p=pathlib.Path(sys.argv[1]); p.parent.mkdir(parents=True,exist_ok=True); p.write_bytes(sys.stdin.buffer.read())',relative],{...context,input:bytes}))
|
|
|
if(method==='image.attach_bytes'){
|
|
if(method==='image.attach_bytes'){
|
|
|
const mime=String(params.mime_type??'image/png')
|
|
const mime=String(params.mime_type??'image/png')
|
|
|
if(!/^image\/(?:png|jpeg|webp|gif)$/.test(mime))throw new HttpError(400,'图片格式无效','invalid_image')
|
|
if(!/^image\/(?:png|jpeg|webp|gif)$/.test(mime))throw new HttpError(400,'图片格式无效','invalid_image')
|
|
@@ -354,6 +346,24 @@ export class ComputerGateway {
|
|
|
}
|
|
}
|
|
|
installTeamLease(id:string,catalog:WorkerTool[],call:(name:string,args:Record<string,unknown>,callId:string)=>Promise<unknown>,workspaceMemory=false){this.team={id,catalog,call,workspaceMemory}}
|
|
installTeamLease(id:string,catalog:WorkerTool[],call:(name:string,args:Record<string,unknown>,callId:string)=>Promise<unknown>,workspaceMemory=false){this.team={id,catalog,call,workspaceMemory}}
|
|
|
removeTeamLease(id:string){if(this.team?.id===id)this.team=undefined}
|
|
removeTeamLease(id:string){if(this.team?.id===id)this.team=undefined}
|
|
|
|
|
+ private ensureLease(live:Live):Promise<ComputerLease>{
|
|
|
|
|
+ if(live.lease)return Promise.resolve(live.lease)
|
|
|
|
|
+ live.acquiring??=(async()=>{
|
|
|
|
|
+ if(this.runtime.imageFor(this.meta)===UNCONFIGURED_COMPUTER_IMAGE)throw new HttpError(409,'请先在应用设置的本地虚拟机页面完成准备','computer_image_required')
|
|
|
|
|
+ await this.runtime.pool.configure(this.spec(live.session),()=>this.guard())
|
|
|
|
|
+ await this.authorized()
|
|
|
|
|
+ const lease=await this.runtime.pool.acquire(this.spec(live.session),this.workId,()=>{this.guard();if(live.controller.signal.aborted)throw new Error('cancelled')},live.controller.signal)
|
|
|
|
|
+ if(this.closed){await this.runtime.pool.release(lease);throw new Error('电脑通道已关闭')}
|
|
|
|
|
+ live.lease=lease
|
|
|
|
|
+ if(this.runtime.config.network==='public-proxy'){
|
|
|
|
|
+ try{live.proxyHandle=await this.runtime.acquireProxy(this.spec(live.session),()=>{if(!this.runtime.pool.hasHolders(this.meta.ownerKey,this.meta.environmentId))throw new HttpError(403,'电脑任务授权已失效','computer_authorization_revoked')})}
|
|
|
|
|
+ catch(error){await this.runtime.pool.release(lease).catch(()=>{});live.lease=undefined;throw error}
|
|
|
|
|
+ }
|
|
|
|
|
+ this.timer=setInterval(()=>{if(this.checking||!this.live?.lease)return;this.checking=true;void this.authorized().then(()=>this.runtime.pool.renew(this.live!.lease!)).catch(async()=>{await this.interrupt().catch(()=>{});if(!this.closed)this.onDisconnect()}).finally(()=>{this.checking=false})},5000);this.timer.unref()
|
|
|
|
|
+ return lease
|
|
|
|
|
+ })().finally(()=>{live.acquiring=undefined})
|
|
|
|
|
+ return live.acquiring
|
|
|
|
|
+ }
|
|
|
private async tool(live:Live,name:string,args:unknown,id:string):Promise<unknown>{
|
|
private async tool(live:Live,name:string,args:unknown,id:string):Promise<unknown>{
|
|
|
if(live.paused)throw new HttpError(409,'电脑正在等待人工操作','computer_paused')
|
|
if(live.paused)throw new HttpError(409,'电脑正在等待人工操作','computer_paused')
|
|
|
await this.authorized();this.guard()
|
|
await this.authorized();this.guard()
|
|
@@ -362,17 +372,30 @@ export class ComputerGateway {
|
|
|
try{
|
|
try{
|
|
|
const team=this.team?.catalog.find(tool=>tool.name===name)
|
|
const team=this.team?.catalog.find(tool=>tool.name===name)
|
|
|
if(team)return await this.team!.call(team.id??name,z.record(z.string(),z.unknown()).parse(args),id)
|
|
if(team)return await this.team!.call(team.id??name,z.record(z.string(),z.unknown()).parse(args),id)
|
|
|
|
|
+ if(needsVmLease(name,this.team?.catalog.map(tool=>tool.name)??[],SKILL_TOOLS.map(tool=>tool.name))){
|
|
|
|
|
+ try{await this.ensureLease(live)}
|
|
|
|
|
+ catch(error){
|
|
|
|
|
+ if(softComputerFailure(error)){
|
|
|
|
|
+ const message=error instanceof Error&&error.message?error.message:'电脑环境不可用'
|
|
|
|
|
+ return {error:message,code:error instanceof HttpError||error instanceof ComputerError?error.code:'computer_unavailable'}
|
|
|
|
|
+ }
|
|
|
|
|
+ throw error
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const lease=live.lease
|
|
|
if(SKILL_TOOLS.some(tool=>tool.name===name)){
|
|
if(SKILL_TOOLS.some(tool=>tool.name===name)){
|
|
|
|
|
+ if(!lease)throw new HttpError(409,'电脑环境尚未就绪','computer_unavailable')
|
|
|
this.skillSession??=new ProfileSkillSession({
|
|
this.skillSession??=new ProfileSkillSession({
|
|
|
python:this.runtime.config.python,script:this.runtime.script,hermesSource:this.runtime.config.hermesSource,hermesHome:this.runtime.config.hermesHome,
|
|
python:this.runtime.config.python,script:this.runtime.script,hermesSource:this.runtime.config.hermesSource,hermesHome:this.runtime.config.hermesHome,
|
|
|
profile:live.session.profile,ownerKey:this.meta.ownerKey,agentId:this.meta.agentId,taskId:this.workId,signal:live.controller.signal,
|
|
profile:live.session.profile,ownerKey:this.meta.ownerKey,agentId:this.meta.agentId,taskId:this.workId,signal:live.controller.signal,
|
|
|
- authorize:async()=>{await this.authorized();if(live.paused||live.received)throw new HttpError(410,'本轮技能工具已结束或暂停','computer_cancelled');this.runtime.pool.authorize(live.lease)},
|
|
|
|
|
- execute:(argv,input)=>this.runtime.pool.use(live.lease,context=>this.runtime.provider.execute(this.spec(live.session),argv,{...context,input})),
|
|
|
|
|
- install:(bundle,script)=>this.runtime.pool.use(live.lease,context=>this.runtime.provider.installSkill(this.spec(live.session),bundle,script,context)),
|
|
|
|
|
|
|
+ authorize:async()=>{await this.authorized();if(live.paused||live.received)throw new HttpError(410,'本轮技能工具已结束或暂停','computer_cancelled');this.runtime.pool.authorize(lease)},
|
|
|
|
|
+ execute:(argv,input)=>this.runtime.pool.use(lease,context=>this.runtime.provider.execute(this.spec(live.session),argv,{...context,input})),
|
|
|
|
|
+ install:(bundle,script)=>this.runtime.pool.use(lease,context=>this.runtime.provider.installSkill(this.spec(live.session),bundle,script,context)),
|
|
|
})
|
|
})
|
|
|
return await this.skillSession.call(name,args)
|
|
return await this.skillSession.call(name,args)
|
|
|
}
|
|
}
|
|
|
- return await this.runtime.pool.use(live.lease,async context=>{
|
|
|
|
|
|
|
+ if(!lease)throw new HttpError(409,'电脑环境尚未就绪','computer_unavailable')
|
|
|
|
|
+ return await this.runtime.pool.use(lease,async context=>{
|
|
|
const execute=(argv:string[],input?:Buffer,lane?:'exec'|'gui',user?:'cua'|'root')=>this.runtime.provider.execute(this.spec(live.session),argv,{...context,input,...(lane?{lane}:{}),...(user?{user}:{})})
|
|
const execute=(argv:string[],input?:Buffer,lane?:'exec'|'gui',user?:'cua'|'root')=>this.runtime.provider.execute(this.spec(live.session),argv,{...context,input,...(lane?{lane}:{}),...(user?{user}:{})})
|
|
|
if(this.meta.hostAccess&&name.startsWith('host_'))return hostTool(name,args,{...context,cwd:this.hostCwd})
|
|
if(this.meta.hostAccess&&name.startsWith('host_'))return hostTool(name,args,{...context,cwd:this.hostCwd})
|
|
|
if((this.meta.hostAccess||this.hermesManaged)&&name==='computer_copy_file'){
|
|
if((this.meta.hostAccess||this.hermesManaged)&&name==='computer_copy_file'){
|
|
@@ -458,12 +481,18 @@ export class ComputerGateway {
|
|
|
}
|
|
}
|
|
|
throw new HttpError(403,'本轮未授权该工具','computer_tool_forbidden')
|
|
throw new HttpError(403,'本轮未授权该工具','computer_tool_forbidden')
|
|
|
})
|
|
})
|
|
|
|
|
+ }catch(error){
|
|
|
|
|
+ if(softComputerFailure(error)){
|
|
|
|
|
+ const message=error instanceof Error&&error.message?error.message:'电脑环境不可用'
|
|
|
|
|
+ return {error:message,code:error instanceof HttpError||error instanceof ComputerError?error.code:'computer_unavailable'}
|
|
|
|
|
+ }
|
|
|
|
|
+ throw error
|
|
|
}finally{this.event('tool.completed',{tool_id:id,name})}
|
|
}finally{this.event('tool.completed',{tool_id:id,name})}
|
|
|
}
|
|
}
|
|
|
private async finish(live:Live,frame:WorkerFrame){
|
|
private async finish(live:Live,frame:WorkerFrame){
|
|
|
if(this.closed||live.paused||live.controller.signal.aborted)return
|
|
if(this.closed||live.paused||live.controller.signal.aborted)return
|
|
|
await this.authorized()
|
|
await this.authorized()
|
|
|
- this.runtime.pool.renew(live.lease)
|
|
|
|
|
|
|
+ if(live.lease)this.runtime.pool.renew(live.lease)
|
|
|
const contextErrors:Record<string,string>={
|
|
const contextErrors:Record<string,string>={
|
|
|
context_compaction_disabled:'模型上下文已超限,基础 Profile 关闭了自动压缩。请开启压缩、缩短输入或新建对话后继续。',
|
|
context_compaction_disabled:'模型上下文已超限,基础 Profile 关闭了自动压缩。请开启压缩、缩短输入或新建对话后继续。',
|
|
|
context_compaction_failed:'模型上下文压缩未能完成,本轮已结束。请缩短输入、检查模型配置或新建对话后继续。',
|
|
context_compaction_failed:'模型上下文压缩未能完成,本轮已结束。请缩短输入、检查模型配置或新建对话后继续。',
|
|
@@ -487,6 +516,7 @@ export class ComputerGateway {
|
|
|
private release(live:Live,keepRunning=false){
|
|
private release(live:Live,keepRunning=false){
|
|
|
clearInterval(this.timer)
|
|
clearInterval(this.timer)
|
|
|
live.releasing??=(async()=>{
|
|
live.releasing??=(async()=>{
|
|
|
|
|
+ if(!live.lease){await live.proxyHandle?.release().catch(()=>{});return}
|
|
|
try{await live.proxyHandle?.release();await this.runtime.pool.release(live.lease,keepRunning)}
|
|
try{await live.proxyHandle?.release();await this.runtime.pool.release(live.lease,keepRunning)}
|
|
|
catch(error){await this.runtime.pool.release(live.lease).catch(()=>{});throw error}
|
|
catch(error){await this.runtime.pool.release(live.lease).catch(()=>{});throw error}
|
|
|
})()
|
|
})()
|
|
@@ -520,7 +550,7 @@ export class ComputerGateway {
|
|
|
history.push({role:'tool',name:tool.function?.name??entry?.name,tool_call_id:tool.id,content});done.add(tool.id)
|
|
history.push({role:'tool',name:tool.function?.name??entry?.name,tool_call_id:tool.id,content});done.add(tool.id)
|
|
|
}
|
|
}
|
|
|
live.session.history=history;this.runtime.save(live.session)
|
|
live.session.history=history;this.runtime.save(live.session)
|
|
|
- this.event('computer.paused',{stage:'human',generation:live.lease.generation})
|
|
|
|
|
|
|
+ this.event('computer.paused',{stage:'human',generation:live.lease?.generation})
|
|
|
})()
|
|
})()
|
|
|
await live.pauseJob
|
|
await live.pauseJob
|
|
|
}
|
|
}
|
|
@@ -529,7 +559,7 @@ export class ComputerGateway {
|
|
|
if(!live?.paused)throw new HttpError(409,'机器人没有处于接管状态','computer_not_paused')
|
|
if(!live?.paused)throw new HttpError(409,'机器人没有处于接管状态','computer_not_paused')
|
|
|
await live.pauseJob;await this.authorized()
|
|
await live.pauseJob;await this.authorized()
|
|
|
live.paused=false;live.pauseJob=undefined;live.journal.clear()
|
|
live.paused=false;live.pauseJob=undefined;live.journal.clear()
|
|
|
- this.event('computer.resumed',{generation:live.lease.generation,notes})
|
|
|
|
|
|
|
+ this.event('computer.resumed',{generation:live.lease?.generation,notes})
|
|
|
if(this.profileSession){
|
|
if(this.profileSession){
|
|
|
await this.profileSession.bind();this.guard()
|
|
await this.profileSession.bind();this.guard()
|
|
|
await this.profileSession.rpc('prompt.submit',{text:`${this.rules}\n用户已交还电脑控制权。请先检查当前状态,避免重复已完成的操作。交还说明:${notes||'无额外说明'}\n${live.workMarker??''}`})
|
|
await this.profileSession.rpc('prompt.submit',{text:`${this.rules}\n用户已交还电脑控制权。请先检查当前状态,避免重复已完成的操作。交还说明:${notes||'无额外说明'}\n${live.workMarker??''}`})
|