a2a-lite 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
a2a_lite/__init__.py CHANGED
@@ -102,7 +102,7 @@ from .auth import (
102
102
  require_auth,
103
103
  )
104
104
 
105
- __version__ = "0.2.0"
105
+ __version__ = "0.2.2"
106
106
 
107
107
  __all__ = [
108
108
  # Core
a2a_lite/agent.py CHANGED
@@ -170,11 +170,14 @@ class Agent:
170
170
  import typing
171
171
  from .tasks import TaskContext as _TaskContext
172
172
  from .human_loop import InteractionContext as _InteractionContext
173
+ from .auth import AuthResult as _AuthResult
173
174
 
174
175
  needs_task_context = False
175
176
  needs_interaction = False
177
+ needs_auth = False
176
178
  task_context_param: str | None = None
177
179
  interaction_param: str | None = None
180
+ auth_param: str | None = None
178
181
 
179
182
  try:
180
183
  resolved_hints = typing.get_type_hints(func)
@@ -190,6 +193,14 @@ class Agent:
190
193
  elif _is_or_subclass(hint, _InteractionContext):
191
194
  needs_interaction = True
192
195
  interaction_param = param_name
196
+ elif _is_or_subclass(hint, _AuthResult):
197
+ needs_auth = True
198
+ auth_param = param_name
199
+
200
+ # Also detect require_auth decorator
201
+ if getattr(func, '__requires_auth__', False) and not needs_auth:
202
+ needs_auth = True
203
+ auth_param = auth_param or "auth"
193
204
 
194
205
  # Extract schemas
195
206
  input_schema, output_schema = extract_function_schemas(func)
@@ -205,8 +216,10 @@ class Agent:
205
216
  is_streaming=is_streaming,
206
217
  needs_task_context=needs_task_context,
207
218
  needs_interaction=needs_interaction,
219
+ needs_auth=needs_auth,
208
220
  task_context_param=task_context_param,
209
221
  interaction_param=interaction_param,
222
+ auth_param=auth_param,
210
223
  )
211
224
 
212
225
  self._skills[skill_name] = skill_def
@@ -332,6 +345,10 @@ class Agent:
332
345
  task_store=self._task_store,
333
346
  )
334
347
 
348
+ # The SDK's InMemoryTaskStore handles protocol-level task lifecycle
349
+ # (task creation, state transitions per the A2A spec). This is separate
350
+ # from self._task_store which provides application-level tracking
351
+ # (progress updates, custom status) exposed via TaskContext to skills.
335
352
  request_handler = DefaultRequestHandler(
336
353
  agent_executor=executor,
337
354
  task_store=InMemoryTaskStore(),
@@ -379,11 +396,14 @@ class Agent:
379
396
  ))
380
397
 
381
398
  # Run startup hooks
382
- for hook in self._on_startup:
383
- if asyncio.iscoroutinefunction(hook):
384
- asyncio.get_event_loop().run_until_complete(hook())
385
- else:
386
- hook()
399
+ async def _run_startup():
400
+ for hook in self._on_startup:
401
+ if asyncio.iscoroutinefunction(hook):
402
+ await hook()
403
+ else:
404
+ hook()
405
+ if self._on_startup:
406
+ asyncio.run(_run_startup())
387
407
 
388
408
  # Enable discovery if requested
389
409
  if enable_discovery:
@@ -430,11 +450,14 @@ class Agent:
430
450
  )
431
451
  finally:
432
452
  # Run shutdown hooks
433
- for hook in self._on_shutdown:
434
- if asyncio.iscoroutinefunction(hook):
435
- asyncio.get_event_loop().run_until_complete(hook())
436
- else:
437
- hook()
453
+ async def _run_shutdown():
454
+ for hook in self._on_shutdown:
455
+ if asyncio.iscoroutinefunction(hook):
456
+ await hook()
457
+ else:
458
+ hook()
459
+ if self._on_shutdown:
460
+ asyncio.run(_run_shutdown())
438
461
 
439
462
  # Unregister discovery
440
463
  if self._discovery:
@@ -485,6 +508,7 @@ class Agent:
485
508
  task_store=self._task_store,
486
509
  )
487
510
 
