@@ -622,6 +622,163 @@ async def update_session(
622622 except ValueError as e :
623623 return {"updated" : False , "message" : f"Failed to update session: { str (e )} " }
624624
625+ # ── Scheduled Sessions ──────────────────────────────────────────────
626+
627+ async def list_scheduled_sessions (self , project : str ) -> dict [str , Any ]:
628+ """List all scheduled sessions."""
629+ self ._validate_input (project , "project" )
630+ response = await self ._request ("GET" , "/v1/scheduled-sessions" , project )
631+ items = response .get ("items" , [])
632+ return {"scheduled_sessions" : items , "total" : len (items )}
633+
634+ async def get_scheduled_session (self , project : str , name : str ) -> dict [str , Any ]:
635+ """Get a specific scheduled session by name."""
636+ self ._validate_input (project , "project" )
637+ self ._validate_input (name , "name" )
638+ return await self ._request ("GET" , f"/v1/scheduled-sessions/{ name } " , project )
639+
640+ async def create_scheduled_session (
641+ self ,
642+ project : str ,
643+ schedule : str ,
644+ session_template : dict [str , Any ],
645+ display_name : str | None = None ,
646+ suspend : bool = False ,
647+ dry_run : bool = False ,
648+ ) -> dict [str , Any ]:
649+ """Create a scheduled session backed by a Kubernetes CronJob."""
650+ self ._validate_input (project , "project" )
651+
652+ payload : dict [str , Any ] = {
653+ "schedule" : schedule ,
654+ "sessionTemplate" : session_template ,
655+ "suspend" : suspend ,
656+ }
657+ if display_name :
658+ payload ["displayName" ] = display_name
659+
660+ if dry_run :
661+ return {
662+ "dry_run" : True ,
663+ "success" : True ,
664+ "message" : f"Would create scheduled session with schedule '{ schedule } '" ,
665+ "manifest" : payload ,
666+ "project" : project ,
667+ }
668+
669+ try :
670+ result = await self ._request ("POST" , "/v1/scheduled-sessions" , project , json_data = payload )
671+ name = result .get ("name" , "unknown" )
672+ return {
673+ "created" : True ,
674+ "name" : name ,
675+ "project" : project ,
676+ "message" : f"Scheduled session '{ name } ' created with schedule '{ schedule } '" ,
677+ }
678+ except (ValueError , TimeoutError ) as e :
679+ return {"created" : False , "message" : str (e )}
680+
681+ async def update_scheduled_session (
682+ self ,
683+ project : str ,
684+ name : str ,
685+ schedule : str | None = None ,
686+ display_name : str | None = None ,
687+ session_template : dict [str , Any ] | None = None ,
688+ suspend : bool | None = None ,
689+ dry_run : bool = False ,
690+ ) -> dict [str , Any ]:
691+ """Update a scheduled session (partial update)."""
692+ self ._validate_input (project , "project" )
693+ self ._validate_input (name , "name" )
694+
695+ payload : dict [str , Any ] = {}
696+ if schedule is not None :
697+ payload ["schedule" ] = schedule
698+ if display_name is not None :
699+ payload ["displayName" ] = display_name
700+ if session_template is not None :
701+ payload ["sessionTemplate" ] = session_template
702+ if suspend is not None :
703+ payload ["suspend" ] = suspend
704+
705+ if not payload :
706+ raise ValueError ("No fields to update. Provide schedule, display_name, session_template, or suspend." )
707+
708+ if dry_run :
709+ return {
710+ "dry_run" : True ,
711+ "success" : True ,
712+ "message" : f"Would update scheduled session '{ name } '" ,
713+ "patch" : payload ,
714+ }
715+
716+ try :
717+ await self ._request ("PUT" , f"/v1/scheduled-sessions/{ name } " , project , json_data = payload )
718+ return {"updated" : True , "message" : f"Successfully updated scheduled session '{ name } '" }
719+ except ValueError as e :
720+ return {"updated" : False , "message" : f"Failed to update: { str (e )} " }
721+
722+ async def delete_scheduled_session (self , project : str , name : str , dry_run : bool = False ) -> dict [str , Any ]:
723+ """Delete a scheduled session."""
724+ self ._validate_input (project , "project" )
725+ self ._validate_input (name , "name" )
726+
727+ if dry_run :
728+ try :
729+ data = await self ._request ("GET" , f"/v1/scheduled-sessions/{ name } " , project )
730+ return {
731+ "dry_run" : True ,
732+ "success" : True ,
733+ "message" : f"Would delete scheduled session '{ name } '" ,
734+ "session_info" : {
735+ "name" : data .get ("name" ),
736+ "schedule" : data .get ("schedule" ),
737+ "suspend" : data .get ("suspend" ),
738+ },
739+ }
740+ except ValueError :
741+ return {"dry_run" : True , "success" : False , "message" : f"Scheduled session '{ name } ' not found" }
742+
743+ try :
744+ await self ._request ("DELETE" , f"/v1/scheduled-sessions/{ name } " , project )
745+ return {"deleted" : True , "message" : f"Successfully deleted scheduled session '{ name } '" }
746+ except ValueError as e :
747+ return {"deleted" : False , "message" : f"Failed to delete: { str (e )} " }
748+
749+ async def suspend_scheduled_session (self , project : str , name : str ) -> dict [str , Any ]:
750+ """Suspend (pause) a scheduled session."""
751+ self ._validate_input (project , "project" )
752+ self ._validate_input (name , "name" )
753+
754+ await self ._request ("POST" , f"/v1/scheduled-sessions/{ name } /suspend" , project )
755+ return {"suspended" : True , "message" : f"Scheduled session '{ name } ' suspended" }
756+
757+ async def resume_scheduled_session (self , project : str , name : str ) -> dict [str , Any ]:
758+ """Resume a suspended scheduled session."""
759+ self ._validate_input (project , "project" )
760+ self ._validate_input (name , "name" )
761+
762+ await self ._request ("POST" , f"/v1/scheduled-sessions/{ name } /resume" , project )
763+ return {"resumed" : True , "message" : f"Scheduled session '{ name } ' resumed" }
764+
765+ async def trigger_scheduled_session (self , project : str , name : str ) -> dict [str , Any ]:
766+ """Manually trigger a scheduled session to run immediately."""
767+ self ._validate_input (project , "project" )
768+ self ._validate_input (name , "name" )
769+
770+ await self ._request ("POST" , f"/v1/scheduled-sessions/{ name } /trigger" , project )
771+ return {"triggered" : True , "message" : f"Scheduled session '{ name } ' triggered" }
772+
773+ async def list_scheduled_session_runs (self , project : str , name : str ) -> dict [str , Any ]:
774+ """List past runs (AgenticSessions) created by a scheduled session."""
775+ self ._validate_input (project , "project" )
776+ self ._validate_input (name , "name" )
777+
778+ response = await self ._request ("GET" , f"/v1/scheduled-sessions/{ name } /runs" , project )
779+ items = response .get ("items" , [])
780+ return {"runs" : items , "total" : len (items ), "scheduled_session" : name }
781+
625782 # ── Observability ────────────────────────────────────────────────────
626783
627784 async def get_session_logs (
@@ -676,6 +833,14 @@ async def get_session_metrics(self, project: str, session: str) -> dict[str, Any
676833 result ["session" ] = session
677834 return result
678835
836+ async def export_session (self , project : str , session : str ) -> dict [str , Any ]:
837+ """Export session chat as markdown."""
838+ self ._validate_input (project , "project" )
839+ self ._validate_input (session , "session" )
840+
841+ text = await self ._request_text ("GET" , f"/v1/sessions/{ session } /export" , project )
842+ return {"export" : text , "session" : session }
843+
679844 # ── Labels ───────────────────────────────────────────────────────────
680845
681846 async def label_session (self , project : str , session : str , labels : dict [str , str ]) -> dict [str , Any ]:
@@ -874,6 +1039,53 @@ async def bulk_restart_sessions_by_label(
8741039 """Restart sessions matching label selectors (max 3 matches)."""
8751040 return await self ._run_bulk_by_label (project , labels , self .restart_session , "restart" , "restarted" , dry_run )
8761041
1042+ # ── Workflow Management ─────────────────────────────────────────────
1043+
1044+ async def set_workflow (self , project : str , session : str , workflow : str ) -> dict [str , Any ]:
1045+ """Set the active workflow on a session."""
1046+ self ._validate_input (project , "project" )
1047+ self ._validate_input (session , "session" )
1048+
1049+ return await self ._request (
1050+ "POST" , f"/v1/sessions/{ session } /workflow" , project , json_data = {"workflow" : workflow }
1051+ )
1052+
1053+ async def get_workflow_metadata (self , project : str , session : str ) -> dict [str , Any ]:
1054+ """Get workflow metadata for a session."""
1055+ self ._validate_input (project , "project" )
1056+ self ._validate_input (session , "session" )
1057+
1058+ result = await self ._request ("GET" , f"/v1/sessions/{ session } /workflow/metadata" , project )
1059+ result ["session" ] = session
1060+ return result
1061+
1062+ # ── Repo Management ─────────────────────────────────────────────────
1063+
1064+ async def add_repo (self , project : str , session : str , repo_url : str ) -> dict [str , Any ]:
1065+ """Add a repository to a running session."""
1066+ self ._validate_input (project , "project" )
1067+ self ._validate_input (session , "session" )
1068+
1069+ return await self ._request ("POST" , f"/v1/sessions/{ session } /repos" , project , json_data = {"url" : repo_url })
1070+
1071+ async def remove_repo (self , project : str , session : str , repo_name : str ) -> dict [str , Any ]:
1072+ """Remove a repository from a session."""
1073+ self ._validate_input (project , "project" )
1074+ self ._validate_input (session , "session" )
1075+ self ._validate_input (repo_name , "repo_name" )
1076+
1077+ await self ._request ("DELETE" , f"/v1/sessions/{ session } /repos/{ repo_name } " , project )
1078+ return {"removed" : True , "message" : f"Repo '{ repo_name } ' removed from session '{ session } '" }
1079+
1080+ async def get_repos_status (self , project : str , session : str ) -> dict [str , Any ]:
1081+ """Get repository clone status for a session."""
1082+ self ._validate_input (project , "project" )
1083+ self ._validate_input (session , "session" )
1084+
1085+ result = await self ._request ("GET" , f"/v1/sessions/{ session } /repos/status" , project )
1086+ result ["session" ] = session
1087+ return result
1088+
8771089 # ── Cluster & auth ───────────────────────────────────────────────────
8781090
8791091 def list_clusters (self ) -> dict [str , Any ]:
0 commit comments