solace-agent-mesh 1.11.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.
Files changed (624) hide show
  1. solace_agent_mesh/__init__.py +0 -0
  2. solace_agent_mesh/agent/__init__.py +0 -0
  3. solace_agent_mesh/agent/adk/__init__.py +0 -0
  4. solace_agent_mesh/agent/adk/adk_llm.txt +226 -0
  5. solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
  6. solace_agent_mesh/agent/adk/alembic/README +74 -0
  7. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  8. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  9. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  10. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  11. solace_agent_mesh/agent/adk/app_llm_agent.py +52 -0
  12. solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
  13. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
  14. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +545 -0
  15. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +609 -0
  16. solace_agent_mesh/agent/adk/callbacks.py +2318 -0
  17. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +406 -0
  18. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +415 -0
  19. solace_agent_mesh/agent/adk/mcp_content_processor.py +666 -0
  20. solace_agent_mesh/agent/adk/models/lite_llm.py +1026 -0
  21. solace_agent_mesh/agent/adk/models/models_llm.txt +189 -0
  22. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +132 -0
  23. solace_agent_mesh/agent/adk/runner.py +390 -0
  24. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  25. solace_agent_mesh/agent/adk/services.py +468 -0
  26. solace_agent_mesh/agent/adk/setup.py +1325 -0
  27. solace_agent_mesh/agent/adk/stream_parser.py +415 -0
  28. solace_agent_mesh/agent/adk/tool_wrapper.py +165 -0
  29. solace_agent_mesh/agent/agent_llm.txt +369 -0
  30. solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
  31. solace_agent_mesh/agent/protocol/__init__.py +0 -0
  32. solace_agent_mesh/agent/protocol/event_handlers.py +2041 -0
  33. solace_agent_mesh/agent/protocol/protocol_llm.txt +81 -0
  34. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
  35. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  36. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  37. solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
  38. solace_agent_mesh/agent/proxies/a2a/app.py +56 -0
  39. solace_agent_mesh/agent/proxies/a2a/component.py +1585 -0
  40. solace_agent_mesh/agent/proxies/a2a/config.py +216 -0
  41. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  42. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  43. solace_agent_mesh/agent/proxies/base/app.py +100 -0
  44. solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
  45. solace_agent_mesh/agent/proxies/base/component.py +816 -0
  46. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  47. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +19 -0
  48. solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
  49. solace_agent_mesh/agent/sac/__init__.py +0 -0
  50. solace_agent_mesh/agent/sac/app.py +595 -0
  51. solace_agent_mesh/agent/sac/component.py +3668 -0
  52. solace_agent_mesh/agent/sac/patch_adk.py +103 -0
  53. solace_agent_mesh/agent/sac/sac_llm.txt +189 -0
  54. solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
  55. solace_agent_mesh/agent/sac/task_execution_context.py +415 -0
  56. solace_agent_mesh/agent/testing/__init__.py +3 -0
  57. solace_agent_mesh/agent/testing/debug_utils.py +135 -0
  58. solace_agent_mesh/agent/testing/testing_llm.txt +58 -0
  59. solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
  60. solace_agent_mesh/agent/tools/__init__.py +16 -0
  61. solace_agent_mesh/agent/tools/audio_tools.py +1740 -0
  62. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +2500 -0
  63. solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +244 -0
  64. solace_agent_mesh/agent/tools/dynamic_tool.py +396 -0
  65. solace_agent_mesh/agent/tools/general_agent_tools.py +572 -0
  66. solace_agent_mesh/agent/tools/image_tools.py +1185 -0
  67. solace_agent_mesh/agent/tools/peer_agent_tool.py +363 -0
  68. solace_agent_mesh/agent/tools/registry.py +38 -0
  69. solace_agent_mesh/agent/tools/test_tools.py +136 -0
  70. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  71. solace_agent_mesh/agent/tools/tool_config_types.py +93 -0
  72. solace_agent_mesh/agent/tools/tool_definition.py +53 -0
  73. solace_agent_mesh/agent/tools/tools_llm.txt +276 -0
  74. solace_agent_mesh/agent/tools/tools_llm_detail.txt +275 -0
  75. solace_agent_mesh/agent/tools/web_tools.py +392 -0
  76. solace_agent_mesh/agent/utils/__init__.py +0 -0
  77. solace_agent_mesh/agent/utils/artifact_helpers.py +1353 -0
  78. solace_agent_mesh/agent/utils/config_parser.py +49 -0
  79. solace_agent_mesh/agent/utils/context_helpers.py +77 -0
  80. solace_agent_mesh/agent/utils/utils_llm.txt +152 -0
  81. solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
  82. solace_agent_mesh/assets/docs/404.html +16 -0
  83. solace_agent_mesh/assets/docs/assets/css/styles.8162edfb.css +1 -0
  84. solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
  85. solace_agent_mesh/assets/docs/assets/images/sam-enterprise-credentials-b269f095349473118b2b33bdfcc40122.png +0 -0
  86. solace_agent_mesh/assets/docs/assets/js/032c2d61.f3d37824.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
  95. solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
  96. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  101. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js.LICENSE.txt +13 -0
  102. solace_agent_mesh/assets/docs/assets/js/2334.1cf50a20.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/240a0364.9ad94d1b.js +1 -0
  104. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  105. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
  106. solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
  107. solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
  108. solace_agent_mesh/assets/docs/assets/js/3624.0eaa1fd0.js +1 -0
  109. solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
  110. solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
  111. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
  112. solace_agent_mesh/assets/docs/assets/js/3ac1795d.28b7c67b.js +1 -0
  113. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2ddc75c0.js +1 -0
  114. solace_agent_mesh/assets/docs/assets/js/41adc471.48b12a4e.js +1 -0
  115. solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
  116. solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
  117. solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
  118. solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
  119. solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
  120. solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
  121. solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
  122. solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
  123. solace_agent_mesh/assets/docs/assets/js/509e993c.a1fbf45a.js +1 -0
  124. solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
  125. solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
  126. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  127. solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
  128. solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
  129. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.90a87880.js +1 -0
  130. solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
  131. solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
  132. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  133. solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
  134. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  135. solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
  136. solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
  137. solace_agent_mesh/assets/docs/assets/js/66d4869e.b77431fc.js +1 -0
  138. solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
  139. solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
  140. solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.js +1 -0
  141. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  142. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  143. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  144. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  145. solace_agent_mesh/assets/docs/assets/js/6fdfefc7.99de744e.js +1 -0
  146. solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
  147. solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
  148. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  149. solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
  150. solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
  151. solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
  152. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  153. solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
  154. solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
  155. solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
  156. solace_agent_mesh/assets/docs/assets/js/81a99df0.2484b8d9.js +1 -0
  157. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  158. solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
  159. solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
  160. solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
  161. solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
  162. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js +2 -0
  163. solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js.LICENSE.txt +61 -0
  164. solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
  165. solace_agent_mesh/assets/docs/assets/js/8731.6c1dbf0c.js +1 -0
  166. solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
  167. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  168. solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
  169. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  170. solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
  171. solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
  172. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  173. solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
  174. solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
  175. solace_agent_mesh/assets/docs/assets/js/9bb13469.b2333011.js +1 -0
  176. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  177. solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
  178. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  179. solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +1 -0
  180. solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
  181. solace_agent_mesh/assets/docs/assets/js/ad71b5ed.af3ecfd1.js +1 -0
  182. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  183. solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.js +1 -0
  184. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  185. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  186. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  187. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  188. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  189. solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
  190. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  191. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  192. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  193. solace_agent_mesh/assets/docs/assets/js/de5f4c65.e8241890.js +1 -0
  194. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  195. solace_agent_mesh/assets/docs/assets/js/e04b235d.52cb25ed.js +1 -0
  196. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.b1068f9b.js +1 -0
  197. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  198. solace_agent_mesh/assets/docs/assets/js/e6f9706b.4488e34c.js +1 -0
  199. solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
  200. solace_agent_mesh/assets/docs/assets/js/f284c35a.250993bf.js +1 -0
  201. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  202. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js +2 -0
  203. solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js.LICENSE.txt +81 -0
  204. solace_agent_mesh/assets/docs/assets/js/runtime~main.9e0813a2.js +1 -0
  205. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +154 -0
  206. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +99 -0
  207. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +90 -0
  208. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +107 -0
  209. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +166 -0
  210. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +101 -0
  211. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +219 -0
  212. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +92 -0
  213. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +29 -0
  214. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +55 -0
  215. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +110 -0
  216. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  217. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  218. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +345 -0
  219. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  220. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +83 -0
  221. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +84 -0
  222. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
  223. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
  224. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +85 -0
  225. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +60 -0
  226. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  227. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +144 -0
  228. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +191 -0
  229. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +128 -0
  230. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
  231. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  232. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +34 -0
  233. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
  234. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +267 -0
  235. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +142 -0
  236. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +116 -0
  237. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +86 -0
  238. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +164 -0
  239. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +140 -0
  240. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +57 -0
  241. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
  242. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +102 -0
  243. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  244. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  245. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  246. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +37 -0
  247. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +86 -0
  248. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  249. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +247 -0
  250. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  251. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +184 -0
  252. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  253. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +75 -0
  254. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +54 -0
  255. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +85 -0
  256. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
  257. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
  258. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +78 -0
  259. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +25 -0
  260. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +78 -0
  261. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +160 -0
  262. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -0
  263. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  264. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  265. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +100 -0
  266. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +52 -0
  267. solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
  268. solace_agent_mesh/assets/docs/img/logo.png +0 -0
  269. solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
  270. solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
  271. solace_agent_mesh/assets/docs/img/sam-enterprise-credentials.png +0 -0
  272. solace_agent_mesh/assets/docs/img/solace-logo-text.svg +18 -0
  273. solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
  274. solace_agent_mesh/assets/docs/lunr-index-1765810064709.json +1 -0
  275. solace_agent_mesh/assets/docs/lunr-index.json +1 -0
  276. solace_agent_mesh/assets/docs/search-doc-1765810064709.json +1 -0
  277. solace_agent_mesh/assets/docs/search-doc.json +1 -0
  278. solace_agent_mesh/assets/docs/sitemap.xml +1 -0
  279. solace_agent_mesh/cli/__init__.py +1 -0
  280. solace_agent_mesh/cli/commands/__init__.py +0 -0
  281. solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
  282. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
  283. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +729 -0
  284. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
  285. solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +102 -0
  286. solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +114 -0
  287. solace_agent_mesh/cli/commands/docs_cmd.py +60 -0
  288. solace_agent_mesh/cli/commands/eval_cmd.py +46 -0
  289. solace_agent_mesh/cli/commands/init_cmd/__init__.py +439 -0
  290. solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
  291. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  292. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
  293. solace_agent_mesh/cli/commands/init_cmd/env_step.py +238 -0
  294. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
  295. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +464 -0
  296. solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
  297. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +119 -0
  298. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +215 -0
  299. solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +20 -0
  300. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +137 -0
  301. solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
  302. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +144 -0
  303. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +306 -0
  304. solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
  305. solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +175 -0
  306. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
  307. solace_agent_mesh/cli/commands/run_cmd.py +215 -0
  308. solace_agent_mesh/cli/main.py +52 -0
  309. solace_agent_mesh/cli/utils.py +262 -0
  310. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-Dj3JtK42.js +1 -0
  311. solace_agent_mesh/client/webui/frontend/static/assets/client-ZKk9kEJ5.js +25 -0
  312. solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
  313. solace_agent_mesh/client/webui/frontend/static/assets/main-BcUaNZ-Q.css +1 -0
  314. solace_agent_mesh/client/webui/frontend/static/assets/main-vjch4RYc.js +435 -0
  315. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BNV4kZN0.js +535 -0
  316. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +15 -0
  317. solace_agent_mesh/client/webui/frontend/static/index.html +16 -0
  318. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  319. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  320. solace_agent_mesh/common/__init__.py +1 -0
  321. solace_agent_mesh/common/a2a/__init__.py +241 -0
  322. solace_agent_mesh/common/a2a/a2a_llm.txt +175 -0
  323. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
  324. solace_agent_mesh/common/a2a/artifact.py +368 -0
  325. solace_agent_mesh/common/a2a/events.py +213 -0
  326. solace_agent_mesh/common/a2a/message.py +375 -0
  327. solace_agent_mesh/common/a2a/protocol.py +689 -0
  328. solace_agent_mesh/common/a2a/task.py +127 -0
  329. solace_agent_mesh/common/a2a/translation.py +655 -0
  330. solace_agent_mesh/common/a2a/types.py +55 -0
  331. solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
  332. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +445 -0
  333. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
  334. solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
  335. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +48 -0
  336. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  337. solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +41 -0
  338. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +330 -0
  339. solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
  340. solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +48 -0
  341. solace_agent_mesh/common/agent_registry.py +122 -0
  342. solace_agent_mesh/common/common_llm.txt +230 -0
  343. solace_agent_mesh/common/common_llm_detail.txt +2562 -0
  344. solace_agent_mesh/common/constants.py +6 -0
  345. solace_agent_mesh/common/data_parts.py +150 -0
  346. solace_agent_mesh/common/exceptions.py +49 -0
  347. solace_agent_mesh/common/middleware/__init__.py +12 -0
  348. solace_agent_mesh/common/middleware/config_resolver.py +132 -0
  349. solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
  350. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
  351. solace_agent_mesh/common/middleware/registry.py +127 -0
  352. solace_agent_mesh/common/oauth/__init__.py +17 -0
  353. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  354. solace_agent_mesh/common/oauth/utils.py +50 -0
  355. solace_agent_mesh/common/sac/__init__.py +0 -0
  356. solace_agent_mesh/common/sac/sac_llm.txt +71 -0
  357. solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
  358. solace_agent_mesh/common/sac/sam_component_base.py +730 -0
  359. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  360. solace_agent_mesh/common/sam_events/event_service.py +208 -0
  361. solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
  362. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
  363. solace_agent_mesh/common/services/__init__.py +4 -0
  364. solace_agent_mesh/common/services/employee_service.py +164 -0
  365. solace_agent_mesh/common/services/identity_service.py +134 -0
  366. solace_agent_mesh/common/services/providers/__init__.py +4 -0
  367. solace_agent_mesh/common/services/providers/local_file_identity_service.py +151 -0
  368. solace_agent_mesh/common/services/providers/providers_llm.txt +81 -0
  369. solace_agent_mesh/common/services/services_llm.txt +368 -0
  370. solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
  371. solace_agent_mesh/common/utils/__init__.py +7 -0
  372. solace_agent_mesh/common/utils/artifact_utils.py +31 -0
  373. solace_agent_mesh/common/utils/asyncio_macos_fix.py +88 -0
  374. solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
  375. solace_agent_mesh/common/utils/embeds/constants.py +56 -0
  376. solace_agent_mesh/common/utils/embeds/converter.py +447 -0
  377. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +220 -0
  378. solace_agent_mesh/common/utils/embeds/evaluators.py +395 -0
  379. solace_agent_mesh/common/utils/embeds/modifiers.py +793 -0
  380. solace_agent_mesh/common/utils/embeds/resolver.py +967 -0
  381. solace_agent_mesh/common/utils/embeds/types.py +23 -0
  382. solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
  383. solace_agent_mesh/common/utils/initializer.py +52 -0
  384. solace_agent_mesh/common/utils/log_formatters.py +64 -0
  385. solace_agent_mesh/common/utils/message_utils.py +80 -0
  386. solace_agent_mesh/common/utils/mime_helpers.py +172 -0
  387. solace_agent_mesh/common/utils/push_notification_auth.py +135 -0
  388. solace_agent_mesh/common/utils/pydantic_utils.py +159 -0
  389. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  390. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  391. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  392. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  393. solace_agent_mesh/common/utils/type_utils.py +28 -0
  394. solace_agent_mesh/common/utils/utils_llm.txt +335 -0
  395. solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
  396. solace_agent_mesh/config_portal/__init__.py +0 -0
  397. solace_agent_mesh/config_portal/backend/__init__.py +0 -0
  398. solace_agent_mesh/config_portal/backend/common.py +77 -0
  399. solace_agent_mesh/config_portal/backend/plugin_catalog/__init__.py +0 -0
  400. solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +24 -0
  401. solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
  402. solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +166 -0
  403. solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +521 -0
  404. solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +217 -0
  405. solace_agent_mesh/config_portal/backend/server.py +644 -0
  406. solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
  407. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DiOiAjzL.js +103 -0
  408. solace_agent_mesh/config_portal/frontend/static/client/assets/components-Rk0n-9cK.js +140 -0
  409. solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-mvZjNKiz.js +19 -0
  410. solace_agent_mesh/config_portal/frontend/static/client/assets/index-DzNKzXrc.js +68 -0
  411. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-ba77705e.js +1 -0
  412. solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
  413. solace_agent_mesh/config_portal/frontend/static/client/assets/root-V2BeTIUc.js +10 -0
  414. solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
  415. solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
  416. solace_agent_mesh/core_a2a/__init__.py +1 -0
  417. solace_agent_mesh/core_a2a/core_a2a_llm.txt +90 -0
  418. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
  419. solace_agent_mesh/core_a2a/service.py +307 -0
  420. solace_agent_mesh/evaluation/__init__.py +0 -0
  421. solace_agent_mesh/evaluation/evaluator.py +691 -0
  422. solace_agent_mesh/evaluation/message_organizer.py +553 -0
  423. solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
  424. solace_agent_mesh/evaluation/report/chart_section.html +141 -0
  425. solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
  426. solace_agent_mesh/evaluation/report/modal.html +59 -0
  427. solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
  428. solace_agent_mesh/evaluation/report/modal_script.js +296 -0
  429. solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
  430. solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
  431. solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
  432. solace_agent_mesh/evaluation/report/templates/header.html +340 -0
  433. solace_agent_mesh/evaluation/report_data_processor.py +970 -0
  434. solace_agent_mesh/evaluation/report_generator.py +607 -0
  435. solace_agent_mesh/evaluation/run.py +954 -0
  436. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  437. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  438. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  439. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  440. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  441. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  442. solace_agent_mesh/evaluation/subscriber.py +776 -0
  443. solace_agent_mesh/evaluation/summary_builder.py +880 -0
  444. solace_agent_mesh/gateway/__init__.py +0 -0
  445. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  446. solace_agent_mesh/gateway/adapter/base.py +143 -0
  447. solace_agent_mesh/gateway/adapter/types.py +221 -0
  448. solace_agent_mesh/gateway/base/__init__.py +1 -0
  449. solace_agent_mesh/gateway/base/app.py +345 -0
  450. solace_agent_mesh/gateway/base/base_llm.txt +226 -0
  451. solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
  452. solace_agent_mesh/gateway/base/component.py +2030 -0
  453. solace_agent_mesh/gateway/base/task_context.py +75 -0
  454. solace_agent_mesh/gateway/gateway_llm.txt +369 -0
  455. solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
  456. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  457. solace_agent_mesh/gateway/generic/app.py +50 -0
  458. solace_agent_mesh/gateway/generic/component.py +727 -0
  459. solace_agent_mesh/gateway/http_sse/__init__.py +0 -0
  460. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +345 -0
  461. solace_agent_mesh/gateway/http_sse/alembic/env.py +87 -0
  462. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  463. solace_agent_mesh/gateway/http_sse/alembic/versions/20250910_d5b3f8f2e9a0_create_initial_database.py +58 -0
  464. solace_agent_mesh/gateway/http_sse/alembic/versions/20250911_b1c2d3e4f5g6_add_database_indexes.py +83 -0
  465. solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +412 -0
  466. solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
  467. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  468. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  469. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  470. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  471. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  472. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  473. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  474. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  475. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  476. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +161 -0
  477. solace_agent_mesh/gateway/http_sse/alembic.ini +109 -0
  478. solace_agent_mesh/gateway/http_sse/app.py +351 -0
  479. solace_agent_mesh/gateway/http_sse/component.py +2360 -0
  480. solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
  481. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +105 -0
  482. solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
  483. solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +110 -0
  484. solace_agent_mesh/gateway/http_sse/dependencies.py +653 -0
  485. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +299 -0
  486. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
  487. solace_agent_mesh/gateway/http_sse/main.py +789 -0
  488. solace_agent_mesh/gateway/http_sse/repository/__init__.py +46 -0
  489. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +102 -0
  490. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +11 -0
  491. solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
  492. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +221 -0
  493. solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
  494. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  495. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  496. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +66 -0
  497. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -0
  498. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +32 -0
  499. solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
  500. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +125 -0
  501. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +239 -0
  502. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +34 -0
  503. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  504. solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
  505. solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
  506. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +257 -0
  507. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  508. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  509. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  510. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +53 -0
  511. solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
  512. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +39 -0
  513. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  514. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  515. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +308 -0
  516. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +268 -0
  517. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +248 -0
  518. solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
  519. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +74 -0
  520. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1137 -0
  521. solace_agent_mesh/gateway/http_sse/routers/auth.py +311 -0
  522. solace_agent_mesh/gateway/http_sse/routers/config.py +371 -0
  523. solace_agent_mesh/gateway/http_sse/routers/dto/__init__.py +10 -0
  524. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +450 -0
  525. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  526. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  527. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +15 -0
  528. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  529. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +133 -0
  530. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +33 -0
  531. solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
  532. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +18 -0
  533. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
  534. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  535. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +123 -0
  536. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +33 -0
  537. solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
  538. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  539. solace_agent_mesh/gateway/http_sse/routers/feedback.py +168 -0
  540. solace_agent_mesh/gateway/http_sse/routers/people.py +38 -0
  541. solace_agent_mesh/gateway/http_sse/routers/projects.py +767 -0
  542. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1415 -0
  543. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +312 -0
  544. solace_agent_mesh/gateway/http_sse/routers/sessions.py +634 -0
  545. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  546. solace_agent_mesh/gateway/http_sse/routers/sse.py +230 -0
  547. solace_agent_mesh/gateway/http_sse/routers/tasks.py +1089 -0
  548. solace_agent_mesh/gateway/http_sse/routers/users.py +83 -0
  549. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  550. solace_agent_mesh/gateway/http_sse/routers/visualization.py +1220 -0
  551. solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
  552. solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +71 -0
  553. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  554. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  555. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +273 -0
  556. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +250 -0
  557. solace_agent_mesh/gateway/http_sse/services/people_service.py +78 -0
  558. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  559. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  560. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +303 -0
  561. solace_agent_mesh/gateway/http_sse/services/session_service.py +702 -0
  562. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +593 -0
  563. solace_agent_mesh/gateway/http_sse/services/task_service.py +119 -0
  564. solace_agent_mesh/gateway/http_sse/session_manager.py +219 -0
  565. solace_agent_mesh/gateway/http_sse/shared/__init__.py +146 -0
  566. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  567. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +252 -0
  568. solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
  569. solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
  570. solace_agent_mesh/gateway/http_sse/shared/enums.py +40 -0
  571. solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
  572. solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +217 -0
  573. solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
  574. solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
  575. solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
  576. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +319 -0
  577. solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
  578. solace_agent_mesh/gateway/http_sse/shared/types.py +50 -0
  579. solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
  580. solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +88 -0
  581. solace_agent_mesh/gateway/http_sse/sse_manager.py +491 -0
  582. solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
  583. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  584. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +72 -0
  585. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
  586. solace_agent_mesh/llm.txt +228 -0
  587. solace_agent_mesh/llm_detail.txt +2835 -0
  588. solace_agent_mesh/services/__init__.py +0 -0
  589. solace_agent_mesh/services/platform/__init__.py +18 -0
  590. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  591. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  592. solace_agent_mesh/services/platform/alembic.ini +109 -0
  593. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  594. solace_agent_mesh/services/platform/api/dependencies.py +147 -0
  595. solace_agent_mesh/services/platform/api/main.py +280 -0
  596. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  597. solace_agent_mesh/services/platform/api/routers/__init__.py +24 -0
  598. solace_agent_mesh/services/platform/app.py +114 -0
  599. solace_agent_mesh/services/platform/component.py +235 -0
  600. solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
  601. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
  602. solace_agent_mesh/templates/agent_template.yaml +53 -0
  603. solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
  604. solace_agent_mesh/templates/gateway_app_template.py +75 -0
  605. solace_agent_mesh/templates/gateway_component_template.py +484 -0
  606. solace_agent_mesh/templates/gateway_config_template.yaml +38 -0
  607. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  608. solace_agent_mesh/templates/main_orchestrator.yaml +66 -0
  609. solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
  610. solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
  611. solace_agent_mesh/templates/plugin_custom_template.py +10 -0
  612. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +60 -0
  613. solace_agent_mesh/templates/plugin_pyproject_template.toml +32 -0
  614. solace_agent_mesh/templates/plugin_readme_template.md +12 -0
  615. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  616. solace_agent_mesh/templates/plugin_tools_template.py +224 -0
  617. solace_agent_mesh/templates/shared_config.yaml +112 -0
  618. solace_agent_mesh/templates/templates_llm.txt +147 -0
  619. solace_agent_mesh/templates/webui.yaml +177 -0
  620. solace_agent_mesh-1.11.2.dist-info/METADATA +504 -0
  621. solace_agent_mesh-1.11.2.dist-info/RECORD +624 -0
  622. solace_agent_mesh-1.11.2.dist-info/WHEEL +4 -0
  623. solace_agent_mesh-1.11.2.dist-info/entry_points.txt +3 -0
  624. solace_agent_mesh-1.11.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,2041 @@
