@@ -27,22 +27,28 @@ var ErrSessionNotFound = errors.New("not found")
2727
2828// session is an in-flight agent task
2929type session struct {
30- ID string `json:"id"`
31- Name string `json:"name"`
32- UserID int64 `json:"user_id"`
33- AgentID int64 `json:"agent_id"`
34- Logs string `json:"logs"`
35- State string `json:"state"`
36- OwnerID uint64 `json:"owner_id"`
37- RepoID uint64 `json:"repo_id"`
38- ResourceType string `json:"resource_type"`
39- ResourceID int64 `json:"resource_id"`
40- LastUpdatedAt time.Time `json:"last_updated_at,omitempty"`
41- CreatedAt time.Time `json:"created_at,omitempty"`
42- CompletedAt time.Time `json:"completed_at,omitempty"`
43- EventURL string `json:"event_url"`
44- EventType string `json:"event_type"`
45- PremiumRequests float64 `json:"premium_requests"`
30+ ID string `json:"id"`
31+ Name string `json:"name"`
32+ UserID int64 `json:"user_id"`
33+ AgentID int64 `json:"agent_id"`
34+ Logs string `json:"logs"`
35+ State string `json:"state"`
36+ OwnerID uint64 `json:"owner_id"`
37+ RepoID uint64 `json:"repo_id"`
38+ ResourceType string `json:"resource_type"`
39+ ResourceID int64 `json:"resource_id"`
40+ ResourceGlobalID string `json:"resource_global_id"`
41+ LastUpdatedAt time.Time `json:"last_updated_at,omitempty"`
42+ CreatedAt time.Time `json:"created_at,omitempty"`
43+ CompletedAt time.Time `json:"completed_at,omitempty"`
44+ EventURL string `json:"event_url"`
45+ EventType string `json:"event_type"`
46+ PremiumRequests float64 `json:"premium_requests"`
47+ WorkflowRunID uint64 `json:"workflow_run_id,omitempty"`
48+ Error * struct {
49+ Code string `json:"code"`
50+ Message string `json:"message"`
51+ } `json:"error,omitempty"`
4652}
4753
4854// A shim of a full pull request because looking up by node ID
@@ -83,11 +89,38 @@ type Session struct {
8389 EventURL string
8490 EventType string
8591 PremiumRequests float64
92+ WorkflowRunID uint64
93+ Error * SessionError
8694
8795 PullRequest * api.PullRequest
8896 User * api.GitHubUser
8997}
9098
99+ type SessionError struct {
100+ Code string
101+ Message string
102+ }
103+
104+ type resource struct {
105+ ID string `json:"id"`
106+ UserID uint64 `json:"user_id"`
107+ ResourceType string `json:"resource_type"`
108+ ResourceID int64 `json:"resource_id"`
109+ ResourceGlobalID string `json:"resource_global_id"`
110+ SessionCount int `json:"session_count"`
111+ SessionLastUpdatedAt int64 `json:"last_updated_at"`
112+ SessionState string `json:"state,omitempty"`
113+ ResourceState string `json:"resource_state"`
114+ Sessions []resourceSession `json:"sessions"`
115+ }
116+
117+ type resourceSession struct {
118+ SessionID string `json:"id"`
119+ Name string `json:"name"`
120+ SessionState string `json:"state,omitempty"`
121+ SessionLastUpdatedAt int64 `json:"last_updated_at"`
122+ }
123+
91124// ListLatestSessionsForViewer lists all agent sessions for the
92125// authenticated user up to limit.
93126func (c * CAPIClient ) ListLatestSessionsForViewer (ctx context.Context , limit int ) ([]* Session , error ) {
@@ -98,7 +131,6 @@ func (c *CAPIClient) ListLatestSessionsForViewer(ctx context.Context, limit int)
98131 url := baseCAPIURL + "/agents/sessions"
99132 pageSize := defaultSessionsPerPage
100133
101- sessions := make ([]session , 0 , limit + pageSize )
102134 seenResources := make (map [int64 ]struct {})
103135 latestSessions := make ([]session , 0 , limit )
104136 for page := 1 ; ; page ++ {
@@ -130,7 +162,6 @@ func (c *CAPIClient) ListLatestSessionsForViewer(ctx context.Context, limit int)
130162
131163 // Process only the newly fetched page worth of sessions.
132164 pageSessions := response .Sessions
133- sessions = append (sessions , pageSessions ... )
134165
135166 // De-duplicate sessions by resource ID.
136167 // Because the API returns newest first, once we've seen
@@ -248,46 +279,42 @@ func (c *CAPIClient) ListSessionsByResourceID(ctx context.Context, resourceType
248279 return nil , nil
249280 }
250281
251- url := fmt .Sprintf ("%s/agents/sessions/resource/%s/%d" , baseCAPIURL , url .PathEscape (resourceType ), resourceID )
252- pageSize := defaultSessionsPerPage
282+ url := fmt .Sprintf ("%s/agents/resource/%s/%d" , baseCAPIURL , url .PathEscape (resourceType ), resourceID )
253283
254- sessions := make ([]session , 0 , limit + pageSize )
284+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , http .NoBody )
285+ if err != nil {
286+ return nil , err
287+ }
255288
256- for page := 1 ; ; page ++ {
257- req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , http .NoBody )
258- if err != nil {
259- return nil , err
260- }
289+ res , err := c .httpClient .Do (req )
290+ if err != nil {
291+ return nil , err
292+ }
293+ defer res .Body .Close ()
294+ if res .StatusCode != http .StatusOK {
295+ return nil , fmt .Errorf ("failed to list sessions: %s" , res .Status )
296+ }
261297
262- q := req . URL . Query ()
263- q . Set ( "page_size" , strconv . Itoa ( pageSize ))
264- q . Set ( "page_number " , strconv . Itoa ( page ) )
265- req . URL . RawQuery = q . Encode ()
298+ var response resource
299+ if err := json . NewDecoder ( res . Body ). Decode ( & response ); err != nil {
300+ return nil , fmt . Errorf ( "failed to decode sessions response: %w " , err )
301+ }
266302
267- res , err := c .httpClient .Do (req )
268- if err != nil {
269- return nil , err
270- }
271- defer res .Body .Close ()
272- if res .StatusCode != http .StatusOK {
273- return nil , fmt .Errorf ("failed to list sessions: %s" , res .Status )
274- }
275- var response struct {
276- Sessions []session `json:"sessions"`
277- }
278- if err := json .NewDecoder (res .Body ).Decode (& response ); err != nil {
279- return nil , fmt .Errorf ("failed to decode sessions response: %w" , err )
303+ sessions := make ([]session , 0 , len (response .Sessions ))
304+ for _ , s := range response .Sessions {
305+ session := session {
306+ ID : s .SessionID ,
307+ Name : s .Name ,
308+ UserID : int64 (response .UserID ),
309+ ResourceType : response .ResourceType ,
310+ ResourceID : response .ResourceID ,
311+ ResourceGlobalID : response .ResourceGlobalID ,
312+ State : s .SessionState ,
280313 }
281-
282- sessions = append (sessions , response .Sessions ... )
283- if len (response .Sessions ) < pageSize || len (sessions ) >= limit {
284- break
314+ if s .SessionLastUpdatedAt != 0 {
315+ session .LastUpdatedAt = time .Unix (s .SessionLastUpdatedAt , 0 ).UTC ()
285316 }
286- }
287-
288- // Drop any above the limit
289- if len (sessions ) > limit {
290- sessions = sessions [:limit ]
317+ sessions = append (sessions , session )
291318 }
292319
293320 result , err := c .hydrateSessionPullRequestsAndUsers (sessions )
@@ -307,7 +334,12 @@ func (c *CAPIClient) hydrateSessionPullRequestsAndUsers(sessions []session) ([]*
307334 userNodeIds := make ([]string , 0 , len (sessions ))
308335 for _ , session := range sessions {
309336 if session .ResourceType == "pull" {
310- prNodeID := generatePullRequestNodeID (int64 (session .RepoID ), session .ResourceID )
337+ prNodeID := session .ResourceGlobalID
338+ // TODO: probably this can be dropped since the API should always
339+ // keep returning the resource global ID.
340+ if session .ResourceGlobalID == "" {
341+ prNodeID = generatePullRequestNodeID (int64 (session .RepoID ), session .ResourceID )
342+ }
311343 if ! slices .Contains (prNodeIds , prNodeID ) {
312344 prNodeIds = append (prNodeIds , prNodeID )
313345 }
@@ -442,7 +474,7 @@ func generateUserNodeID(userID int64) string {
442474}
443475
444476func fromAPISession (s session ) * Session {
445- return & Session {
477+ result := Session {
446478 ID : s .ID ,
447479 Name : s .Name ,
448480 UserID : s .UserID ,
@@ -459,5 +491,13 @@ func fromAPISession(s session) *Session {
459491 EventURL : s .EventURL ,
460492 EventType : s .EventType ,
461493 PremiumRequests : s .PremiumRequests ,
494+ WorkflowRunID : s .WorkflowRunID ,
495+ }
496+ if s .Error != nil {
497+ result .Error = & SessionError {
498+ Code : s .Error .Code ,
499+ Message : s .Error .Message ,
500+ }
462501 }
502+ return & result
463503}
0 commit comments