@@ -72,16 +72,17 @@ type API struct {
7272configFileModifiedTimes map [string ]time.Time // By config file path.
7373recreateSuccessTimes map [string ]time.Time // By workspace folder.
7474recreateErrorTimes map [string ]time.Time // By workspace folder.
75- injectedSubAgentProcs map [string ]subAgentProcess // Bycontainer ID .
75+ injectedSubAgentProcs map [string ]subAgentProcess // Byworkspace folder .
7676asyncWg sync.WaitGroup
7777
7878devcontainerLogSourceIDs map [string ]uuid.UUID // By workspace folder.
7979}
8080
8181type subAgentProcess struct {
82- agent SubAgent
83- ctx context.Context
84- stop context.CancelFunc
82+ agent SubAgent
83+ containerID string
84+ ctx context.Context
85+ stop context.CancelFunc
8586}
8687
8788// Option is a functional option for API.
@@ -586,7 +587,11 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
586587dc .Dirty = true
587588}
588589
589- if _ ,injected := api .injectedSubAgentProcs [dc .Container .ID ];! injected && dc .Status == codersdk .WorkspaceAgentDevcontainerStatusRunning {
590+ proc ,injected := api .injectedSubAgentProcs [dc .WorkspaceFolder ]
591+ if injected && proc .containerID != dc .Container .ID {
592+ injected = false // The container ID changed, we need to re-inject.
593+ }
594+ if ! injected && dc .Status == codersdk .WorkspaceAgentDevcontainerStatusRunning {
590595err := api .injectSubAgentIntoContainerLocked (ctx ,dc )
591596if err != nil {
592597logger .Error (ctx ,"inject subagent into container failed" ,slog .Error (err ))
@@ -660,10 +665,22 @@ func (api *API) getContainers() (codersdk.WorkspaceAgentListContainersResponse,
660665if len (api .knownDevcontainers )> 0 {
661666devcontainers = make ([]codersdk.WorkspaceAgentDevcontainer ,0 ,len (api .knownDevcontainers ))
662667for _ ,dc := range api .knownDevcontainers {
668+ // Include the agent if it's been created (we're iterating over
669+ // copies, so mutating is fine).
670+ if dc .Container != nil {
671+ if proc := api .injectedSubAgentProcs [dc .WorkspaceFolder ];proc .agent .ID != uuid .Nil && proc .containerID == dc .Container .ID {
672+ dc .Agent = & codersdk.WorkspaceAgentDevcontainerAgent {
673+ ID :proc .agent .ID ,
674+ Name :proc .agent .Name ,
675+ Directory :proc .agent .Directory ,
676+ }
677+ }
678+ }
679+
663680devcontainers = append (devcontainers ,dc )
664681}
665682slices .SortFunc (devcontainers ,func (a ,b codersdk.WorkspaceAgentDevcontainer )int {
666- return strings .Compare (a .ID . String () ,b .ID . String () )
683+ return strings .Compare (a .Name ,b .Name )
667684})
668685}
669686
@@ -975,9 +992,25 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
975992return xerrors .New ("container is nil, cannot inject subagent" )
976993}
977994
995+ logger := api .logger .With (
996+ slog .F ("devcontainer_id" ,dc .ID ),
997+ slog .F ("devcontainer_name" ,dc .Name ),
998+ slog .F ("workspace_folder" ,dc .WorkspaceFolder ),
999+ slog .F ("config_path" ,dc .ConfigPath ),
1000+ slog .F ("container_id" ,container .ID ),
1001+ slog .F ("container_name" ,container .FriendlyName ),
1002+ )
1003+
9781004// Skip if subagent already exists for this container.
979- if _ ,injected := api .injectedSubAgentProcs [container .ID ];injected || api .closed {
980- return nil
1005+ if proc ,injected := api .injectedSubAgentProcs [dc .WorkspaceFolder ];injected || api .closed {
1006+ if proc .containerID == container .ID {
1007+ return nil
1008+ }
1009+
1010+ // If the subagent is already injected but the container ID has
1011+ // changed, we need to inject it into the new container.
1012+ logger .Debug (ctx ,"injecting subagent into new container" )
1013+ proc .stop ()
9811014}
9821015
9831016// Mark subagent as being injected immediately with a placeholder.
@@ -1010,13 +1043,6 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
10101043api .mu .Unlock ()
10111044defer api .mu .Lock ()// Re-lock.
10121045
1013- logger := api .logger .With (
1014- slog .F ("devcontainer_id" ,dc .ID ),
1015- slog .F ("devcontainer_name" ,dc .Name ),
1016- slog .F ("workspace_folder" ,dc .WorkspaceFolder ),
1017- slog .F ("config_path" ,dc .ConfigPath ),
1018- )
1019-
10201046arch ,err := api .ccli .DetectArchitecture (ctx ,container .ID )
10211047if err != nil {
10221048return xerrors .Errorf ("detect architecture: %w" ,err )
@@ -1176,7 +1202,9 @@ func (api *API) runSubAgentInContainer(ctx context.Context, dc codersdk.Workspac
11761202}
11771203
11781204api .mu .Lock ()
1179- delete (api .injectedSubAgentProcs ,container .ID )
1205+ if api .injectedSubAgentProcs [dc .WorkspaceFolder ].containerID == container .ID {
1206+ delete (api .injectedSubAgentProcs ,dc .WorkspaceFolder )
1207+ }
11801208api .mu .Unlock ()
11811209
11821210logger .Debug (ctx ,"agent process cleanup complete" )
@@ -1191,10 +1219,11 @@ func (api *API) runSubAgentInContainer(ctx context.Context, dc codersdk.Workspac
11911219return
11921220}
11931221// Update the placeholder with a valid subagent, context and stop.
1194- api .injectedSubAgentProcs [container .ID ]= subAgentProcess {
1195- agent :agent ,
1196- ctx :agentCtx ,
1197- stop :agentStop ,
1222+ api .injectedSubAgentProcs [dc .WorkspaceFolder ]= subAgentProcess {
1223+ agent :agent ,
1224+ containerID :container .ID ,
1225+ ctx :agentCtx ,
1226+ stop :agentStop ,
11981227}
11991228api .mu .Unlock ()
12001229
@@ -1226,7 +1255,11 @@ func (api *API) Close() error {
12261255api .closed = true
12271256
12281257for _ ,proc := range api .injectedSubAgentProcs {
1229- api .logger .Debug (api .ctx ,"canceling subagent process" ,slog .F ("agent_name" ,proc .agent .Name ),slog .F ("agent_id" ,proc .agent .ID ))
1258+ api .logger .Debug (api .ctx ,"canceling subagent process" ,
1259+ slog .F ("agent_name" ,proc .agent .Name ),
1260+ slog .F ("agent_id" ,proc .agent .ID ),
1261+ slog .F ("container_id" ,proc .containerID ),
1262+ )
12301263proc .stop ()
12311264}
12321265