solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.1__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.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (184) hide show
  1. solace_agent_mesh/agent/adk/callbacks.py +0 -5
  2. solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
  3. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
  4. solace_agent_mesh/agent/protocol/event_handlers.py +213 -31
  5. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  6. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  7. solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
  8. solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
  9. solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
  10. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  11. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  12. solace_agent_mesh/agent/proxies/base/app.py +99 -0
  13. solace_agent_mesh/agent/proxies/base/component.py +650 -0
  14. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  15. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
  16. solace_agent_mesh/agent/sac/app.py +58 -5
  17. solace_agent_mesh/agent/sac/component.py +238 -75
  18. solace_agent_mesh/agent/sac/task_execution_context.py +46 -0
  19. solace_agent_mesh/agent/tools/audio_tools.py +125 -8
  20. solace_agent_mesh/agent/tools/web_tools.py +10 -5
  21. solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
  22. solace_agent_mesh/assets/docs/404.html +3 -3
  23. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
  30. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
  32. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/{e3d9abda.2b916f9e.js → e3d9abda.6b9493d0.js} +1 -1
  35. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
  37. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.b12eac43.js} +2 -2
  39. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +1 -0
  40. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +262 -0
  52. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +5 -5
  58. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
  59. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  61. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
  65. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
  66. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
  67. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
  69. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
  70. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
  71. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
  72. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  73. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  74. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
  75. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  76. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  77. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  78. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  79. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  80. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +6 -5
  81. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  82. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  83. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +100 -3
  84. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  85. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  86. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  87. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +1 -0
  88. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  89. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +1 -0
  90. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  91. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  92. solace_agent_mesh/cli/__init__.py +1 -1
  93. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +2 -69
  94. solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
  95. solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
  96. solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
  97. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
  98. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
  99. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
  100. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
  101. solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
  102. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +342 -0
  103. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
  104. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.js} +1 -1
  105. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  106. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  107. solace_agent_mesh/common/a2a/__init__.py +24 -0
  108. solace_agent_mesh/common/a2a/artifact.py +39 -0
  109. solace_agent_mesh/common/a2a/events.py +29 -0
  110. solace_agent_mesh/common/a2a/message.py +68 -0
  111. solace_agent_mesh/common/a2a/protocol.py +151 -1
  112. solace_agent_mesh/common/agent_registry.py +83 -3
  113. solace_agent_mesh/common/constants.py +3 -1
  114. solace_agent_mesh/common/sac/sam_component_base.py +383 -4
  115. solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
  116. solace_agent_mesh/config_portal/backend/common.py +1 -1
  117. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
  118. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
  119. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  120. solace_agent_mesh/evaluation/evaluator.py +128 -104
  121. solace_agent_mesh/evaluation/message_organizer.py +116 -110
  122. solace_agent_mesh/evaluation/report_data_processor.py +84 -86
  123. solace_agent_mesh/evaluation/report_generator.py +73 -79
  124. solace_agent_mesh/evaluation/run.py +421 -235
  125. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  126. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  127. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  128. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  129. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  130. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  131. solace_agent_mesh/evaluation/subscriber.py +111 -232
  132. solace_agent_mesh/evaluation/summary_builder.py +227 -117
  133. solace_agent_mesh/gateway/base/app.py +16 -1
  134. solace_agent_mesh/gateway/base/component.py +112 -39
  135. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  136. solace_agent_mesh/gateway/http_sse/component.py +99 -3
  137. solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
  138. solace_agent_mesh/gateway/http_sse/main.py +1 -0
  139. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
  140. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
  141. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
  142. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
  143. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
  144. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
  145. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
  146. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
  147. solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
  148. solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
  149. solace_agent_mesh/gateway/http_sse/routers/users.py +47 -1
  150. solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
  151. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
  152. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
  153. solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
  154. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
  155. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
  156. solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
  157. solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
  158. solace_agent_mesh/templates/logging_config_template.ini +10 -6
  159. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
  160. solace_agent_mesh/templates/shared_config.yaml +40 -0
  161. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/METADATA +47 -21
  162. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/RECORD +166 -145
  163. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
  164. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
  165. solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
  166. solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
  167. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
  168. solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
  169. solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
  170. solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
  171. solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
  172. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
  173. solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
  174. solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
  175. solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
  176. solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
  177. solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
  178. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
  179. solace_agent_mesh/evaluation/config_loader.py +0 -657
  180. solace_agent_mesh/evaluation/test_case_loader.py +0 -714
  181. /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.b12eac43.js.LICENSE.txt} +0 -0
  182. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/WHEEL +0 -0
  183. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/entry_points.txt +0 -0
  184. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -49,6 +49,7 @@ from google.adk.agents.run_config import StreamingMode
