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

Potentially problematic release.


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

Files changed (122) hide show
  1. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  2. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +1 -1
  3. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +135 -31
  4. solace_agent_mesh/agent/adk/models/lite_llm.py +5 -0
  5. solace_agent_mesh/agent/adk/runner.py +10 -12
  6. solace_agent_mesh/agent/adk/services.py +50 -14
  7. solace_agent_mesh/agent/adk/setup.py +66 -38
  8. solace_agent_mesh/agent/protocol/event_handlers.py +243 -122
  9. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  10. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  11. solace_agent_mesh/agent/sac/app.py +43 -2
  12. solace_agent_mesh/agent/sac/component.py +200 -72
  13. solace_agent_mesh/agent/sac/task_execution_context.py +35 -4
  14. solace_agent_mesh/agent/tools/tool_config_types.py +3 -0
  15. solace_agent_mesh/agent/utils/artifact_helpers.py +1 -1
  16. solace_agent_mesh/assets/docs/404.html +3 -3
  17. solace_agent_mesh/assets/docs/assets/js/240a0364.c39f8388.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/main.d1643f0b.js +2 -0
  24. solace_agent_mesh/assets/docs/assets/js/runtime~main.97f920d4.js +1 -0
  25. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +3 -3
  26. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +3 -3
  27. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +3 -3
  28. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +3 -3
  29. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +3 -3
  30. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +3 -3
  31. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +3 -3
  32. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +3 -3
  33. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +3 -3
  34. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +3 -3
  35. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +3 -3
  36. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +3 -3
  37. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +4 -25
  38. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +76 -0
  41. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +5 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +3 -3
  43. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +3 -3
  44. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +3 -3
  45. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +3 -3
  46. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +3 -3
  47. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +3 -3
  48. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +3 -3
  49. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +3 -3
  50. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +3 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +3 -3
  55. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +3 -3
  56. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +3 -3
  57. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +3 -3
  58. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  59. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  60. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
  61. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  62. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  63. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  64. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  65. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  66. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +3 -6
  67. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  68. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  69. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +3 -3
  70. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  71. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  72. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  73. solace_agent_mesh/assets/docs/lunr-index-1761663789856.json +1 -0
  74. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  75. solace_agent_mesh/assets/docs/search-doc-1761663789856.json +1 -0
  76. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  77. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  78. solace_agent_mesh/cli/__init__.py +1 -1
  79. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-D4_RMYRh.js} +1 -1
  80. solace_agent_mesh/client/webui/frontend/static/assets/{client-CaY59VuC.js → client-UZ3qU6Bq.js} +1 -1
  81. solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +1 -0
  82. solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +342 -0
  83. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BEmvJSYz.js → vendor-DSqhjwq_.js} +1 -1
  84. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  85. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  86. solace_agent_mesh/common/a2a/events.py +2 -1
  87. solace_agent_mesh/common/sac/sam_component_base.py +24 -18
  88. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  89. solace_agent_mesh/gateway/base/component.py +12 -8
  90. solace_agent_mesh/gateway/http_sse/app.py +7 -0
  91. solace_agent_mesh/gateway/http_sse/component.py +17 -9
  92. solace_agent_mesh/gateway/http_sse/dependencies.py +83 -59
  93. solace_agent_mesh/gateway/http_sse/main.py +3 -2
  94. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +1 -1
  95. solace_agent_mesh/gateway/http_sse/routers/auth.py +103 -6
  96. solace_agent_mesh/gateway/http_sse/routers/config.py +1 -1
  97. solace_agent_mesh/gateway/http_sse/routers/sessions.py +1 -1
  98. solace_agent_mesh/gateway/http_sse/routers/sse.py +15 -5
  99. solace_agent_mesh/gateway/http_sse/routers/tasks.py +3 -3
  100. solace_agent_mesh/gateway/http_sse/routers/visualization.py +90 -8
  101. solace_agent_mesh/gateway/http_sse/services/session_service.py +1 -1
  102. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  103. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +16 -1
  104. solace_agent_mesh/gateway/http_sse/sse_manager.py +15 -6
  105. solace_agent_mesh/templates/logging_config_template.ini +2 -2
  106. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.2.dist-info}/METADATA +2 -2
  107. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.2.dist-info}/RECORD +111 -109
  108. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  109. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  110. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  111. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  114. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  115. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  116. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  117. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  118. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  119. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d1643f0b.js.LICENSE.txt} +0 -0
  120. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.2.dist-info}/WHEEL +0 -0
  121. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.2.dist-info}/entry_points.txt +0 -0
  122. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.6.2.dist-info}/licenses/LICENSE +0 -0