511
+ # SDK task store for protocol-level lifecycle (separate from app-level self._task_store)
488
512
  request_handler = DefaultRequestHandler(
489
513
  agent_executor=executor,
490
514
  task_store=InMemoryTaskStore(),
a2a_lite/auth.py CHANGED
@@ -60,6 +60,17 @@ class AuthRequest:
60
60
  method: str = "POST"
61
61
  path: str = "/"
62
62
 
63
+ def get_header(self, name: str) -> Optional[str]:
64
+ """Get a header value (case-insensitive)."""
65
+ # Try exact match first, then case-insensitive
66
+ if name in self.headers:
67
+ return self.headers[name]
68
+ lower = name.lower()
69
+ for k, v in self.headers.items():
70
+ if k.lower() == lower:
71
+ return v
72
+ return None
73
+
63
74
 
64
75
  @dataclass
65
76
  class AuthResult:
@@ -128,8 +139,8 @@ class APIKeyAuth(AuthProvider):
128
139
  return hashlib.sha256(key.encode()).hexdigest()
129
140
 
130
141
  async def authenticate(self, request: AuthRequest) -> AuthResult:
131
- # Check header
132
- key = request.headers.get(self.header)
142
+ # Check header (case-insensitive)
143
+ key = request.get_header(self.header)
133
144
 
134
145
  # Check query param
135
146
  if not key and self.query_param:
@@ -179,7 +190,7 @@ class BearerAuth(AuthProvider):
179
190
  self.header = header
180
191
 
181
192
  async def authenticate(self, request: AuthRequest) -> AuthResult:
182
- auth_header = request.headers.get(self.header, "")
193
+ auth_header = request.get_header(self.header) or ""
183
194
 
184
195
  if not auth_header.startswith("Bearer "):
185
196
  return AuthResult.failure("Bearer token required")
@@ -211,7 +222,7 @@ class OAuth2Auth(AuthProvider):
211
222
  audience="my-agent",
212
223
  )
213
224
 