49
49
 
50
50
  log = logging.getLogger(__name__)
51
51
 
52
+
52
53
  def _register_peer_artifacts_in_parent_context(
53
54
  parent_task_context: "TaskExecutionContext",
54
55
  peer_task_object: Task,
@@ -123,6 +124,30 @@ async def process_event(component, event: Event):
123
124
  agent_status_sub_prefix
124
125
  ):
125
126
  await handle_a2a_response(component, message)
127
+ elif hasattr(component, "trust_manager") and component.trust_manager:
128
+ # Check if this is a trust card message (enterprise feature)
129
+ try:
130
+ if component.trust_manager.is_trust_card_topic(topic):
131
+ await component.trust_manager.handle_trust_card_message(
132
+ message, topic
133
+ )
134
+ message.call_acknowledgements()
135
+ return
136
+ except Exception as e:
137
+ log.error(
138
+ "%s Error handling trust card message: %s",
139
+ component.log_identifier,
140
+ e,
141
+ )
142
+ message.call_acknowledgements()
143
+ return
144
+
145
+ log.warning(
146
+ "%s Received message on unhandled topic: %s",
147
+ component.log_identifier,
148
+ topic,
149
+ )
150
+ message.call_acknowledgements()
126
151
  else:
127
152
  log.warning(
128
153
  "%s Received message on unhandled topic: %s",
@@ -137,6 +162,9 @@ async def process_event(component, event: Event):
137
162
  )
138
163
  if timer_data.get("timer_id") == component._card_publish_timer_id:
139
164
  publish_agent_card(component)
165
+ else:
166
+ # Handle other timer events including health check timer
167
+ component.handle_timer_event(timer_data)
140
168
  elif event.event_type == EventType.CACHE_EXPIRY:
141
169
  # Delegate cache expiry handling to the component itself.
142
170
  await component.handle_cache_expiry_event(event.data)
@@ -226,12 +254,11 @@ async def handle_a2a_request(component, message: SolaceMessage):
226
254
  payload_dict = message.get_payload()
227
255
  if not isinstance(payload_dict, dict):
228
256
  raise ValueError("Payload is not a dictionary.")
229
-
230
-
257
+
231
258
  a2a_request: A2ARequest = A2ARequest.model_validate(payload_dict)
232
259
  jsonrpc_request_id = a2a.get_request_id(a2a_request)
233
260
 
234
- # Extract properties from message user properties
261
+ # Extract properties from message user properties
235
262
  client_id = message.get_user_properties().get("clientId", "default_client")
236
263
  status_topic_from_peer = message.get_user_properties().get("a2aStatusTopic")
237
264
  reply_topic_from_peer = message.get_user_properties().get("replyTo")
@@ -245,6 +272,90 @@ async def handle_a2a_request(component, message: SolaceMessage):
245
272
  # For Send, we will generate it.
246
273
  logical_task_id = None
247
274
  method = a2a.get_request_method(a2a_request)
