@@ -1964,18 +1964,41 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
19641964 }
19651965
19661966 appIDs := make ([]string , 0 )
1967+ agentIDByAppID := make (map [string ]uuid.UUID )
19671968 agentTimeouts := make (map [time.Duration ]bool ) // A set of agent timeouts.
19681969 // This could be a bulk insert to improve performance.
19691970 for _ , protoResource := range jobType .WorkspaceBuild .Resources {
1970- for _ , protoAgent := range protoResource .Agents {
1971+ for _ , protoAgent := range protoResource .GetAgents () {
1972+ if protoAgent == nil {
1973+ continue
1974+ }
1975+ // By default InsertWorkspaceResource ignores the protoAgent.Id
1976+ // and generates a new one, but we will insert these using the
1977+ // InsertWorkspaceResourceWithAgentIDsFromProto option so that
1978+ // we can properly map agent IDs to app IDs. This is needed for
1979+ // task linking.
1980+ agentID := uuid .New ()
1981+ protoAgent .Id = agentID .String ()
1982+
19711983 dur := time .Duration (protoAgent .GetConnectionTimeoutSeconds ()) * time .Second
19721984 agentTimeouts [dur ] = true
19731985 for _ , app := range protoAgent .GetApps () {
19741986 appIDs = append (appIDs , app .GetId ())
1987+ agentIDByAppID [app .GetId ()] = agentID
19751988 }
19761989 }
19771990
1978- err = InsertWorkspaceResource (ctx , db , job .ID , workspaceBuild .Transition , protoResource , telemetrySnapshot )
1991+ err = InsertWorkspaceResource (
1992+ ctx ,
1993+ db ,
1994+ job .ID ,
1995+ workspaceBuild .Transition ,
1996+ protoResource ,
1997+ telemetrySnapshot ,
1998+ // Ensure that the agent IDs we set previously
1999+ // are written to the database.
2000+ InsertWorkspaceResourceWithAgentIDsFromProto (),
2001+ )
19792002 if err != nil {
19802003 return xerrors .Errorf ("insert provisioner job: %w" , err )
19812004 }
@@ -1987,6 +2010,7 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
19872010 }
19882011
19892012 var taskAppID uuid.NullUUID
2013+ var taskAgentID uuid.NullUUID
19902014 var hasAITask bool
19912015 var warnUnknownTaskAppID bool
19922016 if tasks := jobType .WorkspaceBuild .GetAiTasks (); len (tasks ) > 0 {
@@ -2014,6 +2038,9 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
20142038 }
20152039
20162040 taskAppID = uuid.NullUUID {UUID : id , Valid : true }
2041+
2042+ agentID , ok := agentIDByAppID [appID ]
2043+ taskAgentID = uuid.NullUUID {UUID : agentID , Valid : ok }
20172044 }
20182045
20192046 // This is a hacky workaround for the issue with tasks 'disappearing' on stop:
@@ -2108,6 +2135,27 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
21082135 }
21092136 }
21102137
2138+ if task , err := db .GetTaskByWorkspaceID (ctx , workspace .ID ); err == nil {
2139+ // Irrespective of whether the agent or sidebar app is present,
2140+ // perform the upsert to ensure a link between the task and
2141+ // workspace build. Linking the task to the build is typically
2142+ // already established by wsbuilder.
2143+ _ , err = db .UpsertTaskWorkspaceApp (
2144+ ctx ,
2145+ database.UpsertTaskWorkspaceAppParams {
2146+ TaskID : task .ID ,
2147+ WorkspaceBuildNumber : workspaceBuild .BuildNumber ,
2148+ WorkspaceAgentID : taskAgentID ,
2149+ WorkspaceAppID : taskAppID ,
2150+ },
2151+ )
2152+ if err != nil {
2153+ return xerrors .Errorf ("upsert task workspace app: %w" , err )
2154+ }
2155+ } else if ! errors .Is (err , sql .ErrNoRows ) {
2156+ return xerrors .Errorf ("get task by workspace id: %w" , err )
2157+ }
2158+
21112159 // Regardless of whether there is an AI task or not, update the field to indicate one way or the other since it
21122160 // always defaults to nil. ONLY if has_ai_task=true MUST ai_task_sidebar_app_id be set.
21132161 if err := db .UpdateWorkspaceBuildFlagsByID (ctx , database.UpdateWorkspaceBuildFlagsByIDParams {
@@ -2578,7 +2626,28 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store,
25782626 return nil
25792627}
25802628
2581- func InsertWorkspaceResource (ctx context.Context , db database.Store , jobID uuid.UUID , transition database.WorkspaceTransition , protoResource * sdkproto.Resource , snapshot * telemetry.Snapshot ) error {
2629+ type insertWorkspaceResourceOptions struct {
2630+ useAgentIDsFromProto bool
2631+ }
2632+
2633+ // InsertWorkspaceResourceOption represents a functional option for
2634+ // InsertWorkspaceResource.
2635+ type InsertWorkspaceResourceOption func (* insertWorkspaceResourceOptions )
2636+
2637+ // InsertWorkspaceResourceWithAgentIDsFromProto allows inserting agents into the
2638+ // database using the agent IDs defined in the proto resource.
2639+ func InsertWorkspaceResourceWithAgentIDsFromProto () InsertWorkspaceResourceOption {
2640+ return func (opts * insertWorkspaceResourceOptions ) {
2641+ opts .useAgentIDsFromProto = true
2642+ }
2643+ }
2644+
2645+ func InsertWorkspaceResource (ctx context.Context , db database.Store , jobID uuid.UUID , transition database.WorkspaceTransition , protoResource * sdkproto.Resource , snapshot * telemetry.Snapshot , opt ... InsertWorkspaceResourceOption ) error {
2646+ opts := & insertWorkspaceResourceOptions {}
2647+ for _ , o := range opt {
2648+ o (opts )
2649+ }
2650+
25822651 resource , err := db .InsertWorkspaceResource (ctx , database.InsertWorkspaceResourceParams {
25832652 ID : uuid .New (),
25842653 CreatedAt : dbtime .Now (),
@@ -2675,6 +2744,12 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
26752744 }
26762745
26772746 agentID := uuid .New ()
2747+ if opts .useAgentIDsFromProto {
2748+ agentID , err = uuid .Parse (prAgent .Id )
2749+ if err != nil {
2750+ return xerrors .Errorf ("invalid agent ID format; must be uuid: %w" , err )
2751+ }
2752+ }
26782753 dbAgent , err := db .InsertWorkspaceAgent (ctx , database.InsertWorkspaceAgentParams {
26792754 ID : agentID ,
26802755 ParentID : uuid.NullUUID {},
0 commit comments