214
- Requires: pip install pyjwt[crypto]
225
+ Requires: pip install a2a-lite[oauth]
215
226
  """
216
227
 
217
228
  def __init__(
@@ -228,7 +239,7 @@ class OAuth2Auth(AuthProvider):
228
239
  self._jwks_client = None
229
240
 
230
241
  async def authenticate(self, request: AuthRequest) -> AuthResult:
231
- auth_header = request.headers.get("Authorization", "")
242
+ auth_header = request.get_header("Authorization") or ""
232
243
 
233
244
  if not auth_header.startswith("Bearer "):
234
245
  return AuthResult.failure("Bearer token required")
@@ -266,7 +277,7 @@ class OAuth2Auth(AuthProvider):
266
277
 
267
278
  except ImportError:
268
279
  return AuthResult.failure(
269
- "OAuth2 requires pyjwt: pip install pyjwt[crypto]"
280
+ "OAuth2 requires pyjwt: pip install a2a-lite[oauth]"
270
281
  )
271
282
  except Exception as e:
272
283
  return AuthResult.failure(f"Token validation failed: {str(e)}")
a2a_lite/cli.py CHANGED
@@ -75,7 +75,7 @@ version = "0.1.0"
75
75
  description = "A2A Agent: {name}"
76
76
  requires-python = ">=3.10"
77
77
  dependencies = [
78
- "a2a-lite>=0.2.0",
78
+ "a2a-lite>=0.2.2",
79
79
  ]
80
80
  '''
81
81
  (project_path / "pyproject.toml").write_text(pyproject)
a2a_lite/decorators.py CHANGED
@@ -18,8 +18,10 @@ class SkillDefinition:
18
18
  is_streaming: bool = False
19
19
  needs_task_context: bool = False
20
20
  needs_interaction: bool = False
21
+ needs_auth: bool = False
21
22
  task_context_param: Optional[str] = None
22
23
  interaction_param: Optional[str] = None
24
+ auth_param: Optional[str] = None
23
25
 
24
26
  def to_dict(self) -> Dict[str, Any]:
25
27
  """Convert to dictionary for serialization."""
a2a_lite/executor.py CHANGED
@@ -59,6 +59,23 @@ class LiteAgentExecutor(AgentExecutor):
59
59
  from a2a.utils import new_agent_text_message
60
60
 
61
61
  try:
62
+ # Authenticate the request (always run to produce auth_result for injection)
63
+ auth_result = None
64
+ if self.auth_provider:
65
+ from .auth import AuthRequest, NoAuth
66
+ headers = {}
67
+ if context.call_context and context.call_context.state:
68
+ headers = context.call_context.state.get('headers', {})
69
+ auth_request = AuthRequest(headers=headers)
70
+ auth_result = await self.auth_provider.authenticate(auth_request)
71
+ # Reject unauthenticated requests (unless NoAuth)
72
+ if not isinstance(self.auth_provider, NoAuth) and not auth_result.authenticated:
73
+ error_msg = json.dumps({
74
+ "error": auth_result.error or "Authentication failed",
75
+ })
76
+ await event_queue.enqueue_event(new_agent_text_message(error_msg))
77
+ return
78
+
62
79
  # Extract message and parts
63
80
  message, parts = self._extract_message_and_parts(context)
64
81
 
@@ -72,9 +89,10 @@ class LiteAgentExecutor(AgentExecutor):
72
89
  message=message,
73
90
  )
74
91
 
75
- # Store parts in metadata for skill access
92
+ # Store parts and auth result in metadata for skill access
76
93
  ctx.metadata["parts"] = parts
77
94
  ctx.metadata["event_queue"] = event_queue
95
+ ctx.metadata["auth_result"] = auth_result
78
96
 
79
97
  # Define final handler
80
98
  async def final_handler(ctx: MiddlewareContext) -> Any:
@@ -120,7 +138,14 @@ class LiteAgentExecutor(AgentExecutor):
120
138
  if skill_name is None:
121
139
  if not self.skills:
122
140
  return {"error": "No skills registered"}
123
- skill_name = list(self.skills.keys())[0]
141
+ # Only auto-select if there's exactly one skill
142
+ if len(self.skills) == 1:
143
+ skill_name = list(self.skills.keys())[0]
144
+ else:
145
+ return {
146
+ "error": "No skill specified. Use {\"skill\": \"name\", \"params\": {...}} format.",
147
+ "available_skills": list(self.skills.keys()),
148
+ }
124
149
 
125
150
  if skill_name not in self.skills:
126
151
  return {
@@ -150,6 +175,10 @@ class LiteAgentExecutor(AgentExecutor):
150
175
  param_name = skill_def.interaction_param or "ctx"
151
176
  params[param_name] = interaction_ctx
152
177
 
178
+ if skill_def.needs_auth:
179
+ param_name = skill_def.auth_param or "auth"
180
+ params[param_name] = metadata.get("auth_result")
181
+
153
182
  # Call the handler
154
183
  handler = skill_def.handler
155
184
 
@@ -167,11 +196,19 @@ class LiteAgentExecutor(AgentExecutor):
167
196
  metadata: Dict[str, Any],
168
197
  ) -> Dict[str, Any]:
169
198
  """Convert parameters to Pydantic models and file parts if needed."""
199
+ import typing
170
200
  handler = skill_def.handler
171
- hints = getattr(handler, '__annotations__', {})
201
+ try:
202
+ hints = typing.get_type_hints(handler)
203
+ except Exception:
204
+ hints = getattr(handler, '__annotations__', {})
205
+
206
+ from .parts import FilePart, DataPart
172
207
 
173
208
  converted = {}
174
209
  for param_name, value in params.items():
210
+ if param_name == 'return':
211
+ continue
175
212
  param_type = hints.get(param_name)
176
213
 
177
214
  if param_type is None:
@@ -181,13 +218,12 @@ class LiteAgentExecutor(AgentExecutor):
181
218
  # Skip special context types
182
219
  from .tasks import TaskContext as _TaskContext
183
220
  from .human_loop import InteractionContext as _InteractionContext
184
- if _is_or_subclass(param_type, _TaskContext) or _is_or_subclass(param_type, _InteractionContext):
221
+ from .auth import AuthResult as _AuthResult
222
+ if _is_or_subclass(param_type, _TaskContext) or _is_or_subclass(param_type, _InteractionContext) or _is_or_subclass(param_type, _AuthResult):
185
223
  continue
186
224
 
187
225
  # Convert FilePart
188
- type_name = str(param_type)
189
- if "FilePart" in type_name:
190
- from .parts import FilePart
226
+ if _is_or_subclass(param_type, FilePart):
191
227
  if isinstance(value, dict):
192
228
  # Handle both A2A format and simple dict format
193
229
  if "file" in value:
@@ -208,8 +244,7 @@ class LiteAgentExecutor(AgentExecutor):
208
244
  continue
209
245
 
210
246
  # Convert DataPart
211
- if "DataPart" in type_name:
212
- from .parts import DataPart
247
+ if _is_or_subclass(param_type, DataPart):
213
248
  if isinstance(value, dict):
214
249
  # Handle both A2A format and simple dict format
215
250
  if "type" in value and value.get("type") == "data":
@@ -317,7 +352,7 @@ class LiteAgentExecutor(AgentExecutor):
317
352
  if asyncio.iscoroutinefunction(handler):
318
353
  return await handler(*args, **kwargs)
319
354
  else:
320
- loop = asyncio.get_event_loop()
355
+ loop = asyncio.get_running_loop()
321
356
  return await loop.run_in_executor(
322
357
  None,
323
358
  lambda: handler(*args, **kwargs)
a2a_lite/middleware.py CHANGED
@@ -159,7 +159,12 @@ def retry_middleware(max_retries: int = 3, delay: float = 1.0):
159
159
 
160
160
  def rate_limit_middleware(requests_per_minute: int = 60):
161
161
  """