275
+
276
+ # Enterprise feature: Verify user authentication if trust manager enabled
277
+ verified_user_identity = None
278
+ if hasattr(component, "trust_manager") and component.trust_manager:
279
+ # Determine task_id for verification
280
+ if method == "tasks/cancel":
281
+ verification_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
282
+ elif method in ["message/send", "message/stream"]:
283
+ verification_task_id = str(a2a.get_request_id(a2a_request))
284
+ else:
285
+ verification_task_id = None
286
+
287
+ if verification_task_id:
288
+ try:
289
+ # Enterprise handles all verification logic
290
+ verified_user_identity = (
291
+ component.trust_manager.verify_request_authentication(
292
+ message=message,
293
+ task_id=verification_task_id,
294
+ namespace=namespace,
295
+ jsonrpc_request_id=jsonrpc_request_id,
296
+ )
297
+ )
298
+
299
+ if verified_user_identity:
300
+ log.info(
301
+ "%s Successfully authenticated user '%s' for task %s",
302
+ component.log_identifier,
303
+ verified_user_identity.get("user_id"),
304
+ verification_task_id,
305
+ )
306
+
307
+ except Exception as e:
308
+ # Authentication failed - enterprise provides error details
309
+ log.error(
310
+ "%s Authentication failed for task %s: %s",
311
+ component.log_identifier,
312
+ verification_task_id,
313
+ e,
314
+ )
315
+
316
+ # Build error response using enterprise exception data if available
317
+ error_data = {
318
+ "reason": "authentication_failed",
319
+ "task_id": verification_task_id,
320
+ }
321
+ if hasattr(e, "create_error_response_data"):
322
+ error_data = e.create_error_response_data()
323
+
324
+ error_response = a2a.create_invalid_request_error_response(
325
+ message="Authentication failed",
326
+ request_id=jsonrpc_request_id,
327
+ data=error_data,
328
+ )
329
+
330
+ # Determine reply topic
331
+ reply_topic = message.get_user_properties().get("replyTo")
332
+ if not reply_topic:
333
+ client_id = message.get_user_properties().get(
334
+ "clientId", "default_client"
335
+ )
336
+ reply_topic = a2a.get_client_response_topic(
337
+ namespace, client_id
338
+ )
339
+
340
+ component.publish_a2a_message(
341
+ payload=error_response.model_dump(exclude_none=True),
342
+ topic=reply_topic,
343
+ )
344
+
345
+ try:
346
+ message.call_acknowledgements()
347
+ log.debug(
348
+ "%s ACKed message with failed authentication",
349
+ component.log_identifier,
350
+ )
351
+ except Exception as ack_e:
352
+ log.error(
353
+ "%s Failed to ACK message after authentication failure: %s",
354
+ component.log_identifier,
355
+ ack_e,
356
+ )
357
+ return None
358
+
248
359
  if method == "tasks/cancel":
249
360
  logical_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
250
361
  log.info(
@@ -534,6 +645,15 @@ async def handle_a2a_request(component, message: SolaceMessage):
534
645
  "response_format": response_format,
535
646
  "host_agent_name": agent_name,
536
647
  }
