1919import jwt
2020import dataclasses
2121from dataclasses import dataclass
22- from typing import Optional , List , Literal
22+ from typing import Optional , List , Literal , Type , TypeVar
2323from google .protobuf .json_format import MessageToDict , ParseDict
2424
2525from livekit .protocol .room import RoomConfiguration
@@ -92,6 +92,43 @@ class ObservabilityGrants:
9292 write : bool = False
9393
9494
95+ @dataclass
96+ class AgentGrants :
97+ # create/update/delete Cloud Agents
98+ admin : bool = False
99+ # manage simulations and scenarios for evaluating agents
100+ simulation_admin : bool = False
101+ # access the project's agent databases
102+ database_admin : bool = False
103+ # manage the project's agent dispatch queue
104+ dispatch_admin : bool = False
105+
106+
107+ @dataclass
108+ class AgentEndpointGrants :
109+ # invoke an agent's non-public HTTP endpoints
110+ call : bool = False
111+ # restrict to one agent; empty grants every agent in the project
112+ agent_name : str = ""
113+ # restrict to one deployment; empty grants every deployment
114+ deployment : str = ""
115+
116+
117+ @dataclass
118+ class AgentThreadGrants :
119+ # every thread and scope of the project; the action flags still apply
120+ all : bool = False
121+ # scope in which threads may be listed and created
122+ scope : str = ""
123+ # threads that may be read, written and deleted
124+ thread_ids : Optional [List [str ]] = None
125+ list : bool = False
126+ create : bool = False
127+ write : bool = False
128+ read : bool = False
129+ delete : bool = False
130+
131+
95132@dataclass
96133class Claims :
97134 identity : str = ""
@@ -102,6 +139,9 @@ class Claims:
102139 sip : Optional [SIPGrants ] = None
103140 inference : Optional [InferenceGrants ] = None
104141 observability : Optional [ObservabilityGrants ] = None
142+ agent : Optional [AgentGrants ] = None
143+ agent_endpoint : Optional [AgentEndpointGrants ] = None
144+ agent_thread : Optional [AgentThreadGrants ] = None
105145 attributes : Optional [dict [str , str ]] = None
106146 sha256 : Optional [str ] = None
107147 room_preset : Optional [str ] = None
@@ -162,6 +202,18 @@ def with_inference_grants(self, grants: InferenceGrants) -> "AccessToken":
162202 self .claims .inference = grants
163203 return self
164204
205+ def with_agent_grants (self , grants : AgentGrants ) -> "AccessToken" :
206+ self .claims .agent = grants
207+ return self
208+
209+ def with_agent_endpoint_grants (self , grants : AgentEndpointGrants ) -> "AccessToken" :
210+ self .claims .agent_endpoint = grants
211+ return self
212+
213+ def with_agent_thread_grants (self , grants : AgentThreadGrants ) -> "AccessToken" :
214+ self .claims .agent_thread = grants
215+ return self
216+
165217 def with_identity (self , identity : str ) -> "AccessToken" :
166218 self .identity = identity
167219 return self
@@ -279,6 +331,9 @@ def verify(self, token: str, *, verify_signature: bool = True) -> Claims:
279331 attributes = claims .get ("attributes" , {}),
280332 metadata = claims .get ("metadata" , "" ),
281333 sha256 = claims .get ("sha256" , "" ),
334+ agent = _parse_grants (claims , "agent" , AgentGrants ),
335+ agent_endpoint = _parse_grants (claims , "agentEndpoint" , AgentEndpointGrants ),
336+ agent_thread = _parse_grants (claims , "agentThread" , AgentThreadGrants ),
282337 )
283338
284339 if claims .get ("roomPreset" ):
@@ -294,6 +349,17 @@ def verify(self, token: str, *, verify_signature: bool = True) -> Claims:
294349 return grant_claims
295350
296351
352+ _G = TypeVar ("_G" )
353+
354+
355+ def _parse_grants (claims : dict , key : str , cls : Type [_G ]) -> Optional [_G ]:
356+ grants = claims .get (key )
357+ if grants is None :
358+ return None
359+ fields = cls .__dataclass_fields__ # type: ignore[attr-defined]
360+ return cls (** {camel_to_snake (k ): v for k , v in grants .items () if camel_to_snake (k ) in fields })
361+
362+
297363def camel_to_snake (t : str ) -> str :
298364 return re .sub (r"(?<!^)(?=[A-Z])" , "_" , t ).lower ()
299365
0 commit comments