162
- Create a simple rate limiting middleware.
162
+ Create a simple in-process rate limiting middleware.
163
+
164
+ Note: This rate limiter is per-process. Under multi-worker uvicorn
165
+ (e.g., ``--workers 4``), each worker tracks limits independently.
166
+ For shared rate limiting across workers, use an external store
167
+ (Redis, etc.) and a custom middleware.
163
168
 
164
169
  Example:
165
170
  agent.add_middleware(rate_limit_middleware(requests_per_minute=100))
a2a_lite/tasks.py CHANGED
@@ -30,7 +30,7 @@ from __future__ import annotations
30
30
  import asyncio
31
31
  import logging
32
32
  from dataclasses import dataclass, field
33
- from datetime import datetime
33
+ from datetime import datetime, timezone
34
34
  from enum import Enum
35
35
  from typing import Any, Callable, Dict, List, Optional
36
36
  from uuid import uuid4
@@ -55,7 +55,7 @@ class TaskStatus:
55
55
  state: TaskState
56
56
  message: Optional[str] = None
57
57
  progress: Optional[float] = None # 0.0 to 1.0
58
- timestamp: datetime = field(default_factory=datetime.utcnow)
58
+ timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
59
59
 
60
60
  def to_dict(self) -> Dict[str, Any]:
61
61
  return {
@@ -77,8 +77,8 @@ class Task:
77
77
  error: Optional[str] = None
78
78
  artifacts: List[Any] = field(default_factory=list)
79
79
  history: List[TaskStatus] = field(default_factory=list)
80
- created_at: datetime = field(default_factory=datetime.utcnow)
81
- updated_at: datetime = field(default_factory=datetime.utcnow)
80
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
81
+ updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
82
82
 
83
83
  def update_status(
84
84
  self,
@@ -89,7 +89,7 @@ class Task:
89
89
  """Update task status."""
90
90
  self.history.append(self.status)
91
91
  self.status = TaskStatus(state=state, message=message, progress=progress)
92
- self.updated_at = datetime.utcnow()
92
+ self.updated_at = datetime.now(timezone.utc)
93
93
 
94
94
 
95
95
  class TaskContext:
a2a_lite/testing.py CHANGED
@@ -214,7 +214,16 @@ class AgentTestClient:
214
214
  result = await gen
215
215
  results.append(result)
216
216
 
217
- asyncio.get_event_loop().run_until_complete(run_handler())
217
+ # Handle both sync and async calling contexts
218
+ try:
219
+ asyncio.get_running_loop()
220
+ # Already in an async context — run in a separate thread
221
+ import concurrent.futures
222
+ with concurrent.futures.ThreadPoolExecutor(1) as pool:
223
+ pool.submit(asyncio.run, run_handler()).result()
224
+ except RuntimeError:
225
+ # No running loop — safe to use asyncio.run()
226
+ asyncio.run(run_handler())
218
227
  return results
219
228
 
220
229
 
a2a_lite/utils.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Helper functions for A2A Lite.
3
3
  """
4
+ import typing
4
5
  from typing import Any, Dict, Type, get_origin, get_args, Union
5
6
  import inspect
6
7
 
@@ -103,7 +104,10 @@ def extract_function_schemas(func) -> tuple[Dict[str, Any], Dict[str, Any]]:
103
104
  Tuple of (input_schema, output_schema)
104
105
  """
105
106
  sig = inspect.signature(func)
106
- hints = getattr(func, '__annotations__', {})
107
+ try:
108
+ hints = typing.get_type_hints(func)
109
+ except Exception:
110
+ hints = getattr(func, '__annotations__', {})
107
111
 
108
112
  # Build input schema from parameters
109
113
  properties = {}
a2a_lite/webhooks.py CHANGED
@@ -9,7 +9,7 @@ Simplifies sending notifications when tasks complete:
9
9
  """
10
10
  from __future__ import annotations
11
11
 
12
- from dataclasses import dataclass
12
+ from dataclasses import dataclass, field
13
13
  from typing import Any, Callable, Dict, List, Optional
14
14
  import asyncio
15
15
  import json
@@ -19,15 +19,11 @@ import json
19
19
  class WebhookConfig:
20
20
  """Configuration for a webhook endpoint."""
21
21
  url: str
22
- headers: Dict[str, str] = None
22
+ headers: Dict[str, str] = field(default_factory=dict)
23
23
  retry_count: int = 3
24
24
  retry_delay: float = 1.0
25
25
  timeout: float = 30.0
26
26
 
27
- def __post_init__(self):
28
- if self.headers is None:
29
- self.headers = {}
30
-
31
27
 
32
28
  class WebhookClient:
33
29
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: a2a-lite
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Simplified wrapper for Google's A2A Protocol SDK
5
5
  Author: A2A Lite Contributors
6
6
  License-Expression: Apache-2.0
@@ -27,6 +27,8 @@ Provides-Extra: dev
27
27
  Requires-Dist: httpx>=0.25; extra == 'dev'
28
28
  Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
29
29
  Requires-Dist: pytest>=7.0; extra == 'dev'
30
+ Provides-Extra: oauth
31
+ Requires-Dist: pyjwt[crypto]>=2.0; extra == 'oauth'
30
32
  Description-Content-Type: text/markdown
31
33
 
32
34
  # A2A Lite - Python
@@ -0,0 +1,19 @@
1
+ a2a_lite/__init__.py,sha256=-uZfl-CoWKt4xFSmda3TEBmPB7pRA71u9C4HkK8Pavs,3520
2
+ a2a_lite/agent.py,sha256=OrIzIaJLv7O55G1jrSl81pNem9S7kD47QDOzxISAmw0,17793
3
+ a2a_lite/auth.py,sha256=A1AMncM0cuWEAcysjumAhjd0lI_jMLmJpeYWuAPj30A,10181
4
+ a2a_lite/cli.py,sha256=chLmUrWPxXmXU75LO8yhN5HP0rct3yN8W_i_txTvoJA,9043
5
+ a2a_lite/decorators.py,sha256=RDekZdDJQR8124zX0lvTinaU7iJdNjNHaNPop17_gmg,1116
6
+ a2a_lite/discovery.py,sha256=BxpiJAUDxIyI2gvsLhjmHte5c9ax5Qf1hbBQnyAmxLQ,4508
7
+ a2a_lite/executor.py,sha256=gC_9mzoGalUJMZUio-SQeXcoZvOU4LR69eK9ikncJwQ,13628
8
+ a2a_lite/human_loop.py,sha256=XAqxp-k8I7TNyuLqqNmLEqABHqcAUiKYCL8n3W5StaY,8685
9
+ a2a_lite/middleware.py,sha256=c6jb9aFfyTf-JY6KjqaSgFJmpzqbHLC6Q1h9NNteqzo,5545
10
+ a2a_lite/parts.py,sha256=qVRiD-H9_NlMPk-R0gTUiGVQ77E2poiuBWAUyAyAoTI,6177
11
+ a2a_lite/streaming.py,sha256=RFv9EJYnhwkT0h1Wovkj4EXwFzCgHdaA-h7WpPaaONo,2329
12
+ a2a_lite/tasks.py,sha256=UpmDP-VGIQ1LodBNq4zx2pJElQ31gOJOAduHFBVyxOA,7039
13
+ a2a_lite/testing.py,sha256=M9IbLA6oUz1DokJ9Sc_r0gK43NNkU78IVkiBRuDFFCU,9393
14
+ a2a_lite/utils.py,sha256=AFLYQ4J-F7H_HeYWAeg8H3p9EOdDv4dOpju_ebrU5PI,3934
15
+ a2a_lite/webhooks.py,sha256=TNhDlG84rrP_gC2pQ-op5xo01p5Z9sm_ZgQIrJRI7OY,6095
16
+ a2a_lite-0.2.2.dist-info/METADATA,sha256=o74kDuPcSpG1A82qVROqwqMMTTvR95vigXwtx64OeCU,12657
17
+ a2a_lite-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
+ a2a_lite-0.2.2.dist-info/entry_points.txt,sha256=BONfFqZbCntNal2iwlTJAE09gCUvurfvqslMYVYh4is,46
19
+ a2a_lite-0.2.2.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- a2a_lite/__init__.py,sha256=HfTauky7g1ugDu1OQO65qcCi8bzbHeSaAK_k6ENrB8k,3520
2
- a2a_lite/agent.py,sha256=ZdRGi1w1VYDUd7DxlGneZ_1Y5JSAIyur_zU6RZIrmp0,16647
3
- a2a_lite/auth.py,sha256=vRAYOj-4VOiaXQ_-hKk8YFkideyU5b8JFj2lEp-rW8s,9772
4
- a2a_lite/cli.py,sha256=beffnCU-FJR7kVCCJBwHYazUQzwi4g7HiSD20lgrpAM,9043
5
- a2a_lite/decorators.py,sha256=VdinkddmF61IS8TkjdSfHGdn3nDKwepcIjWZPZBKg3w,1050
6
- a2a_lite/discovery.py,sha256=BxpiJAUDxIyI2gvsLhjmHte5c9ax5Qf1hbBQnyAmxLQ,4508
7
- a2a_lite/executor.py,sha256=tNtrIbwAImadMyJFtrat1iaXkqrACckrqjt3x39IPOc,11891
8
- a2a_lite/human_loop.py,sha256=XAqxp-k8I7TNyuLqqNmLEqABHqcAUiKYCL8n3W5StaY,8685
9
- a2a_lite/middleware.py,sha256=ciR2z5y85uYIc4YFGNTR2litjRehuU7jsohYMJDGHkI,5282
10
- a2a_lite/parts.py,sha256=qVRiD-H9_NlMPk-R0gTUiGVQ77E2poiuBWAUyAyAoTI,6177
11
- a2a_lite/streaming.py,sha256=RFv9EJYnhwkT0h1Wovkj4EXwFzCgHdaA-h7WpPaaONo,2329
12
- a2a_lite/tasks.py,sha256=VWHhRwi4ajrr4vMNSJVpFjSUqMOdAPM-PnVUeCnYbww,6963
13
- a2a_lite/testing.py,sha256=kmQGkFucUAv2zPkrko0ZWn7S1q3bkz0v7FXpO6J47mU,8985
14
- a2a_lite/utils.py,sha256=CnkO6HH9oKEXymbJG2ohdt1ESxldQ3fkmcYVO-o9R_k,3841
15
- a2a_lite/webhooks.py,sha256=t6ebT3jVBEKFpjhBnPI-nuQWIUKQUbJm24phXOBnNKA,6158
16
- a2a_lite-0.2.0.dist-info/METADATA,sha256=CR6_O8Uu-M8whAcFH9bGetWql2ZzmSRZwFCncjIg2tU,12583
17
- a2a_lite-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
- a2a_lite-0.2.0.dist-info/entry_points.txt,sha256=BONfFqZbCntNal2iwlTJAE09gCUvurfvqslMYVYh4is,46
19
- a2a_lite-0.2.0.dist-info/RECORD,,