648
+
649
+ # Store verified user identity claims in a2a_context (not the raw token)
650
+ if verified_user_identity:
651
+ a2a_context["verified_user_identity"] = verified_user_identity
652
+ log.debug(
653
+ "%s Stored verified user identity in a2a_context for task %s",
654
+ component.log_identifier,
655
+ logical_task_id,
656
+ )
537
657
  log.debug(
538
658
  "%s A2A Context (shared service model): %s",
539
659
  component.log_identifier,
@@ -544,6 +664,18 @@ async def handle_a2a_request(component, message: SolaceMessage):
544
664
  task_context = TaskExecutionContext(
545
665
  task_id=logical_task_id, a2a_context=a2a_context
546
666
  )
667
+
668
+ # Store auth token for peer delegation using generic security storage
669
+ if hasattr(component, "trust_manager") and component.trust_manager:
670
+ auth_token = message.get_user_properties().get("authToken")
671
+ if auth_token:
672
+ task_context.set_security_data("auth_token", auth_token)
673
+ log.debug(
674
+ "%s Stored authentication token in TaskExecutionContext security storage for task %s",
675
+ component.log_identifier,
676
+ logical_task_id,
677
+ )
678
+
547
679
  with component.active_tasks_lock:
548
680
  component.active_tasks[logical_task_id] = task_context
549
681
  log.info(
@@ -794,9 +926,26 @@ def handle_agent_card_message(component, message: SolaceMessage):
794
926
  break
795
927
 
796
928
  if is_allowed:
797
- # The received card is stored as-is. We don't need to modify it.
929
+
930
+ # Also store in peer_agents for backward compatibility
798
931
  component.peer_agents[agent_name] = agent_card
799
932
 
933
+ # Store the agent card in the registry for health tracking
934
+ is_new = component.agent_registry.add_or_update_agent(agent_card)
935
+
936
+ if is_new:
937
+ log.info(
938
+ "%s Registered new agent '%s' in registry.",
939
+ component.log_identifier,
940
+ agent_name,
941
+ )
942
+ else:
943
+ log.debug(
944
+ "%s Updated existing agent '%s' in registry.",
945
+ component.log_identifier,
946
+ agent_name,
947
+ )
948
+
800
949
  message.call_acknowledgements()
801
950
 
802
951
  except Exception as e:
@@ -1438,6 +1587,7 @@ def publish_agent_card(component):
1438
1587
  dynamic_url = f"solace:{agent_request_topic}"
1439
1588
 
1440
1589
  # Define unique URIs for our custom extensions.
1590
+ DEPLOYMENT_EXTENSION_URI = "https://solace.com/a2a/extensions/sam/deployment"
1441
1591
  PEER_TOPOLOGY_EXTENSION_URI = (
1442
1592
  "https://solace.com/a2a/extensions/peer-agent-topology"
1443
1593
  )
@@ -1446,6 +1596,24 @@ def publish_agent_card(component):
1446
1596
 
1447
1597
  extensions_list = []
1448
1598
 
1599
+ # Create the extension object for deployment tracking.
1600
+ deployment_config = component.get_config("deployment", {})
1601
+ deployment_id = deployment_config.get("id")
1602
+
1603
+ if deployment_id:
1604
+ deployment_extension = AgentExtension(
1605
+ uri=DEPLOYMENT_EXTENSION_URI,
1606
+ description="SAM deployment tracking for rolling updates",
1607
+ required=False,
1608
+ params={"id": deployment_id}
1609
+ )
1610
+ extensions_list.append(deployment_extension)
1611
+ log.debug(
1612
+ "%s Added deployment extension with ID: %s",
1613
+ component.log_identifier,
1614
+ deployment_id
1615
+ )
1616
+
1449
1617
  # Create the extension object for peer agents.
1450
1618
  if peer_agents:
1451
1619
  peer_topology_extension = AgentExtension(
@@ -1542,81 +1710,95 @@ def handle_sam_event(component, message, topic):
1542
1710
  """Handle incoming SAM system events."""
1543
1711
  try:
1544
1712
  payload = message.get_payload()
1545
-
1713
+
1546
1714
  if not isinstance(payload, dict):
1547
1715
  log.warning("Invalid SAM event payload - not a dict")
1548
1716
  message.call_acknowledgements()
1549
1717
  return
1550
-
1718
+
1551
1719
  event_type = payload.get("event_type")
1552
1720
  if not event_type:
1553
1721
  log.warning("SAM event missing event_type field")
1554
1722
  message.call_acknowledgements()
1555
1723
  return
1556
-
1724
+
1557
1725
  log.info("%s Received SAM event: %s", component.log_identifier, event_type)
1558
-
1726
+
1559
1727
  if event_type == "session.deleted":
1560
1728
  data = payload.get("data", {})
1561
1729
  session_id = data.get("session_id")
1562
1730
  user_id = data.get("user_id")
1563
1731
  agent_id = data.get("agent_id")
1564
-
1732
+
1565
1733
  if not all([session_id, user_id, agent_id]):
1566
1734
  log.warning("Missing required fields in session.deleted event")
1567
1735
  message.call_acknowledgements()
1568
1736
  return
1569
-
1737
+
1570
1738
  current_agent = component.get_config("agent_name")
1571
-
1739
+
1572
1740
  if agent_id == current_agent:
1573
- log.info("%s Processing session.deleted event for session %s",
1574
- component.log_identifier, session_id)
1575
- asyncio.create_task(cleanup_agent_session(component, session_id, user_id))
1741
+ log.info(
1742
+ "%s Processing session.deleted event for session %s",
1743
+ component.log_identifier,
1744
+ session_id,
1745
+ )
1746
+ asyncio.create_task(
1747
+ cleanup_agent_session(component, session_id, user_id)
1748
+ )
1576
1749
  else:
1577
- log.debug("Session deletion event for different agent: %s != %s", agent_id, current_agent)
1750
+ log.debug(
1751
+ "Session deletion event for different agent: %s != %s",
1752
+ agent_id,
1753
+ current_agent,
1754
+ )
1578
1755
  else:
1579
1756
  log.debug("Unhandled SAM event type: %s", event_type)
1580
-
1757
+
1581
1758
  message.call_acknowledgements()
1582
-
1759
+
1583
1760
  except Exception as e:
1584
1761
  log.error("Error handling SAM event %s: %s", topic, e)
1585
1762
  message.call_acknowledgements()
1586
1763
 
1587
1764
 
1588
-
1589
1765
  async def cleanup_agent_session(component, session_id: str, user_id: str):
1590
1766
  """Clean up agent-side session data."""
1591
1767
  try:
1592
1768
  log.info("Starting cleanup for session %s, user %s", session_id, user_id)
1593
-
1594
- if hasattr(component, 'session_service') and component.session_service:
1769
+
1770
+ if hasattr(component, "session_service") and component.session_service:
1595
1771
  agent_name = component.get_config("agent_name")
1596
- log.info("Deleting session %s from agent %s session service", session_id, agent_name)
1772
+ log.info(
1773
+ "Deleting session %s from agent %s session service",
1774
+ session_id,
1775
+ agent_name,
1776
+ )
1597
1777
  await component.session_service.delete_session(
1598
- app_name=agent_name,
1599
- user_id=user_id,
1600
- session_id=session_id
1778
+ app_name=agent_name, user_id=user_id, session_id=session_id
1601
1779
  )
1602
1780
  log.info("Successfully deleted session %s from session service", session_id)
1603
1781
  else:
1604
1782
  log.info("No session service available for cleanup")
1605
-
1783
+
1606
1784
  with component.active_tasks_lock:
1607
1785
  tasks_to_cancel = []
1608
1786
  for task_id, context in component.active_tasks.items():
1609
- if (hasattr(context, 'a2a_context') and
1610
- context.a2a_context.get('session_id') == session_id):
1787
+ if (
1788
+ hasattr(context, "a2a_context")
1789
+ and context.a2a_context.get("session_id") == session_id
1790
+ ):
1611
1791
  tasks_to_cancel.append(task_id)
1612
-
1792
+
1613
1793
  for task_id in tasks_to_cancel:
1614
1794
  context = component.active_tasks.get(task_id)
1615
1795
  if context:
1616
1796
  context.cancel()
1617
- log.info("Cancelled task %s for deleted session %s", task_id, session_id)
1618
-
1797
+ log.info(
1798
+ "Cancelled task %s for deleted session %s", task_id, session_id
1799
+ )
1800
+
1619
1801
  log.info("Session cleanup completed for session %s", session_id)
1620
-
1802
+
1621
1803
  except Exception as e:
1622
1804
  log.error("Error cleaning up session %s: %s", session_id, e)
File without changes
@@ -0,0 +1,3 @@
1
+ """
2
+ This package contains the concrete implementation for proxying standard A2A-over-HTTPS agents.
3
+ """
@@ -0,0 +1,55 @@
1
+ """
2
+ Concrete App class for the A2A-over-HTTPS proxy.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, Type
8
+
9
+ from pydantic import ValidationError
10
+ from solace_ai_connector.common.log import log
11
+
12
+ from ..base.app import BaseProxyApp
13
+ from ..base.component import BaseProxyComponent
14
+ from .component import A2AProxyComponent
15
+ from .config import A2AProxyAppConfig
16
+
17
+ info = {
18
+ "class_name": "A2AProxyApp",
19
+ }
20
+
21
+
22
+ class A2AProxyApp(BaseProxyApp):
23
+ """
24
+ Concrete App class for the A2A-over-HTTPS proxy.
25
+
26
+ Extends the BaseProxyApp to add specific configuration validation for
27
+ A2A agents (e.g., URL, authentication).
28
+ """
29
+
30
+ # Keep app_schema for documentation purposes, but it's now redundant
31
+ # The Pydantic models handle all validation
32
+ app_schema = {}
33
+
34
+ def __init__(self, app_info: Dict[str, Any], **kwargs):
35
+ app_info["class_name"] = "A2AProxyApp"
36
+
37
+ # Validate A2A-specific configuration before calling super().__init__
38
+ app_config_dict = app_info.get("app_config", {})
39
+ try:
40
+ # Validate with A2A-specific config model
41
+ app_config = A2AProxyAppConfig.model_validate_and_clean(app_config_dict)
42
+ # Overwrite the raw dict with the validated object
43
+ app_info["app_config"] = app_config
44
+ log.debug("A2A proxy configuration validated successfully.")
45
+ except ValidationError as e:
46
+ log.error("A2A proxy configuration validation failed:\n%s", e)
47
+ raise ValueError(f"Invalid A2A proxy configuration: {e}") from e
48
+
49
+ super().__init__(app_info, **kwargs)
50
+
51
+ def _get_component_class(self) -> Type[BaseProxyComponent]:
52
+ """
53
+ Returns the concrete A2AProxyComponent class.
54
+ """
55
+ return A2AProxyComponent