1
+ """
2
+ Contains event handling logic for the A2A_ADK_HostComponent.
3
+ """
4
+
5
+ import asyncio
6
+ import fnmatch
7
+ import json
8
+ import logging
9
+ from typing import TYPE_CHECKING, Any, Dict
10
+
11
+ from a2a.types import (
12
+ A2ARequest,
13
+ AgentCapabilities,
14
+ AgentCard,
15
+ AgentExtension,
16
+ DataPart,
17
+ JSONRPCResponse,
18
+ Task,
19
+ TaskArtifactUpdateEvent,
20
+ TaskState,
21
+ TaskStatusUpdateEvent,
22
+ TextPart,
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
+ from sqlalchemy.exc import OperationalError
29
+
30
+ from ...agent.adk.callbacks import _publish_data_part_status_update
31
+ from ...agent.adk.runner import TaskCancelledError, run_adk_async_task_thread_wrapper
32
+ from ...agent.utils.artifact_helpers import generate_artifact_metadata_summary
33
+ from ...common import a2a
34
+ from ...common.utils.embeds.constants import (
35
+ EMBED_DELIMITER_OPEN,
36
+ EMBED_DELIMITER_CLOSE,
37
+ )
38
+ from ...common.a2a import (
39
+ get_agent_request_topic,
40
+ get_agent_response_subscription_topic,
41
+ get_agent_status_subscription_topic,
42
+ get_client_response_topic,
43
+ get_discovery_topic,
44
+ get_sam_events_subscription_topic,
45
+ get_text_from_message,
46
+ topic_matches_subscription,
47
+ translate_a2a_to_adk_content,
48
+ )
49
+ from ...common.a2a.types import ToolsExtensionParams
50
+ from ...common.data_parts import ToolResultData
51
+ from ..sac.task_execution_context import TaskExecutionContext
52
+
53
+ if TYPE_CHECKING:
54
+ from ..sac.component import SamAgentComponent
55
+
56
+ log = logging.getLogger(__name__)
57
+ trace_logger = logging.getLogger("sam_trace")
58
+
59
+
60
+ def _forward_jsonrpc_response(
61
+ component: "SamAgentComponent",
62
+ original_jsonrpc_request_id: str,
63
+ result_data: Any,
64
+ target_topic: str,
65
+ main_logical_task_id: str,
66
+ peer_agent_name: str,
67
+ message: SolaceMessage,
68
+ ) -> None:
69
+ """
70
+ Utility method to forward a JSONRPCResponse with the given result data.
71
+
72
+ Args:
73
+ component: The SamAgentComponent instance
74
+ original_jsonrpc_request_id: The original JSONRPC request ID
75
+ result_data: The data to include in the response result
76
+ target_topic: The topic to publish to
77
+ main_logical_task_id: The main logical task ID for logging
78
+ peer_agent_name: The peer agent name for logging
79
+ message: The original message to acknowledge
80
+ """
81
+ forwarded_rpc_response = JSONRPCResponse(
82
+ id=original_jsonrpc_request_id,
83
+ result=result_data,
84
+ )
85
+ payload_to_publish = forwarded_rpc_response.model_dump(
86
+ by_alias=True, exclude_none=True
87
+ )
88
+
89
+ try:
90
+ component.publish_a2a_message(
91
+ payload_to_publish,
92
+ target_topic,
93
+ )
94
+ log.debug(
95
+ "%s Forwarded DataPart signal for main task %s (from peer %s) to %s.",
96
+ component.log_identifier,
97
+ main_logical_task_id,
98
+ peer_agent_name,
99
+ target_topic,
100
+ )
101
+ except Exception as pub_err:
102
+ log.exception(
103
+ "%s Failed to publish forwarded status signal for main task %s: %s",
104
+ component.log_identifier,
105
+ main_logical_task_id,
106
+ pub_err,
107
+ )
108
+ message.call_acknowledgements()
109
+
110
+
111
+ def _register_peer_artifacts_in_parent_context(
112
+ parent_task_context: "TaskExecutionContext",
113
+ peer_task_object: Task,
114
+ log_identifier: str,
115
+ ):
116
+ """
117
+ Registers artifacts produced by a peer agent in the parent agent's
118
+ task execution context, allowing them to be "bubbled up".
119
+ """
120
+ if not parent_task_context:
121
+ return
122
+
123
+ if peer_task_object.metadata and "produced_artifacts" in peer_task_object.metadata:
124
+ peer_artifacts = peer_task_object.metadata.get("produced_artifacts", [])
125
+ if not peer_artifacts:
126
+ return
127
+
128
+ log.debug(
129
+ "%s Registering %d artifacts from peer response into parent task context.",
130
+ log_identifier,
131
+ len(peer_artifacts),
132
+ )
133
+ for artifact_ref in peer_artifacts:
134
+ filename = artifact_ref.get("filename")
135
+ version = artifact_ref.get("version")
136
+ if filename and version is not None:
137
+ parent_task_context.register_produced_artifact(
138
+ filename=filename,
139
+ version=version,
140
+ )
141
+
142
+
143
+ async def process_event(component, event: Event):
144
+ """
145
+ Processes incoming events (Messages, Timers, etc.). Routes to specific handlers.
146
+ Args:
147
+ component: The A2A_ADK_HostComponent instance.
148
+ event: The event object received from the SAC framework.
149
+ """
150
+ try:
151
+ if event.event_type == EventType.MESSAGE:
152
+ message = event.data
153
+ topic = message.get_topic()
154
+ if not topic:
155
+ log.warning(
156
+ "%s Received message without topic. Ignoring.",
157
+ component.log_identifier,
158
+ )
159
+ return
160
+ namespace = component.get_config("namespace")
161
+ agent_name = component.get_config("agent_name")
162
+ agent_request_topic = get_agent_request_topic(namespace, agent_name)
163
+ discovery_topic = get_discovery_topic(namespace)
164
+ agent_response_sub_prefix = (
165
+ get_agent_response_subscription_topic(namespace, agent_name)[:-2] + "/"
166
+ )
167
+ agent_status_sub_prefix = (
168
+ get_agent_status_subscription_topic(namespace, agent_name)[:-2] + "/"
169
+ )
170
+ sam_events_topic = get_sam_events_subscription_topic(namespace, "session")
171
+ if topic == agent_request_topic:
172
+ await handle_a2a_request(component, message)
173
+ elif topic == discovery_topic:
174
+ payload = message.get_payload()
175
+ if isinstance(payload, dict) and payload.get("name") != agent_name:
176
+ handle_agent_card_message(component, message)
177
+ else:
178
+ message.call_acknowledgements()
179
+ elif topic_matches_subscription(topic, sam_events_topic):
180
+ handle_sam_event(component, message, topic)
181
+ elif topic.startswith(agent_response_sub_prefix) or topic.startswith(
182
+ agent_status_sub_prefix
183
+ ):
184
+ await handle_a2a_response(component, message)
185
+ elif hasattr(component, "trust_manager") and component.trust_manager:
186
+ # Check if this is a trust card message (enterprise feature)
187
+ try:
188
+ if component.trust_manager.is_trust_card_topic(topic):
189
+ await component.trust_manager.handle_trust_card_message(
190
+ message, topic
191
+ )
192
+ message.call_acknowledgements()
193
+ return
194
+ except Exception as e:
195
+ log.error(
196
+ "%s Error handling trust card message: %s",
197
+ component.log_identifier,
198
+ e,
199
+ )
200
+ message.call_acknowledgements()
201
+ return
202
+
203
+ log.warning(
204
+ "%s Received message on unhandled topic: %s",
205
+ component.log_identifier,
206
+ topic,
207
+ )
208
+ message.call_acknowledgements()
209
+ else:
210
+ log.warning(
211
+ "%s Received message on unhandled topic: %s",
212
+ component.log_identifier,
213
+ topic,
214
+ )
215
+ message.call_acknowledgements()
216
+ elif event.event_type == EventType.TIMER:
217
+ timer_data = event.data
218
+ log.debug(
219
+ "%s Received timer event: %s", component.log_identifier, timer_data
220
+ )
221
+ if timer_data.get("timer_id") == component._card_publish_timer_id:
222
+ publish_agent_card(component)
223
+ else:
224
+ # Handle other timer events including health check timer
225
+ component.handle_timer_event(timer_data)
226
+ elif event.event_type == EventType.CACHE_EXPIRY:
227
+ # Delegate cache expiry handling to the component itself.
228
+ await component.handle_cache_expiry_event(event.data)
229
+ else:
230
+ log.warning(
231
+ "%s Received unknown event type: %s",
232
+ component.log_identifier,
233
+ event.event_type,
234
+ )
235
+ except Exception as e:
236
+ log.exception(
237
+ "%s Unhandled error in process_event: %s", component.log_identifier, e
238
+ )
239
+ if event.event_type == EventType.MESSAGE:
240
+ try:
241
+ event.data.call_negative_acknowledgements()
242
+ log.warning(
243
+ "%s NACKed message due to error in process_event.",
244
+ component.log_identifier,
245
+ )
246
+ except Exception as nack_e:
247
+ log.error(
248
+ "%s Failed to NACK message after error in process_event: %s",
249
+ component.log_identifier,
250
+ nack_e,
251
+ )
252
+ component.handle_error(e, event)
253
+
254
+
255
+ async def _publish_peer_tool_result_notification(
256
+ component: "SamAgentComponent",
257
+ correlation_data: dict[str, Any],
258
+ payload_to_queue: Any,
259
+ log_identifier: str,
260
+ ):
261
+ """Publishes a ToolResultData status update for a completed peer tool call."""
262
+ peer_tool_name = correlation_data.get("peer_tool_name")
263
+ function_call_id = correlation_data.get("adk_function_call_id")
264
+ original_task_context_data = correlation_data.get("original_task_context")
265
+
266
+ if not (peer_tool_name and function_call_id and original_task_context_data):
267
+ log.warning(
268
+ "%s Missing data in correlation_data. Cannot publish peer tool result notification.",
269
+ log_identifier,
270
+ )
271
+ return
272
+
273
+ log.info(
274
+ "%s Publishing tool_result notification for completed peer task '%s'.",
275
+ log_identifier,
276
+ peer_tool_name,
277
+ )
278
+ try:
279
+ tool_result_notification = ToolResultData(
280
+ tool_name=peer_tool_name,
281
+ result_data=payload_to_queue,
282
+ function_call_id=function_call_id,
283
+ )
284
+ await _publish_data_part_status_update(
285
+ host_component=component,
286
+ a2a_context=original_task_context_data,
287
+ data_part_model=tool_result_notification,
288
+ )
289
+ except Exception as e:
290
+ log.error(
291
+ "%s Failed to publish peer tool result notification for '%s': %s",
292
+ log_identifier,
293
+ peer_tool_name,
294
+ e,
295
+ exc_info=True,
296
+ )
297
+
298
+
299
+ async def handle_a2a_request(component, message: SolaceMessage):
300
+ """
301
+ Handles an incoming A2A request message.
302
+ Starts the ADK runner for SendTask/SendTaskStreaming requests.
303
+ Handles CancelTask requests directly.
304
+ Stores the original SolaceMessage in context for the ADK runner to ACK/NACK.
305
+ """
306
+ log.info(
307
+ "%s Received new A2A request on topic: %s",
308
+ component.log_identifier,
309
+ message.get_topic(),
310
+ )
311
+ try:
312
+ payload_dict = message.get_payload()
313
+ if not isinstance(payload_dict, dict):
314
+ raise ValueError("Payload is not a dictionary.")
315
+
316
+ a2a_request: A2ARequest = A2ARequest.model_validate(payload_dict)
317
+ jsonrpc_request_id = a2a.get_request_id(a2a_request)
318
+
319
+ # Extract properties from message user properties
320
+ client_id = message.get_user_properties().get("clientId", "default_client")
321
+ status_topic_from_peer = message.get_user_properties().get("a2aStatusTopic")
322
+ reply_topic_from_peer = message.get_user_properties().get("replyTo")
323
+ namespace = component.get_config("namespace")
324
+ a2a_user_config = message.get_user_properties().get("a2aUserConfig", {})
325
+ if not isinstance(a2a_user_config, dict):
326
+ log.warning("a2aUserConfig is not a dict, using empty dict instead")
327
+ a2a_user_config = {}
328
+
329
+ # The concept of logical_task_id changes. For Cancel, it's in params.id.
330
+ # For Send, we will generate it.
331
+ logical_task_id = None
332
+ method = a2a.get_request_method(a2a_request)
333
+
334
+ # Enterprise feature: Verify user authentication if trust manager enabled
335
+ verified_user_identity = None
336
+ if hasattr(component, "trust_manager") and component.trust_manager:
337
+ # Determine task_id for verification
338
+ if method == "tasks/cancel":
339
+ verification_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
340
+ elif method in ["message/send", "message/stream"]:
341
+ verification_task_id = str(a2a.get_request_id(a2a_request))
342
+ else:
343
+ verification_task_id = None
344
+
345
+ if verification_task_id:
346
+ try:
347
+ # Enterprise handles all verification logic
348
+ verified_user_identity = (
349
+ component.trust_manager.verify_request_authentication(
350
+ message=message,
351
+ task_id=verification_task_id,
352
+ namespace=namespace,
353
+ jsonrpc_request_id=jsonrpc_request_id,
354
+ )
355
+ )
356
+
357
+ if verified_user_identity:
358
+ log.info(
359
+ "%s Successfully authenticated user '%s' for task %s",
360
+ component.log_identifier,
361
+ verified_user_identity.get("user_id"),
362
+ verification_task_id,
363
+ )
364
+
365
+ except Exception as e:
366
+ # Authentication failed - enterprise provides error details
367
+ log.error(
368
+ "%s Authentication failed for task %s: %s",
369
+ component.log_identifier,
370
+ verification_task_id,
371
+ e,
372
+ )
373
+
374
+ # Build error response using enterprise exception data if available
375
+ error_data = {
376
+ "reason": "authentication_failed",
377
+ "task_id": verification_task_id,
378
+ }
379
+ if hasattr(e, "create_error_response_data"):
380
+ error_data = e.create_error_response_data()
381
+
382
+ error_response = a2a.create_invalid_request_error_response(
383
+ message="Authentication failed",
384
+ request_id=jsonrpc_request_id,
385
+ data=error_data,
386
+ )
387
+
388
+ # Determine reply topic
389
+ reply_topic = message.get_user_properties().get("replyTo")
390
+ if not reply_topic:
391
+ client_id = message.get_user_properties().get(
392
+ "clientId", "default_client"
393
+ )
394
+ reply_topic = a2a.get_client_response_topic(
395
+ namespace, client_id
396
+ )
397
+
398
+ component.publish_a2a_message(
399
+ payload=error_response.model_dump(exclude_none=True),
400
+ topic=reply_topic,
401
+ )
402
+
403
+ try:
404
+ message.call_acknowledgements()
405
+ log.debug(
406
+ "%s ACKed message with failed authentication",
407
+ component.log_identifier,
408
+ )
409
+ except Exception as ack_e:
410
+ log.error(
411
+ "%s Failed to ACK message after authentication failure: %s",
412
+ component.log_identifier,
413
+ ack_e,
414
+ )
415
+ return None
416
+
417
+ if method == "tasks/cancel":
418
+ logical_task_id = a2a.get_task_id_from_cancel_request(a2a_request)
419
+ log.info(
420
+ "%s Received CancelTaskRequest for Task ID: %s.",
421
+ component.log_identifier,
422
+ logical_task_id,
423
+ )
424
+ task_context = None
425
+ with component.active_tasks_lock:
426
+ task_context = component.active_tasks.get(logical_task_id)
427
+
428
+ if task_context:
429
+ task_context.cancel()
430
+ log.info(
431
+ "%s Sent cancellation signal to ADK task %s.",
432
+ component.log_identifier,
433
+ logical_task_id,
434
+ )
435
+
436
+ peer_sub_tasks = task_context.active_peer_sub_tasks.copy()
437
+ if peer_sub_tasks:
438
+ for sub_task_id, sub_task_info in peer_sub_tasks.items():
439
+ target_peer_agent_name = sub_task_info.get("peer_agent_name")
440
+ peer_task_id_to_cancel = sub_task_info.get("peer_task_id")
441
+
442
+ if not peer_task_id_to_cancel:
443
+ log.warning(
444
+ "%s Cannot cancel peer sub-task %s for main task %s because the peer's taskId is not yet known.",
445
+ component.log_identifier,
446
+ sub_task_id,
447
+ logical_task_id,
448
+ )
449
+ continue
450
+
451
+ if peer_task_id_to_cancel and target_peer_agent_name:
452
+ log.info(
453
+ "%s Attempting to cancel peer sub-task %s (Peer Task ID: %s) for agent %s (main task %s).",
454
+ component.log_identifier,
455
+ sub_task_id,
456
+ peer_task_id_to_cancel,
457
+ target_peer_agent_name,
458
+ logical_task_id,
459
+ )
460
+ try:
461
+ peer_cancel_request = a2a.create_cancel_task_request(
462
+ task_id=peer_task_id_to_cancel
463
+ )
464
+ peer_cancel_user_props = {
465
+ "clientId": component.agent_name
466
+ }
467
+ component.publish_a2a_message(
468
+ payload=peer_cancel_request.model_dump(
469
+ exclude_none=True
470
+ ),
471
+ topic=component._get_agent_request_topic(
472
+ target_peer_agent_name
473
+ ),
474
+ user_properties=peer_cancel_user_props,
475
+ )
476
+ log.info(
477
+ "%s Sent CancelTaskRequest to peer %s for its task %s.",
478
+ component.log_identifier,
479
+ target_peer_agent_name,
480
+ peer_task_id_to_cancel,
481
+ )
482
+ except Exception as e_peer_cancel:
483
+ log.error(
484
+ "%s Failed to send CancelTaskRequest to peer %s for task %s: %s",
485
+ component.log_identifier,
486
+ target_peer_agent_name,
487
+ peer_task_id_to_cancel,
488
+ e_peer_cancel,
489
+ )
490
+ else:
491
+ log.warning(
492
+ "%s Peer info for main task %s incomplete, cannot cancel peer task. Info: %s",
493
+ component.log_identifier,
494
+ logical_task_id,
495
+ sub_task_info,
496
+ )
497
+ else:
498
+ # No peer sub-tasks - check if task is paused and needs immediate finalization
499
+ if task_context.get_is_paused():
500
+ log.info(
501
+ "%s Task %s is paused with no peer sub-tasks. Scheduling immediate finalization.",
502
+ component.log_identifier,
503
+ logical_task_id,
504
+ )
505
+ loop = component.get_async_loop()
506
+ if loop and loop.is_running():
507
+ task_context.set_paused(False)
508
+
509
+ asyncio.run_coroutine_threadsafe(
510
+ component.finalize_task_with_cleanup(
511
+ task_context.a2a_context,
512
+ is_paused=False,
513
+ exception=TaskCancelledError(
514
+ f"Task {logical_task_id} cancelled while paused."
515
+ )
516
+ ),
517
+ loop,
518
+ )
519
+ else:
520
+ log.error(
521
+ "%s Cannot finalize cancelled paused task %s - event loop not available.",
522
+ component.log_identifier,
523
+ logical_task_id,
524
+ )
525
+ else:
526
+ log.info(
527
+ "%s No active task found for cancellation (ID: %s) or task already completed. Ignoring signal.",
528
+ component.log_identifier,
529
+ logical_task_id,
530
+ )
531
+ try:
532
+ message.call_acknowledgements()
533
+ log.debug(
534
+ "%s ACKed CancelTaskRequest for Task ID: %s.",
535
+ component.log_identifier,
536
+ logical_task_id,
537
+ )
538
+ except Exception as ack_e:
539
+ log.error(
540
+ "%s Failed to ACK CancelTaskRequest for Task ID %s: %s",
541
+ component.log_identifier,
542
+ logical_task_id,
543
+ ack_e,
544
+ )
545
+ return None
546
+ elif method in ["message/send", "message/stream"]:
547
+ a2a_message = a2a.get_message_from_send_request(a2a_request)
548
+ if not a2a_message:
549
+ raise ValueError("Could not extract message from SendMessageRequest")
550
+
551
+ # The gateway/client is the source of truth for the task ID.
552
+ # The agent adopts the ID from the JSON-RPC request envelope.
553
+ logical_task_id = str(a2a.get_request_id(a2a_request))
554
+
555
+ try:
556
+ from solace_agent_mesh_enterprise.auth.input_required import (
557
+ a2a_auth_message_handler,
558
+ )
559
+
560
+ try:
561
+ message_handled = await a2a_auth_message_handler(
562
+ component, a2a_message, logical_task_id
563
+ )
564
+ if message_handled:
565
+ message.call_acknowledgements()
566
+ log.debug(
567
+ "%s ACKed message handled by input-required auth handler.",
568
+ component.log_identifier,
569
+ )
570
+ return None
571
+ except Exception as auth_import_err:
572
+ log.error(
573
+ "%s Error in input-required auth handler: %s",
574
+ component.log_identifier,
575
+ auth_import_err,
576
+ )
577
+ message.call_acknowledgements()
578
+ return None
579
+
580
+ except ImportError:
581
+ pass
582
+
583
+ # The session id is now contextId on the message
584
+ original_session_id = a2a_message.context_id
585
+ message_id = a2a_message.message_id
586
+ task_metadata = a2a_message.metadata or {}
587
+ system_purpose = task_metadata.get("system_purpose")
588
+ response_format = task_metadata.get("response_format")
589
+ session_behavior_from_meta = task_metadata.get("sessionBehavior")
590
+ if session_behavior_from_meta:
591
+ session_behavior = str(session_behavior_from_meta).upper()
592
+ if session_behavior not in ["PERSISTENT", "RUN_BASED"]:
593
+ log.warning(
594
+ "%s Invalid 'sessionBehavior' in task metadata: '%s'. Using component default: '%s'.",
595
+ component.log_identifier,
596
+ session_behavior,
597
+ component.default_session_behavior,
598
+ )
599
+ session_behavior = component.default_session_behavior
600
+ else:
601
+ log.info(
602
+ "%s Using 'sessionBehavior' from task metadata: '%s'.",
603
+ component.log_identifier,
604
+ session_behavior,
605
+ )
606
+ else:
607
+ session_behavior = component.default_session_behavior
608
+ log.debug(
609
+ "%s No 'sessionBehavior' in task metadata. Using component default: '%s'.",
610
+ component.log_identifier,
611
+ session_behavior,
612
+ )
613
+ user_id = message.get_user_properties().get("userId", "default_user")
614
+ agent_name = component.get_config("agent_name")
615
+ is_streaming_request = method == "message/stream"
616
+ host_supports_streaming = component.get_config("supports_streaming", False)
617
+ if is_streaming_request and not host_supports_streaming:
618
+ raise ValueError(
619
+ "Host does not support streaming (tasks/sendSubscribe) requests."
620
+ )
621
+ effective_session_id = original_session_id
622
+ is_run_based_session = False
623
+ temporary_run_session_id_for_cleanup = None
624
+
625
+ session_id_from_data = None
626
+ if a2a_message and a2a_message.parts:
627
+ for part in a2a_message.parts:
628
+ if isinstance(part, DataPart) and "session_id" in part.data:
629
+ session_id_from_data = part.data["session_id"]
630
+ log.info(
631
+ f"Extracted session_id '{session_id_from_data}' from DataPart."
632
+ )
633
+ break
634
+
635
+ if session_id_from_data:
636
+ original_session_id = session_id_from_data
637
+
638
+ if session_behavior == "RUN_BASED":
639
+ is_run_based_session = True
640
+ effective_session_id = f"{original_session_id}:{logical_task_id}:run"
641
+ temporary_run_session_id_for_cleanup = effective_session_id
642
+ log.info(
643
+ "%s Session behavior is RUN_BASED. OriginalID='%s', EffectiveID for this run='%s', TaskID='%s'.",
644
+ component.log_identifier,
645
+ original_session_id,
646
+ effective_session_id,
647
+ logical_task_id,
648
+ )
649
+ else:
650
+ is_run_based_session = False
651
+ effective_session_id = original_session_id
652
+ temporary_run_session_id_for_cleanup = None
653
+ log.info(
654
+ "%s Session behavior is PERSISTENT. EffectiveID='%s' for TaskID='%s'.",
655
+ component.log_identifier,
656
+ effective_session_id,
657
+ logical_task_id,
658
+ )
659
+
660
+ adk_session_for_run = await component.session_service.get_session(
661
+ app_name=agent_name, user_id=user_id, session_id=effective_session_id
662
+ )
663
+ if adk_session_for_run is None:
664
+ adk_session_for_run = await component.session_service.create_session(
665
+ app_name=agent_name,
666
+ user_id=user_id,
667
+ session_id=effective_session_id,
668
+ )
669
+ log.info(
670
+ "%s Created new ADK session '%s' for task '%s'.",
671
+ component.log_identifier,
672
+ effective_session_id,
673
+ logical_task_id,
674
+ )
675
+
676
+ else:
677
+ log.info(
678
+ "%s Reusing existing ADK session '%s' for task '%s'.",
679
+ component.log_identifier,
680
+ effective_session_id,
681
+ logical_task_id,
682
+ )
683
+
684
+ if is_run_based_session:
685
+ try:
686
+ original_adk_session_data = (
687
+ await component.session_service.get_session(
688
+ app_name=agent_name,
689
+ user_id=user_id,
690
+ session_id=original_session_id,
691
+ )
692
+ )
693
+ if original_adk_session_data and hasattr(
694
+ original_adk_session_data, "history"
695
+ ):
696
+ original_history_events = original_adk_session_data.history
697
+ if original_history_events:
698
+ log.debug(
699
+ "%s Copying %d events from original session '%s' to run-based session '%s'.",
700
+ component.log_identifier,
701
+ len(original_history_events),
702
+ original_session_id,
703
+ effective_session_id,
704
+ )
705
+ run_based_adk_session_for_copy = (
706
+ await component.session_service.create_session(
707
+ app_name=agent_name,
708
+ user_id=user_id,
709
+ session_id=effective_session_id,
710
+ )
711
+ )
712
+ for event_to_copy in original_history_events:
713
+ await component.session_service.append_event(
714
+ session=run_based_adk_session_for_copy,
715
+ event=event_to_copy,
716
+ )
717
+ else:
718
+ log.debug(
719
+ "%s No history to copy from original session '%s' for run-based task '%s'.",
720
+ component.log_identifier,
721
+ original_session_id,
722
+ logical_task_id,
723
+ )
724
+ else:
725
+ log.debug(
726
+ "%s Original session '%s' not found or has no history, cannot copy for run-based task '%s'.",
727
+ component.log_identifier,
728
+ original_session_id,
729
+ logical_task_id,
730
+ )
731
+ except Exception as e_copy:
732
+ log.error(
733
+ "%s Error copying history for run-based session '%s' (task '%s'): %s. Proceeding with empty session.",
734
+ component.log_identifier,
735
+ effective_session_id,
736
+ logical_task_id,
737
+ e_copy,
738
+ )
739
+ a2a_context = {
740
+ "jsonrpc_request_id": jsonrpc_request_id,
741
+ "logical_task_id": logical_task_id,
742
+ "contextId": original_session_id,
743
+ "messageId": message_id,
744
+ "session_id": original_session_id, # Keep for now for compatibility
745
+ "user_id": user_id,
746
+ "client_id": client_id,
747
+ "is_streaming": is_streaming_request,
748
+ "statusTopic": status_topic_from_peer,
749
+ "replyToTopic": reply_topic_from_peer,
750
+ "a2a_user_config": a2a_user_config,
751
+ "effective_session_id": effective_session_id,
752
+ "is_run_based_session": is_run_based_session,
753
+ "temporary_run_session_id_for_cleanup": temporary_run_session_id_for_cleanup,
754
+ "agent_name_for_session": (
755
+ agent_name if is_run_based_session else None
756
+ ),
757
+ "user_id_for_session": user_id if is_run_based_session else None,
758
+ "system_purpose": system_purpose,
759
+ "response_format": response_format,
760
+ "host_agent_name": agent_name,
761
+ }
762
+
763
+ # Store verified user identity claims in a2a_context (not the raw token)
764
+ if verified_user_identity:
765
+ a2a_context["verified_user_identity"] = verified_user_identity
766
+ log.debug(
767
+ "%s Stored verified user identity in a2a_context for task %s",
768
+ component.log_identifier,
769
+ logical_task_id,
770
+ )
771
+ if trace_logger.isEnabledFor(logging.DEBUG):
772
+ trace_logger.debug(
773
+ "%s A2A Context (shared service model): %s",
774
+ component.log_identifier,
775
+ a2a_context,
776
+ )
777
+ else:
778
+ log.debug(
779
+ "%s A2A Context prepared for task %s",
780
+ component.log_identifier,
781
+ a2a_context.get("logical_task_id", "unknown"),
782
+ )
783
+
784
+ # Create and store the execution context for this task
785
+ task_context = TaskExecutionContext(
786
+ task_id=logical_task_id, a2a_context=a2a_context
787
+ )
788
+
789
+ # Store the original Solace message in TaskExecutionContext instead of a2a_context
790
+ # This avoids serialization issues when a2a_context is stored in ADK session state
791
+ task_context.set_original_solace_message(message)
792
+
793
+ # Store auth token for peer delegation using generic security storage
794
+ if hasattr(component, "trust_manager") and component.trust_manager:
795
+ auth_token = message.get_user_properties().get("authToken")
796
+ if auth_token:
797
+ task_context.set_security_data("auth_token", auth_token)
798
+ log.debug(
799
+ "%s Stored authentication token in TaskExecutionContext security storage for task %s",
800
+ component.log_identifier,
801
+ logical_task_id,
802
+ )
803
+
804
+ with component.active_tasks_lock:
805
+ component.active_tasks[logical_task_id] = task_context
806
+ log.info(
807
+ "%s Created and stored new TaskExecutionContext for task %s.",
808
+ component.log_identifier,
809
+ logical_task_id,
810
+ )
811
+
812
+ a2a_message_for_adk = a2a_message
813
+ invoked_artifacts = (
814
+ a2a_message_for_adk.metadata.get("invoked_with_artifacts", [])
815
+ if a2a_message_for_adk.metadata
816
+ else []
817
+ )
818
+
819
+ if invoked_artifacts:
820
+ log.info(
821
+ "%s Task %s invoked with %d artifact(s). Preparing context from metadata.",
822
+ component.log_identifier,
823
+ logical_task_id,
824
+ len(invoked_artifacts),
825
+ )
826
+ header_text = (
827
+ "The user has provided the following artifacts as context for your task. "
828
+ "Use the information contained within their metadata to complete your objective."
829
+ )
830
+ artifact_summary = await generate_artifact_metadata_summary(
831
+ component=component,
832
+ artifact_identifiers=invoked_artifacts,
833
+ user_id=user_id,
834
+ session_id=effective_session_id,
835
+ app_name=agent_name,
836
+ header_text=header_text,
837
+ )
838
+
839
+ task_description = get_text_from_message(a2a_message_for_adk)
840
+ final_prompt = f"{task_description}\n\n{artifact_summary}"
841
+
842
+ a2a_message_for_adk = a2a.update_message_parts(
843
+ message=a2a_message_for_adk,
844
+ new_parts=[a2a.create_text_part(text=final_prompt)],
845
+ )
846
+ log.debug(
847
+ "%s Generated new prompt for task %s with artifact context.",
848
+ component.log_identifier,
849
+ logical_task_id,
850
+ )
851
+
852
+ adk_content = await translate_a2a_to_adk_content(
853
+ a2a_message=a2a_message_for_adk,
854
+ component=component,
855
+ user_id=user_id,
856
+ session_id=effective_session_id,
857
+ )
858
+
859
+ adk_session = await component.session_service.get_session(
860
+ app_name=agent_name, user_id=user_id, session_id=effective_session_id
861
+ )
862
+ if adk_session is None:
863
+ log.info(
864
+ "%s ADK session '%s' not found in component.session_service, creating new one.",
865
+ component.log_identifier,
866
+ effective_session_id,
867
+ )
868
+ adk_session = await component.session_service.create_session(
869
+ app_name=agent_name,
870
+ user_id=user_id,
871
+ session_id=effective_session_id,
872
+ )
873
+ else:
874
+ log.info(
875
+ "%s Reusing existing ADK session '%s' from component.session_service.",
876
+ component.log_identifier,
877
+ effective_session_id,
878
+ )
879
+
880
+ # Always use SSE streaming mode for the ADK runner.
881
+ # This ensures that real-time callbacks (e.g., for fenced artifact
882
+ # progress) can function correctly for all task types. The component's
883
+ # internal logic uses the 'is_run_based_session' flag to differentiate
884
+ # between aggregating a final response and streaming partial updates.
885
+ streaming_mode = StreamingMode.SSE
886
+
887
+ max_llm_calls_per_task = component.get_config("max_llm_calls_per_task", 20)
888
+ log.debug(
889
+ "%s Using max_llm_calls_per_task: %s",
890
+ component.log_identifier,
891
+ max_llm_calls_per_task,
892
+ )
893
+
894
+ run_config = RunConfig(
895
+ streaming_mode=streaming_mode, max_llm_calls=max_llm_calls_per_task
896
+ )
897
+ log.info(
898
+ "%s Setting ADK RunConfig streaming_mode to: %s, max_llm_calls to: %s",
899
+ component.log_identifier,
900
+ streaming_mode,
901
+ max_llm_calls_per_task,
902
+ )
903
+
904
+ log.info(
905
+ "%s Starting ADK runner task for request %s (Task ID: %s)",
906
+ component.log_identifier,
907
+ jsonrpc_request_id,
908
+ logical_task_id,
909
+ )
910
+
911
+ await run_adk_async_task_thread_wrapper(
912
+ component,
913
+ adk_session,
914
+ adk_content,
915
+ run_config,
916
+ a2a_context,
917
+ )
918
+
919
+ log.info(
920
+ "%s ADK task execution awaited for Task ID %s.",
921
+ component.log_identifier,
922
+ logical_task_id,
923
+ )
924
+
925
+ else:
926
+ log.warning(
927
+ "%s Received unhandled A2A request type: %s. Acknowledging.",
928
+ component.log_identifier,
929
+ method,
930
+ )
931
+ try:
932
+ message.call_acknowledgements()
933
+ except Exception as ack_e:
934
+ log.error(
935
+ "%s Failed to ACK unhandled request type %s: %s",
936
+ component.log_identifier,
937
+ method,
938
+ ack_e,
939
+ )
940
+ return None
941
+
942
+ except (json.JSONDecodeError, ValueError, TypeError) as e:
943
+ log.error(
944
+ "%s Failed to parse, validate, or start ADK task for A2A request: %s",
945
+ component.log_identifier,
946
+ e,
947
+ )
948
+ error_data = {"taskId": logical_task_id} if logical_task_id else None
949
+ error_response = a2a.create_internal_error_response(
950
+ message=str(e), request_id=jsonrpc_request_id, data=error_data
951
+ )
952
+
953
+ target_topic = reply_topic_from_peer or (
954
+ get_client_response_topic(namespace, client_id) if client_id else None
955
+ )
956
+ if target_topic:
957
+ component.publish_a2a_message(
958
+ error_response.model_dump(exclude_none=True),
959
+ target_topic,
960
+ )
961
+
962
+ try:
963
+ message.call_negative_acknowledgements()
964
+ log.warning(
965
+ "%s NACKed original A2A request due to parsing/validation/start error.",
966
+ component.log_identifier,
967
+ )
968
+ except Exception as nack_e:
969
+ log.error(
970
+ "%s Failed to NACK message after pre-start error: %s",
971
+ component.log_identifier,
972
+ nack_e,
973
+ )
974
+
975
+ component.handle_error(e, Event(EventType.MESSAGE, message))
976
+ return None
977
+
978
+ except OperationalError as e:
979
+ log.error(
980
+ "%s Database error while processing A2A request: %s",
981
+ component.log_identifier,
982
+ e,
983
+ )
984
+
985
+ # Check if it's a schema error
986
+ error_msg = str(e).lower()
987
+ if "no such column" in error_msg or "no such table" in error_msg:
988
+ user_message = (
989
+ "Database schema update required. "
990
+ "Please contact your administrator to run database migrations."
991
+ )
992
+ else:
993
+ user_message = (
994
+ "Database error occurred. Please try again or contact support."
995
+ )
996
+
997
+ error_response = a2a.create_internal_error_response(
998
+ message=user_message,
999
+ request_id=jsonrpc_request_id,
1000
+ data={"taskId": logical_task_id} if logical_task_id else None,
1001
+ )
1002
+
1003
+ target_topic = reply_topic_from_peer or (
1004
+ get_client_response_topic(namespace, client_id) if client_id else None
1005
+ )
1006
+ if target_topic:
1007
+ component.publish_a2a_message(
1008
+ error_response.model_dump(exclude_none=True),
1009
+ target_topic,
1010
+ )
1011
+
1012
+ try:
1013
+ message.call_negative_acknowledgements()
1014
+ log.warning(
1015
+ "%s NACKed A2A request due to database error.",
1016
+ component.log_identifier,
1017
+ )
1018
+ except Exception as nack_e:
1019
+ log.error(
1020
+ "%s Failed to NACK message after database error: %s",
1021
+ component.log_identifier,
1022
+ nack_e,
1023
+ )
1024
+
1025
+ component.handle_error(e, Event(EventType.MESSAGE, message))
1026
+ return None
1027
+
1028
+ except Exception as e:
1029
+ log.exception(
1030
+ "%s Unexpected error handling A2A request: %s", component.log_identifier, e
1031
+ )
1032
+ error_response = a2a.create_internal_error_response(
1033
+ message=f"Unexpected server error: {e}",
1034
+ request_id=jsonrpc_request_id,
1035
+ data={"taskId": logical_task_id},
1036
+ )
1037
+ target_topic = reply_topic_from_peer or (
1038
+ get_client_response_topic(namespace, client_id) if client_id else None
1039
+ )
1040
+ if target_topic:
1041
+ component.publish_a2a_message(
1042
+ error_response.model_dump(exclude_none=True),
1043
+ target_topic,
1044
+ )
1045
+
1046
+ try:
1047
+ message.call_negative_acknowledgements()
1048
+ log.warning(
1049
+ "%s NACKed original A2A request due to unexpected error.",
1050
+ component.log_identifier,
1051
+ )
1052
+ except Exception as nack_e:
1053
+ log.error(
1054
+ "%s Failed to NACK message after unexpected error: %s",
1055
+ component.log_identifier,
1056
+ nack_e,
1057
+ )
1058
+
1059
+ component.handle_error(e, Event(EventType.MESSAGE, message))
1060
+ return None
1061
+
1062
+
1063
+ def handle_agent_card_message(component, message: SolaceMessage):
1064
+ """Handles incoming Agent Card messages."""
1065
+ try:
1066
+ payload = message.get_payload()
1067
+ if not isinstance(payload, dict):
1068
+ log.warning(
1069
+ "%s Received agent card with non-dict payload. Ignoring.",
1070
+ component.log_identifier,
1071
+ )
1072
+ message.call_acknowledgements()
1073
+ return
1074
+
1075
+ agent_card = AgentCard(**payload)
1076
+ agent_name = agent_card.name
1077
+ self_agent_name = component.get_config("agent_name")
1078
+
1079
+ if agent_name == self_agent_name:
1080
+ message.call_acknowledgements()
1081
+ return
1082
+
1083
+ agent_discovery = component.get_config("agent_discovery", {})
1084
+ if agent_discovery.get("enabled", False) is False:
1085
+ message.call_acknowledgements()
1086
+ return
1087
+
1088
+ inter_agent_config = component.get_config("inter_agent_communication", {})
1089
+ allow_list = inter_agent_config.get("allow_list", ["*"])
1090
+ deny_list = inter_agent_config.get("deny_list", [])
1091
+ is_allowed = False
1092
+ for pattern in allow_list:
1093
+ if fnmatch.fnmatch(agent_name, pattern):
1094
+ is_allowed = True
1095
+ break
1096
+
1097
+ if is_allowed:
1098
+ for pattern in deny_list:
1099
+ if fnmatch.fnmatch(agent_name, pattern):
1100
+ is_allowed = False
1101
+ break
1102
+
1103
+ if is_allowed:
1104
+
1105
+ # Also store in peer_agents for backward compatibility
1106
+ component.peer_agents[agent_name] = agent_card
1107
+
1108
+ # Store the agent card in the registry for health tracking
1109
+ is_new = component.agent_registry.add_or_update_agent(agent_card)
1110
+
1111
+ if is_new:
1112
+ log.info(
1113
+ "%s Registered new agent '%s' in registry.",
1114
+ component.log_identifier,
1115
+ agent_name,
1116
+ )
1117
+ else:
1118
+ log.debug(
1119
+ "%s Updated existing agent '%s' in registry.",
1120
+ component.log_identifier,
1121
+ agent_name,
1122
+ )
1123
+
1124
+ message.call_acknowledgements()
1125
+
1126
+ except Exception as e:
1127
+ log.exception(
1128
+ "%s Error processing agent card message: %s", component.log_identifier, e
1129
+ )
1130
+ message.call_acknowledgements()
1131
+ component.handle_error(e, Event(EventType.MESSAGE, message))
1132
+
1133
+
1134
+ async def handle_a2a_response(component, message: SolaceMessage):
1135
+ """Handles incoming responses/status updates from peer agents."""
1136
+ sub_task_id = None
1137
+ payload_to_queue = None
1138
+ is_final_response = False
1139
+
1140
+ try:
1141
+ topic = message.get_topic()
1142
+ agent_response_sub = a2a.get_agent_response_subscription_topic(
1143
+ component.namespace, component.agent_name
1144
+ )
1145
+ agent_status_sub = a2a.get_agent_status_subscription_topic(
1146
+ component.namespace, component.agent_name
1147
+ )
1148
+
1149
+ if a2a.topic_matches_subscription(topic, agent_response_sub):
1150
+ sub_task_id = a2a.extract_task_id_from_topic(
1151
+ topic, agent_response_sub, component.log_identifier
1152
+ )
1153
+ elif a2a.topic_matches_subscription(topic, agent_status_sub):
1154
+ sub_task_id = a2a.extract_task_id_from_topic(
1155
+ topic, agent_status_sub, component.log_identifier
1156
+ )
1157
+ else:
1158
+ sub_task_id = None
1159
+
1160
+ if not sub_task_id:
1161
+ log.error(
1162
+ "%s Could not extract sub-task ID from topic: %s",
1163
+ component.log_identifier,
1164
+ topic,
1165
+ )
1166
+ message.call_negative_acknowledgements()
1167
+ return
1168
+
1169
+ log.debug("%s Extracted sub-task ID: %s", component.log_identifier, sub_task_id)
1170
+
1171
+ payload_dict = message.get_payload()
1172
+ if not isinstance(payload_dict, dict):
1173
+ log.error(
1174
+ "%s Received non-dict payload for sub-task %s. Payload: %s",
1175
+ component.log_identifier,
1176
+ sub_task_id,
1177
+ payload_dict,
1178
+ )
1179
+ payload_to_queue = {
1180
+ "error": "Received invalid payload format from peer.",
1181
+ "code": "PEER_PAYLOAD_ERROR",
1182
+ }
1183
+ is_final_response = True
1184
+ else:
1185
+ try:
1186
+ a2a_response = JSONRPCResponse.model_validate(payload_dict)
1187
+
1188
+ result = a2a.get_response_result(a2a_response)
1189
+ if result:
1190
+ payload_data = result
1191
+
1192
+ # Store the peer's task ID if we see it for the first time
1193
+ peer_task_id = getattr(payload_data, "task_id", None)
1194
+ if peer_task_id:
1195
+ correlation_data = (
1196
+ await component._get_correlation_data_for_sub_task(
1197
+ sub_task_id
1198
+ )
1199
+ )
1200
+ if correlation_data and "peer_task_id" not in correlation_data:
1201
+ log.info(
1202
+ "%s Received first response for sub-task %s. Storing peer taskId: %s",
1203
+ component.log_identifier,
1204
+ sub_task_id,
1205
+ peer_task_id,
1206
+ )
1207
+ main_logical_task_id = correlation_data.get(
1208
+ "logical_task_id"
1209
+ )
1210
+ with component.active_tasks_lock:
1211
+ task_context = component.active_tasks.get(
1212
+ main_logical_task_id
1213
+ )
1214
+ if task_context:
1215
+ with task_context.lock:
1216
+ if (
1217
+ sub_task_id
1218
+ in task_context.active_peer_sub_tasks
1219
+ ):
1220
+ task_context.active_peer_sub_tasks[
1221
+ sub_task_id
1222
+ ]["peer_task_id"] = peer_task_id
1223
+
1224
+ parsed_successfully = False
1225
+ is_final_response = False
1226
+ payload_to_queue = None
1227
+
1228
+ if isinstance(payload_data, TaskStatusUpdateEvent):
1229
+ try:
1230
+ status_event = payload_data
1231
+
1232
+ data_parts = a2a.get_data_parts_from_status_update(
1233
+ status_event
1234
+ )
1235
+ if data_parts:
1236
+
1237
+ peer_agent_name = (
1238
+ status_event.metadata.get(
1239
+ "agent_name", "UnknownPeer"
1240
+ )
1241
+ if status_event.metadata
1242
+ else "UnknownPeer"
1243
+ )
1244
+
1245
+ correlation_data = (
1246
+ await component._get_correlation_data_for_sub_task(
1247
+ sub_task_id
1248
+ )
1249
+ )
1250
+ if not correlation_data:
1251
+ log.warning(
1252
+ "%s Correlation data not found for sub-task %s. Cannot forward status signal.",
1253
+ component.log_identifier,
1254
+ sub_task_id,
1255
+ )
1256
+ message.call_acknowledgements()
1257
+ return
1258
+
1259
+ original_task_context = correlation_data.get(
1260
+ "original_task_context"
1261
+ )
1262
+ if not original_task_context:
1263
+ log.warning(
1264
+ "%s original_task_context not found in correlation data for sub-task %s. Cannot forward status signal.",
1265
+ component.log_identifier,
1266
+ sub_task_id,
1267
+ )
1268
+ message.call_acknowledgements()
1269
+ return
1270
+
1271
+ main_logical_task_id = original_task_context.get(
1272
+ "logical_task_id"
1273
+ )
1274
+ original_jsonrpc_request_id = original_task_context.get(
1275
+ "jsonrpc_request_id"
1276
+ )
1277
+ main_context_id = original_task_context.get("contextId")
1278
+
1279
+ target_topic_for_forward = original_task_context.get(
1280
+ "statusTopic"
1281
+ )
1282
+
1283
+ if (
1284
+ not main_logical_task_id
1285
+ or not original_jsonrpc_request_id
1286
+ or not target_topic_for_forward
1287
+ ):
1288
+ log.error(
1289
+ "%s Missing critical info (main_task_id, original_rpc_id, or target_status_topic) in context for sub-task %s. Cannot forward. Context: %s",
1290
+ component.log_identifier,
1291
+ sub_task_id,
1292
+ original_task_context,
1293
+ )
1294
+ message.call_acknowledgements()
1295
+ return
1296
+
1297
+ event_metadata = {
1298
+ "agent_name": component.agent_name,
1299
+ "forwarded_from_peer": peer_agent_name,
1300
+ "original_peer_event_taskId": status_event.task_id,
1301
+ "original_peer_event_timestamp": (
1302
+ status_event.status.timestamp
1303
+ if status_event.status
1304
+ and status_event.status.timestamp
1305
+ else None
1306
+ ),
1307
+ "function_call_id": correlation_data.get(
1308
+ "adk_function_call_id", None
1309
+ ),
1310
+ }
1311
+
1312
+ if (
1313
+ status_event.status.state
1314
+ == TaskState.input_required
1315
+ ):
1316
+ log.debug(
1317
+ "%s Received input-required status for sub-task %s. Requesting user input. Forwarding to target.",
1318
+ component.log_identifier,
1319
+ sub_task_id,
1320
+ )
1321
+
1322
+ if (
1323
+ status_event.metadata
1324
+ and "task_call_stack" in status_event.metadata
1325
+ and isinstance(
1326
+ status_event.metadata["task_call_stack"],
1327
+ list,
1328
+ )
1329
+ ):
1330
+ task_call_stack = status_event.metadata[
1331
+ "task_call_stack"
1332
+ ].copy()
1333
+ task_call_stack.insert(0, sub_task_id)
1334
+ event_metadata["task_call_stack"] = (
1335
+ task_call_stack
1336
+ )
1337
+ else:
1338
+ event_metadata["task_call_stack"] = [
1339
+ sub_task_id
1340
+ ]
1341
+
1342
+ status_event.metadata = event_metadata
1343
+ status_event.task_id = main_logical_task_id
1344
+
1345
+ _forward_jsonrpc_response(
1346
+ component=component,
1347
+ original_jsonrpc_request_id=original_jsonrpc_request_id,
1348
+ result_data=status_event,
1349
+ target_topic=target_topic_for_forward,
1350
+ main_logical_task_id=main_logical_task_id,
1351
+ peer_agent_name=peer_agent_name,
1352
+ message=message,
1353
+ )
1354
+ return
1355
+
1356
+ # Filter out artifact creation progress from peer agents.
1357
+ # These are implementation details that should not leak across
1358
+ # agent boundaries. Artifacts are properly bubbled up in the
1359
+ # final Task response metadata.
1360
+ filtered_data_parts = []
1361
+ for data_part in data_parts:
1362
+ if isinstance(data_part.data, dict) and data_part.data.get("type") == "artifact_creation_progress":
1363
+ log.debug(
1364
+ "%s Filtered out artifact_creation_progress DataPart from peer sub-task %s. Not forwarding to user.",
1365
+ component.log_identifier,
1366
+ sub_task_id,
1367
+ )
1368
+ continue
1369
+ filtered_data_parts.append(data_part)
1370
+
1371
+ # Only forward if there are non-filtered data parts
1372
+ if filtered_data_parts:
1373
+ for data_part in filtered_data_parts:
1374
+ log.info(
1375
+ "%s Received DataPart signal from peer for sub-task %s. Forwarding...",
1376
+ component.log_identifier,
1377
+ sub_task_id,
1378
+ )
1379
+
1380
+ forwarded_message = a2a.create_agent_parts_message(
1381
+ parts=[data_part],
1382
+ metadata=event_metadata,
1383
+ )
1384
+
1385
+ forwarded_event = a2a.create_status_update(
1386
+ task_id=main_logical_task_id,
1387
+ context_id=main_context_id,
1388
+ message=forwarded_message,
1389
+ is_final=False,
1390
+ )
1391
+ if (
1392
+ status_event.status
1393
+ and status_event.status.timestamp
1394
+ ):
1395
+ forwarded_event.status.timestamp = (
1396
+ status_event.status.timestamp
1397
+ )
1398
+ _forward_jsonrpc_response(
1399
+ component=component,
1400
+ original_jsonrpc_request_id=original_jsonrpc_request_id,
1401
+ result_data=forwarded_event,
1402
+ target_topic=target_topic_for_forward,
1403
+ main_logical_task_id=main_logical_task_id,
1404
+ peer_agent_name=peer_agent_name,
1405
+ message=message,
1406
+ )
1407
+ return
1408
+ else:
1409
+ log.debug(
1410
+ "%s All DataParts from peer sub-task %s were filtered. Not forwarding.",
1411
+ component.log_identifier,
1412
+ sub_task_id,
1413
+ )
1414
+
1415
+ payload_to_queue = status_event.model_dump(
1416
+ by_alias=True, exclude_none=True
1417
+ )
1418
+ if status_event.final:
1419
+ log.debug(
1420
+ "%s Parsed TaskStatusUpdateEvent(final=True) from peer for sub-task %s. This is an intermediate update for PeerAgentTool.",
1421
+ component.log_identifier,
1422
+ sub_task_id,
1423
+ )
1424
+
1425
+ if status_event.status and status_event.status.message:
1426
+ response_parts_data = []
1427
+ unwrapped_parts = a2a.get_parts_from_message(
1428
+ status_event.status.message
1429
+ )
1430
+ for part in unwrapped_parts:
1431
+ if isinstance(part, TextPart):
1432
+ response_parts_data.append(str(part.text))
1433
+ elif isinstance(part, DataPart):
1434
+ try:
1435
+ response_parts_data.append(
1436
+ json.dumps(part.data)
1437
+ )
1438
+ except TypeError:
1439
+ response_parts_data.append(
1440
+ str(part.data)
1441
+ )
1442
+
1443
+ payload_to_queue = {
1444
+ "result": "\n".join(response_parts_data)
1445
+ }
1446
+ log.debug(
1447
+ "%s Extracted content for TaskStatusUpdateEvent(final=True) for sub-task %s: %s",
1448
+ component.log_identifier,
1449
+ sub_task_id,
1450
+ payload_to_queue,
1451
+ )
1452
+ else:
1453
+ log.debug(
1454
+ "%s TaskStatusUpdateEvent(final=True) for sub-task %s has no message parts to extract. Sending event object.",
1455
+ component.log_identifier,
1456
+ sub_task_id,
1457
+ )
1458
+ else:
1459
+ log.debug(
1460
+ "%s Parsed TaskStatusUpdateEvent(final=False) from peer for sub-task %s. This is an intermediate update.",
1461
+ component.log_identifier,
1462
+ sub_task_id,
1463
+ )
1464
+ parsed_successfully = True
1465
+ except Exception as e:
1466
+ log.warning(
1467
+ "%s Failed to process payload as TaskStatusUpdateEvent for sub-task %s. Payload: %s. Error: %s",
1468
+ component.log_identifier,
1469
+ sub_task_id,
1470
+ payload_data,
1471
+ e,
1472
+ )
1473
+ payload_to_queue = None
1474
+
1475
+ elif isinstance(payload_data, TaskArtifactUpdateEvent):
1476
+ try:
1477
+ artifact_event = payload_data
1478
+ payload_to_queue = artifact_event.model_dump(
1479
+ by_alias=True, exclude_none=True
1480
+ )
1481
+ is_final_response = False
1482
+ log.debug(
1483
+ "%s Parsed TaskArtifactUpdateEvent from peer for sub-task %s. This is an intermediate update.",
1484
+ component.log_identifier,
1485
+ sub_task_id,
1486
+ )
1487
+ parsed_successfully = True
1488
+ except Exception as e:
1489
+ log.warning(
1490
+ "%s Failed to parse payload as TaskArtifactUpdateEvent for sub-task %s. Payload: %s. Error: %s",
1491
+ component.log_identifier,
1492
+ sub_task_id,
1493
+ payload_data,
1494
+ e,
1495
+ )
1496
+ payload_to_queue = None
1497
+
1498
+ elif isinstance(payload_data, Task):
1499
+ try:
1500
+ final_task = payload_data
1501
+ payload_to_queue = final_task.model_dump(
1502
+ by_alias=True, exclude_none=True
1503
+ )
1504
+ is_final_response = True
1505
+ log.debug(
1506
+ "%s Parsed final Task object from peer for sub-task %s.",
1507
+ component.log_identifier,
1508
+ sub_task_id,
1509
+ )
1510
+ parsed_successfully = True
1511
+ except Exception as task_parse_error:
1512
+ log.error(
1513
+ "%s Failed to parse peer response for sub-task %s as Task. Payload: %s. Error: %s",
1514
+ component.log_identifier,
1515
+ sub_task_id,
1516
+ payload_data,
1517
+ task_parse_error,
1518
+ )
1519
+ if not a2a.get_response_error(a2a_response):
1520
+ error = a2a.create_internal_error(
1521
+ message=f"Failed to parse response from peer agent for sub-task {sub_task_id}",
1522
+ data={
1523
+ "original_payload": payload_data.model_dump(
1524
+ by_alias=True, exclude_none=True
1525
+ ),
1526
+ "error": str(task_parse_error),
1527
+ },
1528
+ )
1529
+ a2a_response = a2a.create_error_response(
1530
+ error, a2a.get_response_id(a2a_response)
1531
+ )
1532
+ payload_to_queue = None
1533
+ is_final_response = True
1534
+
1535
+ if (
1536
+ not parsed_successfully
1537
+ and not a2a.get_response_error(a2a_response)
1538
+ and payload_to_queue is None
1539
+ ):
1540
+ log.error(
1541
+ "%s Unhandled payload structure from peer for sub-task %s: %s.",
1542
+ component.log_identifier,
1543
+ sub_task_id,
1544
+ payload_data,
1545
+ )
1546
+ error = a2a.create_internal_error(
1547
+ message=f"Unknown response structure from peer agent for sub-task {sub_task_id}",
1548
+ data={
1549
+ "original_payload": payload_data.model_dump(
1550
+ by_alias=True, exclude_none=True
1551
+ )
1552
+ },
1553
+ )
1554
+ a2a_response = a2a.create_error_response(
1555
+ error, a2a.get_response_id(a2a_response)
1556
+ )
1557
+ is_final_response = True
1558
+
1559
+ elif error := a2a.get_response_error(a2a_response):
1560
+ log.warning(
1561
+ "%s Received error response from peer for sub-task %s: %s",
1562
+ component.log_identifier,
1563
+ sub_task_id,
1564
+ error,
1565
+ )
1566
+ payload_to_queue = {
1567
+ "error": error.message,
1568
+ "code": error.code,
1569
+ "data": error.data,
1570
+ }
1571
+ is_final_response = True
1572
+ else:
1573
+ log.warning(
1574
+ "%s Received JSONRPCResponse with no result or error for sub-task %s.",
1575
+ component.log_identifier,
1576
+ sub_task_id,
1577
+ )
1578
+ payload_to_queue = {"result": "Peer responded with empty message."}
1579
+ is_final_response = True
1580
+
1581
+ except Exception as parse_error:
1582
+ log.error(
1583
+ "%s Failed to parse A2A response payload for sub-task %s: %s",
1584
+ component.log_identifier,
1585
+ sub_task_id,
1586
+ parse_error,
1587
+ )
1588
+ payload_to_queue = {
1589
+ "error": f"Failed to parse response from peer: {parse_error}",
1590
+ "code": "PEER_PARSE_ERROR",
1591
+ }
1592
+ # Print out the stack trace for debugging
1593
+ log.exception(
1594
+ "%s Exception stack trace: %s",
1595
+ component.log_identifier,
1596
+ parse_error,
1597
+ )
1598
+
1599
+ if not is_final_response:
1600
+ # This is an intermediate status update for monitoring.
1601
+ # Log it, acknowledge it, but do not aggregate its content.
1602
+ log.debug(
1603
+ "%s Received and ignored intermediate status update from peer for sub-task %s.",
1604
+ component.log_identifier,
1605
+ sub_task_id,
1606
+ )
1607
+ # Reset the timeout since we received a status update
1608
+ await component.reset_peer_timeout(sub_task_id)
1609
+ message.call_acknowledgements()
1610
+ return
1611
+
1612
+ correlation_data = await component._claim_peer_sub_task_completion(sub_task_id)
1613
+ if not correlation_data:
1614
+ # The helper method logs the reason (timeout, already claimed, etc.)
1615
+ message.call_acknowledgements()
1616
+ return
1617
+
1618
+ async def _handle_final_peer_response():
1619
+ """
1620
+ Handles a final peer response by updating the completion counter and,
1621
+ if all peer tasks are complete, calling the re-trigger logic.
1622
+ """
1623
+ logical_task_id = correlation_data.get("logical_task_id")
1624
+ invocation_id = correlation_data.get("invocation_id")
1625
+
1626
+ if not logical_task_id or not invocation_id:
1627
+ log.error(
1628
+ "%s 'logical_task_id' or 'invocation_id' not found in correlation data for sub-task %s. Cannot proceed.",
1629
+ component.log_identifier,
1630
+ sub_task_id,
1631
+ )
1632
+ return
1633
+
1634
+ log_retrigger = (
1635
+ f"{component.log_identifier}[RetriggerManager:{logical_task_id}]"
1636
+ )
1637
+
1638
+ with component.active_tasks_lock:
1639
+ task_context = component.active_tasks.get(logical_task_id)
1640
+
1641
+ if not task_context:
1642
+ log.error(
1643
+ "%s TaskExecutionContext not found for task %s. Cannot process final peer response.",
1644
+ log_retrigger,
1645
+ logical_task_id,
1646
+ )
1647
+ return
1648
+
1649
+ final_text = ""
1650
+ artifact_summary = ""
1651
+ if isinstance(payload_to_queue, dict):
1652
+ if "result" in payload_to_queue:
1653
+ final_text = payload_to_queue["result"]
1654
+ elif "error" in payload_to_queue:
1655
+ final_text = (
1656
+ f"Peer agent returned an error: {payload_to_queue['error']}"
1657
+ )
1658
+ elif "status" in payload_to_queue: # It's a Task object
1659
+ try:
1660
+ task_obj = Task(**payload_to_queue)
1661
+ if task_obj.status and task_obj.status.message:
1662
+ final_text = get_text_from_message(task_obj.status.message)
1663
+
1664
+ if (
1665
+ task_obj.metadata
1666
+ and "produced_artifacts" in task_obj.metadata
1667
+ ):
1668
+ produced_artifacts = task_obj.metadata.get(
1669
+ "produced_artifacts", []
1670
+ )
1671
+ if produced_artifacts:
1672
+ peer_agent_name = task_obj.metadata.get(
1673
+ "agent_name", "A peer agent"
1674
+ )
1675
+ original_task_context = correlation_data.get(
1676
+ "original_task_context", {}
1677
+ )
1678
+ user_id = original_task_context.get("user_id")
1679
+ session_id = original_task_context.get("session_id")
1680
+
1681
+ header_text = f"Peer agent `{peer_agent_name}` created {len(produced_artifacts)} artifact(s):"
1682
+
1683
+ if user_id and session_id:
1684
+ artifact_summary = (
1685
+ await generate_artifact_metadata_summary(
1686
+ component=component,
1687
+ artifact_identifiers=produced_artifacts,
1688
+ user_id=user_id,
1689
+ session_id=session_id,
1690
+ app_name=peer_agent_name,
1691
+ header_text=header_text,
1692
+ )
1693
+ )
1694
+
1695
+ # Add guidance about artifact_return responsibility
1696
+ artifact_return_guidance = (
1697
+ f"\n\n**Note:** If any of these artifacts fulfill the user's request, "
1698
+ f"you should return them directly to the user using the "
1699
+ f"{EMBED_DELIMITER_OPEN}artifact_return:filename:version{EMBED_DELIMITER_CLOSE} embed. "
1700
+ f"This is more convenient for the user than just describing the artifacts. "
1701
+ f"Replace 'filename' and 'version' with the actual values from the artifact metadata above."
1702
+ )
1703
+ artifact_summary += artifact_return_guidance
1704
+ else:
1705
+ log.warning(
1706
+ "%s Could not generate artifact summary: missing user_id or session_id in correlation data.",
1707
+ log_retrigger,
1708
+ )
1709
+ artifact_summary = ""
1710
+ # Bubble up the peer's artifacts to the parent context
1711
+ _register_peer_artifacts_in_parent_context(
1712
+ task_context, task_obj, log_retrigger
1713
+ )
1714
+
1715
+ except Exception:
1716
+ final_text = json.dumps(payload_to_queue)
1717
+ else:
1718
+ final_text = json.dumps(payload_to_queue)
1719
+ elif isinstance(payload_to_queue, str):
1720
+ final_text = payload_to_queue
1721
+ else:
1722
+ final_text = str(payload_to_queue)
1723
+
1724
+ full_response_text = final_text
1725
+ if artifact_summary:
1726
+ full_response_text = f"{artifact_summary}\n---\n\nPeer Agent Response:\n\n{full_response_text}"
1727
+
1728
+ await _publish_peer_tool_result_notification(
1729
+ component=component,
1730
+ correlation_data=correlation_data,
1731
+ payload_to_queue=payload_to_queue,
1732
+ log_identifier=log_retrigger,
1733
+ )
1734
+
1735
+ current_result = {
1736
+ "adk_function_call_id": correlation_data.get("adk_function_call_id"),
1737
+ "peer_tool_name": correlation_data.get("peer_tool_name"),
1738
+ "payload": {"result": full_response_text},
1739
+ }
1740
+
1741
+ all_sub_tasks_completed = task_context.record_parallel_result(
1742
+ current_result, invocation_id
1743
+ )
1744
+ log.info(
1745
+ "%s Updated parallel counter for task %s: %s",
1746
+ log_retrigger,
1747
+ logical_task_id,
1748
+ task_context.parallel_tool_calls.get(invocation_id),
1749
+ )
1750
+
1751
+ if not all_sub_tasks_completed:
1752
+ log.info(
1753
+ "%s Waiting for more peer responses for task %s.",
1754
+ log_retrigger,
1755
+ logical_task_id,
1756
+ )
1757
+ return
1758
+
1759
+ log.info(
1760
+ "%s All peer responses received for task %s. Retriggering agent.",
1761
+ log_retrigger,
1762
+ logical_task_id,
1763
+ )
1764
+ results_to_inject = task_context.parallel_tool_calls.get(
1765
+ invocation_id, {}
1766
+ ).get("results", [])
1767
+
1768
+ await component._retrigger_agent_with_peer_responses(
1769
+ results_to_inject, correlation_data, task_context
1770
+ )
1771
+
1772
+ loop = component.get_async_loop()
1773
+ if loop and loop.is_running():
1774
+ asyncio.run_coroutine_threadsafe(_handle_final_peer_response(), loop)
1775
+ else:
1776
+ log.error(
1777
+ "%s Async loop not available. Cannot handle final peer response for sub-task %s.",
1778
+ component.log_identifier,
1779
+ sub_task_id,
1780
+ )
1781
+
1782
+ message.call_acknowledgements()
1783
+ log.info(
1784
+ "%s Acknowledged final peer response message for sub-task %s.",
1785
+ component.log_identifier,
1786
+ sub_task_id,
1787
+ )
1788
+
1789
+ except Exception as e:
1790
+ log.exception(
1791
+ "%s Unexpected error handling A2A response for sub-task %s: %s",
1792
+ component.log_identifier,
1793
+ sub_task_id,
1794
+ e,
1795
+ )
1796
+ try:
1797
+ message.call_negative_acknowledgements()
1798
+ log.warning(
1799
+ "%s NACKed peer response message for sub-task %s due to unexpected error.",
1800
+ component.log_identifier,
1801
+ sub_task_id,
1802
+ )
1803
+ except Exception as nack_e:
1804
+ log.error(
1805
+ "%s Failed to NACK peer response message for sub-task %s after error: %s",
1806
+ component.log_identifier,
1807
+ sub_task_id,
1808
+ nack_e,
1809
+ )
1810
+ component.handle_error(e, Event(EventType.MESSAGE, message))
1811
+
1812
+
1813
+ def publish_agent_card(component):
1814
+ """Publishes the agent's card to the discovery topic."""
1815
+ try:
1816
+ card_config = component.get_config("agent_card", {})
1817
+ agent_name = component.get_config("agent_name")
1818
+ display_name = component.get_config("display_name")
1819
+ namespace = component.get_config("namespace")
1820
+ supports_streaming = component.get_config("supports_streaming", False)
1821
+ peer_agents = component.peer_agents
1822
+
1823
+ agent_request_topic = get_agent_request_topic(namespace, agent_name)
1824
+ dynamic_url = f"solace:{agent_request_topic}"
1825
+
1826
+ # Define unique URIs for our custom extensions.
1827
+ DEPLOYMENT_EXTENSION_URI = "https://solace.com/a2a/extensions/sam/deployment"
1828
+ PEER_TOPOLOGY_EXTENSION_URI = (
1829
+ "https://solace.com/a2a/extensions/peer-agent-topology"
1830
+ )
1831
+ DISPLAY_NAME_EXTENSION_URI = "https://solace.com/a2a/extensions/display-name"
1832
+ TOOLS_EXTENSION_URI = "https://solace.com/a2a/extensions/sam/tools"
1833
+
1834
+ extensions_list = []
1835
+
1836
+ # Create the extension object for deployment tracking.
1837
+ deployment_config = component.get_config("deployment", {})
1838
+ deployment_id = deployment_config.get("id")
1839
+
1840
+ if deployment_id:
1841
+ deployment_extension = AgentExtension(
1842
+ uri=DEPLOYMENT_EXTENSION_URI,
1843
+ description="SAM deployment tracking for rolling updates",
1844
+ required=False,
1845
+ params={"id": deployment_id}
1846
+ )
1847
+ extensions_list.append(deployment_extension)
1848
+ log.debug(
1849
+ "%s Added deployment extension with ID: %s",
1850
+ component.log_identifier,
1851
+ deployment_id
1852
+ )
1853
+
1854
+ # Create the extension object for peer agents.
1855
+ if peer_agents:
1856
+ peer_topology_extension = AgentExtension(
1857
+ uri=PEER_TOPOLOGY_EXTENSION_URI,
1858
+ description="A list of peer agents this agent is configured to communicate with.",
1859
+ params={"peer_agent_names": list(peer_agents.keys())},
1860
+ )
1861
+ extensions_list.append(peer_topology_extension)
1862
+
1863
+ # Create the extension object for the UI display name.
1864
+ if display_name:
1865
+ display_name_extension = AgentExtension(
1866
+ uri=DISPLAY_NAME_EXTENSION_URI,
1867
+ description="A UI-friendly display name for the agent.",
1868
+ params={"display_name": display_name},
1869
+ )
1870
+ extensions_list.append(display_name_extension)
1871
+
1872
+ # Create the extension object for the agent's tools.
1873
+ dynamic_tools = getattr(component, "agent_card_tool_manifest", [])
1874
+ if dynamic_tools:
1875
+ # Ensure all tools have a 'tags' field to prevent validation errors.
1876
+ processed_tools = []
1877
+ for tool in dynamic_tools:
1878
+ if "tags" not in tool:
1879
+ log.debug(
1880
+ "%s Tool '%s' in manifest is missing 'tags' field. Defaulting to empty list.",
1881
+ component.log_identifier,
1882
+ tool.get("id", "unknown"),
1883
+ )
1884
+ tool["tags"] = []
1885
+ processed_tools.append(tool)
1886
+
1887
+ tools_params = ToolsExtensionParams(tools=processed_tools)
1888
+ tools_extension = AgentExtension(
1889
+ uri=TOOLS_EXTENSION_URI,
1890
+ description="A list of tools available to the agent.",
1891
+ params=tools_params.model_dump(exclude_none=True),
1892
+ )
1893
+ extensions_list.append(tools_extension)
1894
+
1895
+ # Build the capabilities object, including our custom extensions.
1896
+ capabilities = AgentCapabilities(
1897
+ streaming=supports_streaming,
1898
+ push_notifications=False,
1899
+ state_transition_history=False,
1900
+ extensions=extensions_list if extensions_list else None,
1901
+ )
1902
+
1903
+ skills_from_config = card_config.get("skills", [])
1904
+ # The 'tools' field is not part of the official AgentCard spec.
1905
+ # The tools are now included as an extension.
1906
+
1907
+ # Ensure all skills have a 'tags' field to prevent validation errors.
1908
+ processed_skills = []
1909
+ for skill in skills_from_config:
1910
+ if "tags" not in skill:
1911
+ skill["tags"] = []
1912
+ processed_skills.append(skill)
1913
+
1914
+ agent_card = AgentCard(
1915
+ name=agent_name,
1916
+ protocol_version=card_config.get("protocolVersion", "0.3.0"),
1917
+ version=component.HOST_COMPONENT_VERSION,
1918
+ url=dynamic_url,
1919
+ capabilities=capabilities,
1920
+ description=card_config.get("description", ""),
1921
+ skills=processed_skills,
1922
+ default_input_modes=card_config.get("defaultInputModes", ["text"]),
1923
+ default_output_modes=card_config.get("defaultOutputModes", ["text"]),
1924
+ documentation_url=card_config.get("documentationUrl"),
1925
+ provider=card_config.get("provider"),
1926
+ )
1927
+
1928
+ discovery_topic = get_discovery_topic(namespace)
1929
+
1930
+ component.publish_a2a_message(
1931
+ agent_card.model_dump(exclude_none=True), discovery_topic
1932
+ )
1933
+ log.debug(
1934
+ "%s Successfully published Agent Card to %s",
1935
+ component.log_identifier,
1936
+ discovery_topic,
1937
+ )
1938
+
1939
+ except Exception as e:
1940
+ log.exception(
1941
+ "%s Failed to publish Agent Card: %s", component.log_identifier, e
1942
+ )
1943
+ component.handle_error(e, None)
1944
+
1945
+
1946
+ def handle_sam_event(component, message, topic):
1947
+ """Handle incoming SAM system events."""
1948
+ try:
1949
+ payload = message.get_payload()
1950
+
1951
+ if not isinstance(payload, dict):
1952
+ log.warning("Invalid SAM event payload - not a dict")
1953
+ message.call_acknowledgements()
1954
+ return
1955
+
1956
+ event_type = payload.get("event_type")
1957
+ if not event_type:
1958
+ log.warning("SAM event missing event_type field")
1959
+ message.call_acknowledgements()
1960
+ return
1961
+
1962
+ log.info("%s Received SAM event: %s", component.log_identifier, event_type)
1963
+
1964
+ if event_type == "session.deleted":
1965
+ data = payload.get("data", {})
1966
+ session_id = data.get("session_id")
1967
+ user_id = data.get("user_id")
1968
+ agent_id = data.get("agent_id")
1969
+
1970
+ if not all([session_id, user_id, agent_id]):
1971
+ log.warning("Missing required fields in session.deleted event")
1972
+ message.call_acknowledgements()
1973
+ return
1974
+
1975
+ current_agent = component.get_config("agent_name")
1976
+
1977
+ if agent_id == current_agent:
1978
+ log.info(
1979
+ "%s Processing session.deleted event for session %s",
1980
+ component.log_identifier,
1981
+ session_id,
1982
+ )
1983
+ asyncio.create_task(
1984
+ cleanup_agent_session(component, session_id, user_id)
1985
+ )
1986
+ else:
1987
+ log.debug(
1988
+ "Session deletion event for different agent: %s != %s",
1989
+ agent_id,
1990
+ current_agent,
1991
+ )
1992
+ else:
1993
+ log.debug("Unhandled SAM event type: %s", event_type)
1994
+
1995
+ message.call_acknowledgements()
1996
+
1997
+ except Exception as e:
1998
+ log.error("Error handling SAM event %s: %s", topic, e)
1999
+ message.call_acknowledgements()
2000
+
2001
+
2002
+ async def cleanup_agent_session(component, session_id: str, user_id: str):
2003
+ """Clean up agent-side session data."""
2004
+ try:
2005
+ log.info("Starting cleanup for session %s, user %s", session_id, user_id)
2006
+
2007
+ if hasattr(component, "session_service") and component.session_service:
2008
+ agent_name = component.get_config("agent_name")
2009
+ log.info(
2010
+ "Deleting session %s from agent %s session service",
2011
+ session_id,
2012
+ agent_name,
2013
+ )
2014
+ await component.session_service.delete_session(
2015
+ app_name=agent_name, user_id=user_id, session_id=session_id
2016
+ )
2017
+ log.info("Successfully deleted session %s from session service", session_id)
2018
+ else:
2019
+ log.info("No session service available for cleanup")
2020
+
2021
+ with component.active_tasks_lock:
2022
+ tasks_to_cancel = []
2023
+ for task_id, context in component.active_tasks.items():
2024
+ if (
2025
+ hasattr(context, "a2a_context")
2026
+ and context.a2a_context.get("session_id") == session_id
2027
+ ):
2028
+ tasks_to_cancel.append(task_id)
2029
+
2030
+ for task_id in tasks_to_cancel:
2031
+ context = component.active_tasks.get(task_id)
2032
+ if context:
2033
+ context.cancel()
2034
+ log.info(
2035
+ "Cancelled task %s for deleted session %s", task_id, session_id
2036
+ )
2037
+
2038
+ log.info("Session cleanup completed for session %s", session_id)
2039
+
2040
+ except Exception as e:
2041
+ log.error("Error cleaning up session %s: %s", session_id, e)