@@ -2,52 +2,105 @@
2
2
  Contains event handling logic for the A2A_ADK_HostComponent.
3
3
  """
4
4
 
5
- import logging
6
- import json
7
5
  import asyncio
8
- from typing import TYPE_CHECKING, Dict, Any
9
6
  import fnmatch
10
- from solace_ai_connector.common.message import Message as SolaceMessage
11
- from ...agent.adk.callbacks import _publish_data_part_status_update
12
- from ...common.data_parts import ToolResultData
13
- from ...common.a2a.types import ToolsExtensionParams
14
- from solace_ai_connector.common.event import Event, EventType
7
+ import json
8
+ import logging
9
+ from typing import TYPE_CHECKING, Any, Dict
10
+
15
11
  from a2a.types import (
16
12
  A2ARequest,
17
- AgentCard,
18
13
  AgentCapabilities,
14
+ AgentCard,
19
15
  AgentExtension,
20
16
  DataPart,
21
17
  JSONRPCResponse,
22
18
  Task,
23
19
  TaskArtifactUpdateEvent,
20
+ TaskState,
24
21
  TaskStatusUpdateEvent,
25
22
  TextPart,
26
23
  )
24
+ from google.adk.agents import RunConfig
25
+ from google.adk.agents.run_config import StreamingMode
26
+ from solace_ai_connector.common.event import Event, EventType
27
+ from solace_ai_connector.common.message import Message as SolaceMessage
28
+
29
+ from ...agent.adk.callbacks import _publish_data_part_status_update
30
+ from ...agent.adk.runner import run_adk_async_task_thread_wrapper
31
+ from ...agent.utils.artifact_helpers import generate_artifact_metadata_summary
27
32
  from ...common import a2a
28
33
  from ...common.a2a import (
29
34
  get_agent_request_topic,
30
- get_discovery_topic,
31
- translate_a2a_to_adk_content,
32
- get_client_response_topic,
33
35
  get_agent_response_subscription_topic,
34
36
  get_agent_status_subscription_topic,
37
+ get_client_response_topic,
38
+ get_discovery_topic,
35
39
  get_sam_events_subscription_topic,
36
40
  get_text_from_message,
37
41
  topic_matches_subscription,
42
+ translate_a2a_to_adk_content,
38
43
  )
39
- from ...agent.utils.artifact_helpers import (
40
- generate_artifact_metadata_summary,
41
- )
42
- from ...agent.adk.runner import run_adk_async_task_thread_wrapper
44
+ from ...common.a2a.types import ToolsExtensionParams
45
+ from ...common.data_parts import ToolResultData
43
46
  from ..sac.task_execution_context import TaskExecutionContext
44
- from google.adk.agents import RunConfig
45
47
 
46
48
  if TYPE_CHECKING:
47
49
  from ..sac.component import SamAgentComponent
48
- from google.adk.agents.run_config import StreamingMode
49
50
 
50
51
  log = logging.getLogger(__name__)
52
+ trace_logger = logging.getLogger("sam_trace")
53
+
54
+
55
+ def _forward_jsonrpc_response(
56
+ component: "SamAgentComponent",
57
+ original_jsonrpc_request_id: str,
58
+ result_data: Any,
59
+ target_topic: str,
60
+ main_logical_task_id: str,
61
+ peer_agent_name: str,
62
+ message: SolaceMessage,
63
+ ) -> None:
64
+ """
65
+ Utility method to forward a JSONRPCResponse with the given result data.
66
+
67
+ Args:
68
+ component: The SamAgentComponent instance
69
+ original_jsonrpc_request_id: The original JSONRPC request ID
70
+ result_data: The data to include in the response result
71
+ target_topic: The topic to publish to
72
+ main_logical_task_id: The main logical task ID for logging
73
+ peer_agent_name: The peer agent name for logging
74
+ message: The original message to acknowledge
75
+ """
76
+ forwarded_rpc_response = JSONRPCResponse(
77
+ id=original_jsonrpc_request_id,
78
+ result=result_data,
79
+ )
80
+ payload_to_publish = forwarded_rpc_response.model_dump(
81
+ by_alias=True, exclude_none=True
82
+ )
83
+
84
+ try:
85
+ component.publish_a2a_message(
86
+ payload_to_publish,
87
+ target_topic,
88
+ )
89
+ log.debug(
90
+ "%s Forwarded DataPart signal for main task %s (from peer %s) to %s.",
91
+ component.log_identifier,
92
+ main_logical_task_id,
93
+ peer_agent_name,
94
+ target_topic,
95
+ )
96
+ except Exception as pub_err:
97
+ log.exception(
98
+ "%s Failed to publish forwarded status signal for main task %s: %s",
99
+ component.log_identifier,
100
+ main_logical_task_id,
101
+ pub_err,
102
+ )
103
+ message.call_acknowledgements()
51
104
 
52
105
 
53
106
  def _register_peer_artifacts_in_parent_context(
@@ -196,7 +249,7 @@ async def process_event(component, event: Event):
196
249
 
197
250
  async def _publish_peer_tool_result_notification(
198
251
  component: "SamAgentComponent",
199
- correlation_data: Dict[str, Any],
252
+ correlation_data: dict[str, Any],
200
253
  payload_to_queue: Any,
201
254
  log_identifier: str,
202
255
  ):
@@ -465,6 +518,35 @@ async def handle_a2a_request(component, message: SolaceMessage):
465
518
  # The gateway/client is the source of truth for the task ID.
466
519
  # The agent adopts the ID from the JSON-RPC request envelope.
467
520
  logical_task_id = str(a2a.get_request_id(a2a_request))
521
+
522
+ try:
523
+ from solace_agent_mesh_enterprise.auth.input_required import (
524
+ a2a_auth_message_handler,
525
+ )
526
+
527
+ try:
528
+ message_handled = await a2a_auth_message_handler(
529
+ component, a2a_message, logical_task_id
530
+ )
531
+ if message_handled:
532
+ message.call_acknowledgements()
533
+ log.debug(
534
+ "%s ACKed message handled by input-required auth handler.",
535
+ component.log_identifier,
536
+ )
537
+ return None
538
+ except Exception as auth_import_err:
539
+ log.error(
540
+ "%s Error in input-required auth handler: %s",
541
+ component.log_identifier,
542
+ auth_import_err,
543
+ )
544
+ message.call_acknowledgements()
545
+ return None
546
+
547
+ except ImportError:
548
+ pass
549
+
468
550
  # The session id is now contextId on the message
469
551
  original_session_id = a2a_message.context_id
470
552
  message_id = a2a_message.message_id
@@ -490,7 +572,7 @@ async def handle_a2a_request(component, message: SolaceMessage):
490
572
  )
491
573
  else:
492
574
  session_behavior = component.default_session_behavior
493
- log.info(
575
+ log.debug(
494
576
  "%s No 'sessionBehavior' in task metadata. Using component default: '%s'.",
495
577
  component.log_identifier,
496
578
  session_behavior,
@@ -632,7 +714,6 @@ async def handle_a2a_request(component, message: SolaceMessage):
632
714
  "is_streaming": is_streaming_request,
633
715
  "statusTopic": status_topic_from_peer,
634
716
  "replyToTopic": reply_topic_from_peer,
635
- "original_solace_message": message,
636
717
  "a2a_user_config": a2a_user_config,
637
718
  "effective_session_id": effective_session_id,
638
719
  "is_run_based_session": is_run_based_session,
@@ -654,17 +735,28 @@ async def handle_a2a_request(component, message: SolaceMessage):
654
735
  component.log_identifier,
655
736
  logical_task_id,
656
737
  )
657
- log.debug(
658
- "%s A2A Context (shared service model): %s",
659
- component.log_identifier,
660
- a2a_context,
661
- )
738
+ if trace_logger.isEnabledFor(logging.DEBUG):
739
+ trace_logger.debug(
740
+ "%s A2A Context (shared service model): %s",
741
+ component.log_identifier,
742
+ a2a_context,
743
+ )
744
+ else:
745
+ log.debug(
746
+ "%s A2A Context prepared for task %s",
747
+ component.log_identifier,
748
+ a2a_context.get("logical_task_id", "unknown"),
749
+ )
662
750
 
663
751
  # Create and store the execution context for this task
664
752
  task_context = TaskExecutionContext(
665
753
  task_id=logical_task_id, a2a_context=a2a_context
666
754
  )
667
755
 
756
+ # Store the original Solace message in TaskExecutionContext instead of a2a_context
757
+ # This avoids serialization issues when a2a_context is stored in ADK session state
758
+ task_context.set_original_solace_message(message)
759
+
668
760
  # Store auth token for peer delegation using generic security storage
669
761
  if hasattr(component, "trust_manager") and component.trust_manager:
670
762
  auth_token = message.get_user_properties().get("authToken")
@@ -760,7 +852,7 @@ async def handle_a2a_request(component, message: SolaceMessage):
760
852
  streaming_mode = StreamingMode.SSE
761
853
 
762
854
  max_llm_calls_per_task = component.get_config("max_llm_calls_per_task", 20)
763
- log.info(
855
+ log.debug(
764
856
  "%s Using max_llm_calls_per_task: %s",
765
857
  component.log_identifier,
766
858
  max_llm_calls_per_task,
@@ -926,13 +1018,13 @@ def handle_agent_card_message(component, message: SolaceMessage):
926
1018
  break
927
1019
 
928
1020
  if is_allowed:
929
-
1021
+
930
1022
  # Also store in peer_agents for backward compatibility
931
1023
  component.peer_agents[agent_name] = agent_card
932
1024
 
933
1025
  # Store the agent card in the registry for health tracking
934
1026
  is_new = component.agent_registry.add_or_update_agent(agent_card)
935
-
1027
+
936
1028
  if is_new:
937
1029
  log.info(
938
1030
  "%s Registered new agent '%s' in registry.",
@@ -1053,93 +1145,143 @@ async def handle_a2a_response(component, message: SolaceMessage):
1053
1145
  if isinstance(payload_data, TaskStatusUpdateEvent):
1054
1146
  try:
1055
1147
  status_event = payload_data
1148
+
1056
1149
  data_parts = a2a.get_data_parts_from_status_update(
1057
1150
  status_event
1058
1151
  )
1059
1152
  if data_parts:
1060
- for data_part in data_parts:
1061
- log.info(
1062
- "%s Received DataPart signal from peer for sub-task %s. Forwarding...",
1063
- component.log_identifier,
1064
- sub_task_id,
1153
+
1154
+ peer_agent_name = (
1155
+ status_event.metadata.get(
1156
+ "agent_name", "UnknownPeer"
1065
1157
  )
1066
- correlation_data = await component._get_correlation_data_for_sub_task(
1158
+ if status_event.metadata
1159
+ else "UnknownPeer"
1160
+ )
1161
+
1162
+ correlation_data = (
1163
+ await component._get_correlation_data_for_sub_task(
1067
1164
  sub_task_id
1068
1165
  )
1069
- if not correlation_data:
1070
- log.warning(
1071
- "%s Correlation data not found for sub-task %s. Cannot forward status signal.",
1072
- component.log_identifier,
1073
- sub_task_id,
1074
- )
1075
- message.call_acknowledgements()
1076
- return
1077
-
1078
- original_task_context = correlation_data.get(
1079
- "original_task_context"
1166
+ )
1167
+ if not correlation_data:
1168
+ log.warning(
1169
+ "%s Correlation data not found for sub-task %s. Cannot forward status signal.",
1170
+ component.log_identifier,
1171
+ sub_task_id,
1080
1172
  )
1081
- if not original_task_context:
1082
- log.warning(
1083
- "%s original_task_context not found in correlation data for sub-task %s. Cannot forward status signal.",
1084
- component.log_identifier,
1085
- sub_task_id,
1086
- )
1087
- message.call_acknowledgements()
1088
- return
1173
+ message.call_acknowledgements()
1174
+ return
1089
1175
 
1090
- main_logical_task_id = original_task_context.get(
1091
- "logical_task_id"
1092
- )
1093
- original_jsonrpc_request_id = (
1094
- original_task_context.get("jsonrpc_request_id")
1176
+ original_task_context = correlation_data.get(
1177
+ "original_task_context"
1178
+ )
1179
+ if not original_task_context:
1180
+ log.warning(
1181
+ "%s original_task_context not found in correlation data for sub-task %s. Cannot forward status signal.",
1182
+ component.log_identifier,
1183
+ sub_task_id,
1095
1184
  )
1096
- main_context_id = original_task_context.get(
1097
- "contextId"
1185
+ message.call_acknowledgements()
1186
+ return
1187
+
1188
+ main_logical_task_id = original_task_context.get(
1189
+ "logical_task_id"
1190
+ )
1191
+ original_jsonrpc_request_id = original_task_context.get(
1192
+ "jsonrpc_request_id"
1193
+ )
1194
+ main_context_id = original_task_context.get("contextId")
1195
+
1196
+ target_topic_for_forward = original_task_context.get(
1197
+ "statusTopic"
1198
+ )
1199
+
1200
+ if (
1201
+ not main_logical_task_id
1202
+ or not original_jsonrpc_request_id
1203
+ or not target_topic_for_forward
1204
+ ):
1205
+ log.error(
1206
+ "%s Missing critical info (main_task_id, original_rpc_id, or target_status_topic) in context for sub-task %s. Cannot forward. Context: %s",
1207
+ component.log_identifier,
1208
+ sub_task_id,
1209
+ original_task_context,
1098
1210
  )
1211
+ message.call_acknowledgements()
1212
+ return
1099
1213
 
1100
- target_topic_for_forward = (
1101
- original_task_context.get("statusTopic")
1214
+ event_metadata = {
1215
+ "agent_name": component.agent_name,
1216
+ "forwarded_from_peer": peer_agent_name,
1217
+ "original_peer_event_taskId": status_event.task_id,
1218
+ "original_peer_event_timestamp": (
1219
+ status_event.status.timestamp
1220
+ if status_event.status
1221
+ and status_event.status.timestamp
1222
+ else None
1223
+ ),
1224
+ "function_call_id": correlation_data.get(
1225
+ "adk_function_call_id", None
1226
+ ),
1227
+ }
1228
+
1229
+ if (
1230
+ status_event.status.state
1231
+ == TaskState.input_required
1232
+ ):
1233
+ log.debug(
1234
+ "%s Received input-required status for sub-task %s. Requesting user input. Forwarding to target.",
1235
+ component.log_identifier,
1236
+ sub_task_id,
1102
1237
  )
1103
1238
 
1104
1239
  if (
1105
- not main_logical_task_id
1106
- or not original_jsonrpc_request_id
1107
- or not target_topic_for_forward
1240
+ status_event.metadata
1241
+ and "task_call_stack" in status_event.metadata
1242
+ and isinstance(
1243
+ status_event.metadata["task_call_stack"],
1244
+ list,
1245
+ )
1108
1246
  ):
1109
- log.error(
1110
- "%s Missing critical info (main_task_id, original_rpc_id, or target_status_topic) in context for sub-task %s. Cannot forward. Context: %s",
1111
- component.log_identifier,
1112
- sub_task_id,
1113
- original_task_context,
1247
+ task_call_stack = status_event.metadata[
1248
+ "task_call_stack"
1249
+ ].copy()
1250
+ task_call_stack.insert(0, sub_task_id)
1251
+ event_metadata["task_call_stack"] = (
1252
+ task_call_stack
1114
1253
  )
1115
- message.call_acknowledgements()
1116
- return
1254
+ else:
1255
+ event_metadata["task_call_stack"] = [
1256
+ sub_task_id
1257
+ ]
1258
+
1259
+ status_event.metadata = event_metadata
1260
+ status_event.task_id = main_logical_task_id
1261
+
1262
+ _forward_jsonrpc_response(
1263
+ component=component,
1264
+ original_jsonrpc_request_id=original_jsonrpc_request_id,
1265
+ result_data=status_event,
1266
+ target_topic=target_topic_for_forward,
1267
+ main_logical_task_id=main_logical_task_id,
1268
+ peer_agent_name=peer_agent_name,
1269
+ message=message,
1270
+ )
1271
+ return
1117
1272
 
1118
- peer_agent_name = (
1119
- status_event.metadata.get(
1120
- "agent_name", "UnknownPeer"
1121
- )
1122
- if status_event.metadata
1123
- else "UnknownPeer"
1273
+ for data_part in data_parts:
1274
+ log.info(
1275
+ "%s Received DataPart signal from peer for sub-task %s. Forwarding...",
1276
+ component.log_identifier,
1277
+ sub_task_id,
1124
1278
  )
1125
1279
 
1126
1280
  forwarded_message = a2a.create_agent_parts_message(
1127
1281
  parts=[data_part],
1128
- metadata={
1129
- "agent_name": component.agent_name,
1130
- "forwarded_from_peer": peer_agent_name,
1131
- "original_peer_event_taskId": status_event.task_id,
1132
- "original_peer_event_timestamp": (
1133
- status_event.status.timestamp
1134
- if status_event.status
1135
- and status_event.status.timestamp
1136
- else None
1137
- ),
1138
- "function_call_id": correlation_data.get(
1139
- "adk_function_call_id", None
1140
- ),
1141
- },
1282
+ metadata=event_metadata,
1142
1283
  )
1284
+
1143
1285
  forwarded_event = a2a.create_status_update(
1144
1286
  task_id=main_logical_task_id,
1145
1287
  context_id=main_context_id,
@@ -1153,36 +1295,15 @@ async def handle_a2a_response(component, message: SolaceMessage):
1153
1295
  forwarded_event.status.timestamp = (
1154
1296
  status_event.status.timestamp
1155
1297
  )
1156
- forwarded_rpc_response = JSONRPCResponse(
1157
- id=original_jsonrpc_request_id,
1158
- result=forwarded_event,
1298
+ _forward_jsonrpc_response(
1299
+ component=component,
1300
+ original_jsonrpc_request_id=original_jsonrpc_request_id,
1301
+ result_data=forwarded_event,
1302
+ target_topic=target_topic_for_forward,
1303
+ main_logical_task_id=main_logical_task_id,
1304
+ peer_agent_name=peer_agent_name,
1305
+ message=message,
1159
1306
  )
1160
- payload_to_publish = (
1161
- forwarded_rpc_response.model_dump(
1162
- by_alias=True, exclude_none=True
1163
- )
1164
- )
1165
-
1166
- try:
1167
- component.publish_a2a_message(
1168
- payload_to_publish,
1169
- target_topic_for_forward,
1170
- )
1171
- log.info(
1172
- "%s Forwarded DataPart signal for main task %s (from peer %s) to %s.",
1173
- component.log_identifier,
1174
- main_logical_task_id,
1175
- peer_agent_name,
1176
- target_topic_for_forward,
1177
- )
1178
- except Exception as pub_err:
1179
- log.exception(
1180
- "%s Failed to publish forwarded status signal for main task %s: %s",
1181
- component.log_identifier,
1182
- main_logical_task_id,
1183
- pub_err,
1184
- )
1185
- message.call_acknowledgements()
1186
1307
  return
1187
1308
 
1188
1309
  payload_to_queue = status_event.model_dump(
@@ -43,8 +43,9 @@ class A2AProxyApp(BaseProxyApp):
43
43
  app_info["app_config"] = app_config
44
44
  log.debug("A2A proxy configuration validated successfully.")
45
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
46
+ message = A2AProxyAppConfig.format_validation_error_message(e, app_info['name'])
47
+ log.error("Invalid A2A Proxy configuration:\n%s", message)
48
+ raise
48
49
 
49
50
  super().__init__(app_info, **kwargs)
50
51
 
@@ -43,8 +43,9 @@ class BaseProxyApp(App, ABC):
43
43
  # Overwrite the raw dict with the validated object for downstream use
44
44
  app_info["app_config"] = app_config
45
45
  except ValidationError as e:
46
- log.error("Proxy configuration validation failed:\n%s", e)
47
- raise ValueError(f"Invalid proxy configuration: {e}") from e
46
+ message = BaseProxyAppConfig.format_validation_error_message(e, app_info['name'])
47
+ log.error("Invalid Proxy configuration:\n%s", message)
48
+ raise
48
49
 
49
50
  namespace = app_config.get("namespace")
50
51
  proxied_agents = app_config.get("proxied_agents", [])
@@ -230,6 +230,32 @@ class ArtifactServiceConfig(SamConfigBase):
230
230
  )
231
231
  return self
232
232
 
233
+ class AgentIdentityConfig(SamConfigBase):
234
+ """Configuration for agent identity and key management."""
235
+ key_mode: Literal["auto", "manual"] = Field(
236
+ default="auto",
237
+ description="Key mode for agent identity: 'auto' for automatic generation, 'manual' for user-provided."
238
+ )
239
+ key_identity: Optional[str] = Field(
240
+ default=None,
241
+ description="Actual key value when key_mode is 'manual'."
242
+ )
243
+ key_persistence: Optional[str] = Field(
244
+ default=None,
245
+ description="Path to the key file, e.g. '/path/to/keys/agent_{name}.key'."
246
+ )
247
+
248
+ @model_validator(mode="after")
249
+ def check_key_mode_and_identity(self) -> "AgentIdentityConfig":
250
+ if self.key_mode == "manual" and not self.key_identity:
251
+ raise ValueError(
252
+ "'key_identity' is required when 'key_mode' is 'manual'."
253
+ )
254
+ if self.key_mode == "auto" and self.key_identity:
255
+ log.warning(
256
+ "Configuration Warning: 'key_identity' is ignored when 'key_mode' is 'auto'."
257
+ )
258
+ return self
233
259
 
234
260
  class SessionServiceConfig(SamConfigBase):
235
261
  """Configuration for the ADK Session Service."""
@@ -245,6 +271,12 @@ class SessionServiceConfig(SamConfigBase):
245
271
  )
246
272
 
247
273
 
274
+ class CredentialServiceConfig(SamConfigBase):
275
+ """Configuration for the ADK Credential Service."""
276
+
277
+ type: str = Field(..., description="Service type (e.g., 'memory').")
278
+
279
+
248
280
  class SamAgentAppConfig(SamConfigBase):
249
281
  """Pydantic model for the complete agent application configuration."""
250
282
 
@@ -264,6 +296,10 @@ class SamAgentAppConfig(SamConfigBase):
264
296
  model: Union[str, Dict[str, Any]] = Field(
265
297
  ..., description="ADK model name (string) or BaseLlm config dict."
266
298
  )
299
+ agent_identity: Optional[AgentIdentityConfig] = Field(
300
+ default_factory=lambda: AgentIdentityConfig(key_mode="auto"),
301
+ description="Configuration for agent identity and key management."
302
+ )
267
303
  trust_manager: Optional[Union[TrustManagerConfig, Dict[str, Any]]] = Field(
268
304
  default=None,
269
305
  description="Configuration for the Trust Manager (enterprise feature)",
@@ -306,6 +342,10 @@ class SamAgentAppConfig(SamConfigBase):
306
342
  default={"type": "memory"},
307
343
  description="Configuration for ADK Memory Service (defaults to memory).",
308
344
  )
345
+ credential_service: Optional[CredentialServiceConfig] = Field(
346
+ default=None,
347
+ description="Configuration for ADK Credential Service (optional).",
348
+ )
309
349
  multi_session_request_response: Dict[str, Any] = Field(
310
350
  default_factory=lambda: {"enabled": True},
311
351
  description="Enables multi-session request/response capabilities for the agent, required for peer delegation.",
@@ -436,8 +476,9 @@ class SamAgentApp(App):
436
476
  # Overwrite the raw dict with the validated object for downstream use
437
477
  app_info["app_config"] = app_config
438
478
  except ValidationError as e:
439
- log.error("Agent configuration validation failed:\n%s", e)
440
- raise ValueError(f"Invalid agent configuration: {e}") from e
479
+ message = SamAgentAppConfig.format_validation_error_message(e, app_info['name'], app_config_dict.get('agent_name'))
480
+ log.error("Invalid Agent configuration:\n%s", message)
481
+ raise
441
482
 
442
483
  # The rest of the method can now safely use .get() on the app_config object,
443
484
  # ensuring full backward compatibility.