Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2bc26f9

Browse files
committed
Add agent state machine
1 parente4bd57f commit2bc26f9

File tree

2 files changed

+146
-79
lines changed

2 files changed

+146
-79
lines changed

‎src/api/workspace.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ export async function waitForBuild(
138138

139139
/**
140140
* Streams agent logs to the emitter in real-time.
141-
* Fetches existing logs and subscribes to new logs via websocket.
142141
* Returns the websocket and a completion promise that rejects on error.
143142
*/
144143
exportasyncfunctionstreamAgentLogs(

‎src/remote/remote.ts‎

Lines changed: 146 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -539,81 +539,13 @@ export class Remote {
539539
constinbox=awaitInbox.create(workspace,workspaceClient,this.logger);
540540
disposables.push(inbox);
541541

542-
// Wait for the agent to connect.
543-
if(agent.status==="connecting"){
544-
this.logger.info(`Waiting for${workspaceName}/${agent.name}...`);
545-
constupdatedAgent=awaitthis.waitForAgentWithProgress(
546-
monitor,
547-
agent,
548-
`Waiting for agent${agent.name} to connect...`,
549-
(foundAgent)=>foundAgent.status!=="connecting",
550-
);
551-
agent=updatedAgent;
552-
this.logger.info(`Agent${agent.name} status is now`,agent.status);
553-
}
554-
555-
// Make sure the agent is connected.
556-
if(agent.status!=="connected"){
557-
constresult=awaitthis.vscodeProposed.window.showErrorMessage(
558-
`${workspaceName}/${agent.name}${agent.status}`,
559-
{
560-
useCustom:true,
561-
modal:true,
562-
detail:`The${agent.name} agent failed to connect. Try restarting your workspace.`,
563-
},
564-
);
565-
if(!result){
566-
awaitthis.closeRemote();
567-
return;
568-
}
569-
awaitthis.reloadWindow();
570-
return;
571-
}
572-
573-
if(agent.lifecycle_state==="starting"){
574-
constisBlocking=agent.scripts.some(
575-
(script)=>script.start_blocks_login,
576-
);
577-
if(isBlocking){
578-
this.logger.info(
579-
`Waiting for${workspaceName}/${agent.name} startup...`,
580-
);
581-
582-
letterminalSession:TerminalSession|undefined;
583-
letsocket:OneWayWebSocket<WorkspaceAgentLog[]>|undefined;
584-
try{
585-
terminalSession=newTerminalSession("Agent Log");
586-
const{socket:agentSocket,completion:logsCompletion}=
587-
awaitstreamAgentLogs(
588-
workspaceClient,
589-
terminalSession.writeEmitter,
590-
agent,
591-
);
592-
socket=agentSocket;
593-
594-
constagentStatePromise=this.waitForAgentWithProgress(
595-
monitor,
596-
agent,
597-
`Waiting for agent${agent.name} startup scripts...`,
598-
(foundAgent)=>foundAgent.lifecycle_state!=="starting",
599-
);
600-
601-
// Race between logs completion and agent state change
602-
constupdatedAgent=awaitPromise.race([
603-
agentStatePromise,
604-
logsCompletion.then(()=>agentStatePromise),
605-
]);
606-
agent=updatedAgent;
607-
this.logger.info(
608-
`Agent${agent.name} lifecycle state is now`,
609-
agent.lifecycle_state,
610-
);
611-
}finally{
612-
terminalSession?.dispose();
613-
socket?.close();
614-
}
615-
}
616-
}
542+
// Ensure agent is ready by handling all status and lifecycle states
543+
agent=awaitthis.ensureAgentReady(
544+
monitor,
545+
agent,
546+
workspaceName,
547+
workspaceClient,
548+
);
617549

618550
constlogDir=this.getLogDir(featureSet);
619551

@@ -740,6 +672,139 @@ export class Remote {
740672
return` --log-dir${escapeCommandArg(logDir)} -v`;
741673
}
742674

675+
/**
676+
* Ensures agent is ready to connect by handling all status and lifecycle states.
677+
* Throws an error if the agent cannot be made ready.
678+
*/
679+
privateasyncensureAgentReady(
680+
monitor:WorkspaceMonitor,
681+
agent:WorkspaceAgent,
682+
workspaceName:string,
683+
workspaceClient:CoderApi,
684+
):Promise<WorkspaceAgent>{
685+
letcurrentAgent=agent;
686+
687+
while(
688+
currentAgent.status!=="connected"||
689+
currentAgent.lifecycle_state!=="ready"
690+
){
691+
switch(currentAgent.status){
692+
case"connecting":
693+
this.logger.info(`Waiting for agent${currentAgent.name}...`);
694+
currentAgent=awaitthis.waitForAgentWithProgress(
695+
monitor,
696+
currentAgent,
697+
`Waiting for agent${currentAgent.name} to connect...`,
698+
(foundAgent)=>foundAgent.status!=="connecting",
699+
);
700+
this.logger.info(
701+
`Agent${currentAgent.name} status is now`,
702+
currentAgent.status,
703+
);
704+
continue;
705+
706+
case"connected":
707+
// Agent connected, now handle lifecycle state
708+
break;
709+
710+
case"disconnected":
711+
case"timeout":
712+
thrownewError(
713+
`${workspaceName}/${currentAgent.name}${currentAgent.status}`,
714+
);
715+
716+
default:
717+
thrownewError(
718+
`${workspaceName}/${currentAgent.name} unknown status:${currentAgent.status}`,
719+
);
720+
}
721+
722+
// Handle agent lifecycle state (only when status is "connected")
723+
switch(currentAgent.lifecycle_state){
724+
case"ready":
725+
returncurrentAgent;
726+
727+
case"starting":{
728+
constisBlocking=currentAgent.scripts.some(
729+
(script)=>script.start_blocks_login,
730+
);
731+
if(!isBlocking){
732+
returncurrentAgent;
733+
}
734+
735+
constlogMsg=`Waiting for agent${currentAgent.name} startup scripts...`;
736+
this.logger.info(logMsg);
737+
738+
letterminalSession:TerminalSession|undefined;
739+
letsocket:OneWayWebSocket<WorkspaceAgentLog[]>|undefined;
740+
try{
741+
terminalSession=newTerminalSession("Agent Log");
742+
const{socket:agentSocket,completion:logsCompletion}=
743+
awaitstreamAgentLogs(
744+
workspaceClient,
745+
terminalSession.writeEmitter,
746+
currentAgent,
747+
);
748+
socket=agentSocket;
749+
750+
constagentStatePromise=this.waitForAgentWithProgress(
751+
monitor,
752+
currentAgent,
753+
logMsg,
754+
(foundAgent)=>foundAgent.lifecycle_state!=="starting",
755+
);
756+
757+
// Race between logs completion and agent state change
758+
currentAgent=awaitPromise.race([
759+
agentStatePromise,
760+
logsCompletion.then(()=>agentStatePromise),
761+
]);
762+
this.logger.info(
763+
`Agent${currentAgent.name} lifecycle state is now`,
764+
currentAgent.lifecycle_state,
765+
);
766+
}finally{
767+
terminalSession?.dispose();
768+
socket?.close();
769+
}
770+
continue;
771+
}
772+
773+
case"created":
774+
this.logger.info(
775+
`Waiting for${workspaceName}/${currentAgent.name} to start...`,
776+
);
777+
currentAgent=awaitthis.waitForAgentWithProgress(
778+
monitor,
779+
currentAgent,
780+
`Waiting for agent${currentAgent.name} to start...`,
781+
(foundAgent)=>foundAgent.lifecycle_state!=="created",
782+
);
783+
this.logger.info(
784+
`Agent${currentAgent.name} lifecycle state is now`,
785+
currentAgent.lifecycle_state,
786+
);
787+
continue;
788+
789+
case"off":
790+
case"start_error":
791+
case"start_timeout":
792+
case"shutdown_error":
793+
case"shutdown_timeout":
794+
case"shutting_down":
795+
thrownewError(
796+
`${workspaceName}/${currentAgent.name} lifecycle state:${currentAgent.lifecycle_state}`,
797+
);
798+
799+
default:
800+
thrownewError(
801+
`${workspaceName}/${currentAgent.name} unknown lifecycle state:${currentAgent.lifecycle_state}`,
802+
);
803+
}
804+
}
805+
returncurrentAgent;
806+
}
807+
743808
/**
744809
* Waits for an agent condition with progress notification.
745810
*/
@@ -749,7 +814,7 @@ export class Remote {
749814
progressTitle:string,
750815
checker:(agent:WorkspaceAgent)=>boolean,
751816
):Promise<WorkspaceAgent>{
752-
constfoundAgent=awaitvscode.window.withProgress(
817+
constfoundAgent=awaitthis.vscodeProposed.window.withProgress(
753818
{
754819
title:progressTitle,
755820
location:vscode.ProgressLocation.Notification,
@@ -770,6 +835,10 @@ export class Remote {
770835
):Promise<WorkspaceAgent>{
771836
returnnewPromise<WorkspaceAgent>((resolve,reject)=>{
772837
constupdateEvent=monitor.onChange.event((workspace)=>{
838+
if(workspace.latest_build.status!=="running"){
839+
constworkspaceName=createWorkspaceIdentifier(workspace);
840+
reject(newError(`Workspace${workspaceName} is not running.`));
841+
}
773842
try{
774843
constagents=extractAgents(workspace.latest_build.resources);
775844
constfoundAgent=agents.find((a)=>a.id===agent.id);
@@ -778,8 +847,7 @@ export class Remote {
778847
`Agent${agent.name} not found in workspace resources`,
779848
);
780849
}
781-
constresult=checker(foundAgent);
782-
if(result!==undefined){
850+
if(checker(foundAgent)){
783851
updateEvent.dispose();
784852
resolve(foundAgent);
785853
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp