solace-agent-mesh 1.6.1__py3-none-any.whl → 1.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (481) hide show
  1. solace_agent_mesh/agent/adk/alembic/README +74 -0
  2. solace_agent_mesh/agent/adk/alembic/env.py +77 -0
  3. solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
  4. solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
  5. solace_agent_mesh/agent/adk/alembic.ini +112 -0
  6. solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
  7. solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
  8. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
  9. solace_agent_mesh/agent/adk/callbacks.py +852 -109
  10. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
  11. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
  12. solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
  13. solace_agent_mesh/agent/adk/models/lite_llm.py +77 -21
  14. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
  15. solace_agent_mesh/agent/adk/runner.py +85 -20
  16. solace_agent_mesh/agent/adk/schema_migration.py +88 -0
  17. solace_agent_mesh/agent/adk/services.py +94 -18
  18. solace_agent_mesh/agent/adk/setup.py +281 -65
  19. solace_agent_mesh/agent/adk/stream_parser.py +231 -37
  20. solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
  21. solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
  22. solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
  23. solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
  24. solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
  25. solace_agent_mesh/agent/proxies/base/app.py +3 -2
  26. solace_agent_mesh/agent/proxies/base/component.py +188 -22
  27. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
  28. solace_agent_mesh/agent/sac/app.py +91 -3
  29. solace_agent_mesh/agent/sac/component.py +591 -157
  30. solace_agent_mesh/agent/sac/patch_adk.py +8 -16
  31. solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
  32. solace_agent_mesh/agent/tools/__init__.py +3 -0
  33. solace_agent_mesh/agent/tools/audio_tools.py +3 -3
  34. solace_agent_mesh/agent/tools/builtin_artifact_tools.py +710 -171
  35. solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
  36. solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
  37. solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
  38. solace_agent_mesh/agent/tools/time_tools.py +126 -0
  39. solace_agent_mesh/agent/tools/tool_config_types.py +57 -2
  40. solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
  41. solace_agent_mesh/agent/tools/web_tools.py +125 -17
  42. solace_agent_mesh/agent/utils/artifact_helpers.py +248 -6
  43. solace_agent_mesh/agent/utils/context_helpers.py +17 -0
  44. solace_agent_mesh/assets/docs/404.html +6 -6
  45. solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
  46. solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
  47. solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
  48. solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
  49. solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
  50. solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
  51. solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
  52. solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
  53. solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
  54. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
  55. solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
  56. solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
  57. solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
  58. solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
  59. solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
  60. solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
  61. solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
  62. solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
  63. solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
  64. solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
  65. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
  66. solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
  67. solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
  68. solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
  69. solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
  70. solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
  71. solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
  72. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
  73. solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
  74. solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
  75. solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
  76. solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
  77. solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
  78. solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
  79. solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
  80. solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
  81. solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
  82. solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
  83. solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
  84. solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
  85. solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
  86. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
  87. solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
  88. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
  89. solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
  90. solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
  91. solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
  92. solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
  93. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
  94. solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
  95. solace_agent_mesh/assets/docs/assets/js/e04b235d.06d23db6.js +1 -0
  96. solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
  97. solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
  98. solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
  99. solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
  100. solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
  101. solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
  102. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
  103. solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
  104. solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
  105. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
  106. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
  107. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
  108. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
  109. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +82 -68
  110. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
  111. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
  112. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
  113. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
  114. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
  115. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
  116. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
  117. solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
  118. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
  119. solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
  120. solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
  121. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
  122. solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
  123. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -49
  124. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
  125. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
  126. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
  127. solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
  128. solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +90 -0
  129. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
  130. solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
  131. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
  132. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +162 -171
  133. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
  134. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
  135. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
  136. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
  137. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
  138. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
  139. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
  140. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
  141. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
  142. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
  143. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
  144. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
  145. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
  146. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
  147. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
  148. solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
  149. solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
  150. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
  151. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
  152. solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
  153. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +112 -87
  154. solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
  155. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +87 -64
  156. solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
  157. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
  158. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -37
  159. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
  160. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
  161. solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
  162. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +311 -0
  163. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
  164. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
  165. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
  166. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
  167. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
  168. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
  169. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
  170. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
  171. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
  172. solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
  173. solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
  174. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  175. solace_agent_mesh/assets/docs/search-doc-1768329217460.json +1 -0
  176. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  177. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  178. solace_agent_mesh/cli/__init__.py +1 -1
  179. solace_agent_mesh/cli/commands/add_cmd/__init__.py +3 -1
  180. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
  181. solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
  182. solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
  183. solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
  184. solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
  185. solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
  186. solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
  187. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
  188. solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
  189. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
  190. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
  191. solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
  192. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
  193. solace_agent_mesh/cli/commands/run_cmd.py +64 -49
  194. solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
  195. solace_agent_mesh/cli/main.py +15 -0
  196. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-KnKMP_vb.js} +1 -1
  197. solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
  198. solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
  199. solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
  200. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CGk8Suyh.js +565 -0
  201. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  202. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  203. solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
  204. solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
  205. solace_agent_mesh/common/a2a/events.py +2 -1
  206. solace_agent_mesh/common/a2a/protocol.py +5 -0
  207. solace_agent_mesh/common/a2a/types.py +2 -1
  208. solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
  209. solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
  210. solace_agent_mesh/common/agent_registry.py +38 -11
  211. solace_agent_mesh/common/data_parts.py +144 -4
  212. solace_agent_mesh/common/error_handlers.py +83 -0
  213. solace_agent_mesh/common/exceptions.py +24 -0
  214. solace_agent_mesh/common/oauth/__init__.py +17 -0
  215. solace_agent_mesh/common/oauth/oauth_client.py +408 -0
  216. solace_agent_mesh/common/oauth/utils.py +50 -0
  217. solace_agent_mesh/common/rag_dto.py +156 -0
  218. solace_agent_mesh/common/sac/sam_component_base.py +97 -19
  219. solace_agent_mesh/common/sam_events/event_service.py +2 -2
  220. solace_agent_mesh/common/services/employee_service.py +1 -1
  221. solace_agent_mesh/common/utils/embeds/constants.py +1 -0
  222. solace_agent_mesh/common/utils/embeds/converter.py +1 -8
  223. solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
  224. solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
  225. solace_agent_mesh/common/utils/embeds/types.py +9 -0
  226. solace_agent_mesh/common/utils/log_formatters.py +20 -0
  227. solace_agent_mesh/common/utils/mime_helpers.py +12 -5
  228. solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
  229. solace_agent_mesh/common/utils/rbac_utils.py +69 -0
  230. solace_agent_mesh/common/utils/templates/__init__.py +8 -0
  231. solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
  232. solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
  233. solace_agent_mesh/config_portal/backend/common.py +12 -0
  234. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
  235. solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
  236. solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
  237. solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
  238. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
  239. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
  240. solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
  241. solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
  242. solace_agent_mesh/core_a2a/service.py +3 -2
  243. solace_agent_mesh/gateway/adapter/__init__.py +1 -0
  244. solace_agent_mesh/gateway/adapter/base.py +170 -0
  245. solace_agent_mesh/gateway/adapter/types.py +230 -0
  246. solace_agent_mesh/gateway/base/app.py +39 -2
  247. solace_agent_mesh/gateway/base/auth_interface.py +103 -0
  248. solace_agent_mesh/gateway/base/component.py +1027 -151
  249. solace_agent_mesh/gateway/generic/__init__.py +1 -0
  250. solace_agent_mesh/gateway/generic/app.py +50 -0
  251. solace_agent_mesh/gateway/generic/component.py +894 -0
  252. solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
  253. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
  254. solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
  255. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
  256. solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
  257. solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
  258. solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
  259. solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
  260. solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
  261. solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
  262. solace_agent_mesh/gateway/http_sse/app.py +40 -11
  263. solace_agent_mesh/gateway/http_sse/component.py +285 -160
  264. solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
  265. solace_agent_mesh/gateway/http_sse/main.py +68 -450
  266. solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
  267. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
  268. solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
  269. solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
  270. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +26 -3
  271. solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
  272. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
  273. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
  274. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
  275. solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
  276. solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
  277. solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
  278. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +8 -2
  279. solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
  280. solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
  281. solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
  282. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +177 -11
  283. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
  284. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
  285. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +256 -58
  286. solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
  287. solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
  288. solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
  289. solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
  290. solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
  291. solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -1
  292. solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
  293. solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
  294. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
  295. solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
  296. solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
  297. solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
  298. solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
  299. solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
  300. solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
  301. solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
  302. solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
  303. solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
  304. solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
  305. solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
  306. solace_agent_mesh/gateway/http_sse/routers/visualization.py +92 -9
  307. solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
  308. solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
  309. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
  310. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
  311. solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
  312. solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
  313. solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
  314. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
  315. solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
  316. solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
  317. solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
  318. solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
  319. solace_agent_mesh/services/__init__.py +0 -0
  320. solace_agent_mesh/services/platform/__init__.py +29 -0
  321. solace_agent_mesh/services/platform/alembic/env.py +85 -0
  322. solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
  323. solace_agent_mesh/services/platform/alembic.ini +109 -0
  324. solace_agent_mesh/services/platform/api/__init__.py +3 -0
  325. solace_agent_mesh/services/platform/api/dependencies.py +154 -0
  326. solace_agent_mesh/services/platform/api/main.py +314 -0
  327. solace_agent_mesh/services/platform/api/middleware.py +51 -0
  328. solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
  329. solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
  330. solace_agent_mesh/services/platform/app.py +215 -0
  331. solace_agent_mesh/services/platform/component.py +777 -0
  332. solace_agent_mesh/shared/__init__.py +14 -0
  333. solace_agent_mesh/shared/api/__init__.py +42 -0
  334. solace_agent_mesh/shared/auth/__init__.py +26 -0
  335. solace_agent_mesh/shared/auth/dependencies.py +204 -0
  336. solace_agent_mesh/shared/auth/middleware.py +347 -0
  337. solace_agent_mesh/shared/database/__init__.py +20 -0
  338. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
  339. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
  340. solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
  341. solace_agent_mesh/shared/exceptions/__init__.py +36 -0
  342. solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +19 -5
  343. solace_agent_mesh/shared/utils/__init__.py +21 -0
  344. solace_agent_mesh/templates/logging_config_template.yaml +48 -0
  345. solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
  346. solace_agent_mesh/templates/platform.yaml +49 -0
  347. solace_agent_mesh/templates/plugin_readme_template.md +3 -25
  348. solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
  349. solace_agent_mesh/templates/proxy_template.yaml +62 -0
  350. solace_agent_mesh/templates/webui.yaml +148 -6
  351. solace_agent_mesh/tools/web_search/__init__.py +18 -0
  352. solace_agent_mesh/tools/web_search/base.py +84 -0
  353. solace_agent_mesh/tools/web_search/google_search.py +247 -0
  354. solace_agent_mesh/tools/web_search/models.py +99 -0
  355. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
  356. solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
  357. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
  358. solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
  359. solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
  360. solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
  361. solace_agent_mesh/agent/adk/models/models_llm.txt +0 -142
  362. solace_agent_mesh/agent/agent_llm.txt +0 -378
  363. solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
  364. solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
  365. solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
  366. solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
  367. solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
  368. solace_agent_mesh/agent/testing/testing_llm.txt +0 -57
  369. solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
  370. solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
  371. solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
  372. solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
  373. solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
  374. solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
  375. solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
  376. solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
  377. solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
  378. solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
  379. solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
  380. solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
  381. solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
  382. solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
  383. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
  384. solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
  385. solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
  386. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
  387. solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
  388. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
  389. solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
  390. solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
  391. solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
  392. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
  393. solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
  394. solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
  395. solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
  396. solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +0 -1
  397. solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
  398. solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
  399. solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
  400. solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
  401. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
  402. solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
  403. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +0 -1
  404. solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
  405. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
  406. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
  407. solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
  408. solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
  409. solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
  410. solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
  411. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
  412. solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
  413. solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
  414. solace_agent_mesh/client/webui/frontend/static/assets/client-CaY59VuC.js +0 -25
  415. solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
  416. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
  417. solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
  418. solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
  419. solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
  420. solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
  421. solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
  422. solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
  423. solace_agent_mesh/common/common_llm.txt +0 -251
  424. solace_agent_mesh/common/common_llm_detail.txt +0 -2562
  425. solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
  426. solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
  427. solace_agent_mesh/common/sac/sac_llm.txt +0 -71
  428. solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
  429. solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
  430. solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
  431. solace_agent_mesh/common/services/providers/providers_llm.txt +0 -80
  432. solace_agent_mesh/common/services/services_llm.txt +0 -363
  433. solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
  434. solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
  435. solace_agent_mesh/common/utils/utils_llm.txt +0 -336
  436. solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
  437. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
  438. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
  439. solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
  440. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
  441. solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
  442. solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
  443. solace_agent_mesh/gateway/base/base_llm.txt +0 -224
  444. solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
  445. solace_agent_mesh/gateway/gateway_llm.txt +0 -373
  446. solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
  447. solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
  448. solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
  449. solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
  450. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
  451. solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
  452. solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -263
  453. solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
  454. solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
  455. solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
  456. solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
  457. solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
  458. solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
  459. solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
  460. solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
  461. solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
  462. solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
  463. solace_agent_mesh/llm.txt +0 -228
  464. solace_agent_mesh/llm_detail.txt +0 -2835
  465. solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
  466. solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
  467. solace_agent_mesh/templates/logging_config_template.ini +0 -45
  468. solace_agent_mesh/templates/templates_llm.txt +0 -147
  469. solace_agent_mesh-1.6.1.dist-info/RECORD +0 -525
  470. /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
  471. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
  472. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
  473. /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
  474. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
  475. /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
  476. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
  477. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
  478. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
  479. /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
  480. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
  481. {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
@@ -27,6 +27,8 @@ from a2a.types import (
27
27
  AgentCard,
28
28
  Artifact,
29
29
  CancelTaskRequest,
30
+ DataPart,
31
+ InternalError,
30
32
  Message,
31
33
  SendMessageRequest,
32
34
  SendStreamingMessageRequest,
@@ -44,6 +46,8 @@ from solace_ai_connector.common.log import log
44
46
  from datetime import datetime, timezone
45
47
 
46
48
  from ....common import a2a
49
+ from ....common.oauth import OAuth2Client, validate_https_url
50
+ from ....common.data_parts import AgentProgressUpdateData
47
51
  from ....agent.utils.artifact_helpers import format_artifact_uri
48
52
  from ..base.component import BaseProxyComponent
49
53
 
@@ -80,11 +84,29 @@ class A2AProxyComponent(BaseProxyComponent):
80
84
  # when multiple concurrent requests target the same agent
81
85
  self._oauth_token_cache: OAuth2TokenCache = OAuth2TokenCache()
82
86
 
87
+ # OAuth 2.0 client for protocol operations (no retry for A2A)
88
+ self._oauth_client = OAuth2Client()
89
+
83
90
  # Index agent configs by name for O(1) lookup (performance optimization)
84
91
  self._agent_config_by_name: Dict[str, Dict[str, Any]] = {
85
92
  agent["name"]: agent for agent in self.proxied_agents_config
86
93
  }
87
94
 
95
+ # NEW: OAuth 2.0 authorization code support (enterprise feature)
96
+ # Stores paused tasks waiting for user authorization
97
+ self._paused_a2a_oauth2_tasks: Dict[str, Dict[str, Any]] = {}
98
+ # Caches CredentialManagerWithDiscovery instances per agent
99
+ self._a2a_oauth2_credential_managers: Dict[str, Any] = {}
100
+
101
+ # NEW: Initialize enterprise features for OAuth2 support
102
+ try:
103
+ from solace_agent_mesh_enterprise.init_enterprise_component import (
104
+ init_enterprise_proxy_features
105
+ )
106
+ init_enterprise_proxy_features(self)
107
+ except ImportError:
108
+ pass # Enterprise not installed
109
+
88
110
  # OAuth 2.0 configuration is now validated by Pydantic models at app initialization
89
111
  # No need for separate _validate_oauth_config() method
90
112
 
@@ -100,11 +122,82 @@ class A2AProxyComponent(BaseProxyComponent):
100
122
  """
101
123
  return self._agent_config_by_name.get(agent_name)
102
124
 
125
+ async def _build_headers(
126
+ self,
127
+ agent_name: str,
128
+ agent_config: Dict[str, Any],
129
+ custom_headers_key: str,
130
+ use_auth: bool = True,
131
+ ) -> Dict[str, str]:
132
+ """
133
+ Builds HTTP headers for requests, applying authentication and custom headers.
134
+
135
+ Args:
136
+ agent_name: The name of the agent.
137
+ agent_config: The agent configuration dictionary.
138
+ custom_headers_key: Key to look up custom headers in config ('agent_card_headers' or 'task_headers').
139
+ use_auth: Whether to apply authentication headers.
140
+
141
+ Returns:
142
+ Dictionary of HTTP headers. Custom headers are applied after auth headers.
143
+ Note: For task invocations, the A2A SDK's AuthInterceptor may further
144
+ modify authentication headers after these are set.
145
+ """
146
+ headers: Dict[str, str] = {}
147
+
148
+ # Step 1: Add authentication headers if requested
149
+ if use_auth:
150
+ auth_config = agent_config.get("authentication")
151
+ if auth_config:
152
+ auth_type = auth_config.get("type")
153
+
154
+ # Determine auth type (with backward compatibility)
155
+ if not auth_type:
156
+ scheme = auth_config.get("scheme", "bearer")
157
+ auth_type = "static_bearer" if scheme == "bearer" else "static_apikey"
158
+
159
+ # Apply authentication based on type
160
+ if auth_type == "static_bearer":
161
+ token = auth_config.get("token")
162
+ if token:
163
+ headers["Authorization"] = f"Bearer {token}"
164
+ elif auth_type == "static_apikey":
165
+ token = auth_config.get("token")
166
+ if token:
167
+ headers["X-API-Key"] = token
168
+ elif auth_type == "oauth2_client_credentials":
169
+ # Fetch OAuth token
170
+ try:
171
+ access_token = await self._fetch_oauth2_token(agent_name, auth_config)
172
+ headers["Authorization"] = f"Bearer {access_token}"
173
+ except Exception as e:
174
+ log.error(
175
+ "%s Failed to obtain OAuth 2.0 token for headers: %s",
176
+ self.log_identifier,
177
+ e,
178
+ )
179
+ # Continue without auth header - let the request fail downstream
180
+
181
+ # Step 2: Add custom headers (these override auth headers)
182
+ custom_headers_list = agent_config.get(custom_headers_key)
183
+ if custom_headers_list:
184
+ for header_config in custom_headers_list:
185
+ header_name = header_config.get("name")
186
+ header_value = header_config.get("value")
187
+ if header_name and header_value:
188
+ headers[header_name] = header_value
189
+
190
+ return headers
191
+
103
192
  async def _fetch_agent_card(
104
193
  self, agent_config: Dict[str, Any]
105
194
  ) -> Optional[AgentCard]:
106
195
  """
107
196
  Fetches the AgentCard from a downstream A2A agent via HTTPS.
197
+
198
+ Applies authentication and custom headers based on configuration:
199
+ - If use_auth_for_agent_card=true, applies the configured authentication
200
+ - Custom agent_card_headers override authentication headers
108
201
  """
109
202
  agent_name = agent_config.get("name")
110
203
  agent_url = agent_config.get("url")
@@ -116,8 +209,27 @@ class A2AProxyComponent(BaseProxyComponent):
116
209
  return None
117
210
 
118
211
  try:
212
+ # Build headers based on configuration
213
+ use_auth = agent_config.get("use_auth_for_agent_card", False)
214
+ headers = await self._build_headers(
215
+ agent_name=agent_name,
216
+ agent_config=agent_config,
217
+ custom_headers_key="agent_card_headers",
218
+ use_auth=use_auth,
219
+ )
220
+
221
+ if headers:
222
+ log.debug(
223
+ "%s Fetching agent card with %d custom header(s) (auth=%s)",
224
+ log_identifier,
225
+ len(headers),
226
+ use_auth,
227
+ )
228
+ else:
229
+ log.debug("%s Fetching agent card without authentication", log_identifier)
230
+
119
231
  log.info("%s Fetching agent card from %s", log_identifier, agent_url)
120
- async with httpx.AsyncClient() as client:
232
+ async with httpx.AsyncClient(headers=headers) as client:
121
233
  resolver = A2ACardResolver(httpx_client=client, base_url=agent_url)
122
234
  agent_card = await resolver.get_agent_card()
123
235
  return agent_card
@@ -152,6 +264,9 @@ class A2AProxyComponent(BaseProxyComponent):
152
264
  f"{self.log_identifier}[ForwardRequest:{task_context.task_id}:{agent_name}]"
153
265
  )
154
266
 
267
+ # Store original request for potential resumption (OAuth2 authorization code flow)
268
+ task_context.original_request = request
269
+
155
270
  # Step 1: Initialize retry counter
156
271
  # Why only retry once: Prevents infinite loops on persistent auth failures.
157
272
  # First 401 may be due to token expiration between cache check and request;
@@ -159,7 +274,57 @@ class A2AProxyComponent(BaseProxyComponent):
159
274
  max_auth_retries: int = 1
160
275
  auth_retry_count: int = 0
161
276
 
162
- # Step 2: Create while loop for retry logic
277
+ # Step 2: Check for OAuth2 authorization code flow
278
+ # This auth type requires user interaction and can pause the task,
279
+ # so we check it before attempting normal request flow
280
+ agent_config = self._get_agent_config(agent_name)
281
+ auth_config = agent_config.get("authentication") if agent_config else None
282
+ auth_type = auth_config.get("type") if auth_config else None
283
+
284
+ if auth_type == "oauth2_authorization_code":
285
+ try:
286
+ from solace_agent_mesh_enterprise.auth.a2a import (
287
+ check_authorization_required,
288
+ request_authorization,
289
+ )
290
+
291
+ # Check if user authorization is needed
292
+ needs_auth = await check_authorization_required(
293
+ component=self,
294
+ agent_name=agent_name,
295
+ task_context=task_context,
296
+ )
297
+
298
+ if needs_auth:
299
+ # Pause task and request authorization
300
+ log.info(
301
+ "%s User authorization required for agent '%s'. Pausing task.",
302
+ log_identifier,
303
+ agent_name,
304
+ )
305
+ await request_authorization(
306
+ component=self,
307
+ agent_name=agent_name,
308
+ task_context=task_context,
309
+ )
310
+ return # Exit - task paused, will resume after OAuth callback
311
+
312
+ except ImportError:
313
+ log.error(
314
+ "%s Agent '%s' requires OAuth2 authorization code flow, "
315
+ "but solace-agent-mesh-enterprise is not installed.",
316
+ log_identifier,
317
+ agent_name,
318
+ )
319
+ raise ValueError(
320
+ f"Agent '{agent_name}' requires OAuth2 authorization code flow, "
321
+ "but solace-agent-mesh-enterprise is not installed."
322
+ )
323
+
324
+ # Step 3: Normal request flow for all other auth types
325
+ # (static_bearer, static_apikey, oauth2_client_credentials, or authorized oauth2_authorization_code)
326
+ received_final_task = False
327
+
163
328
  while auth_retry_count <= max_auth_retries:
164
329
  try:
165
330
  # Get or create A2AClient
@@ -184,6 +349,24 @@ class A2AProxyComponent(BaseProxyComponent):
184
349
  # Extract the Message from the request params
185
350
  message_to_send = request.params.message
186
351
 
352
+ # Check if this is a RUN_BASED request by inspecting message metadata
353
+ # For RUN_BASED requests, omit context_id to indicate independent tasks
354
+ if message_to_send.metadata:
355
+ session_behavior = message_to_send.metadata.get("sessionBehavior")
356
+ if session_behavior:
357
+ session_behavior = str(session_behavior).upper()
358
+ if session_behavior == "RUN_BASED" and message_to_send.context_id:
359
+ # For RUN_BASED requests, omit context_id entirely
360
+ # Each request is independent with no logical grouping
361
+ log.debug(
362
+ "%s RUN_BASED request detected. Omitting context_id "
363
+ "(independent task)",
364
+ log_identifier,
365
+ )
366
+ message_to_send = message_to_send.model_copy(
367
+ update={"context_id": None}
368
+ )
369
+
187
370
  # WORKAROUND: The A2A SDK has a bug in ClientTaskManager that breaks streaming.
188
371
  # For streaming requests, we bypass the Client.send_message() method and call
189
372
  # the transport directly to avoid the buggy ClientTaskManager.
@@ -202,6 +385,10 @@ class A2AProxyComponent(BaseProxyComponent):
202
385
  await self._process_downstream_response(
203
386
  raw_event, task_context, client, agent_name
204
387
  )
388
+ # Check if this is a final task
389
+ if isinstance(raw_event, Task) and raw_event.status:
390
+ if raw_event.status.state in [TaskState.completed, TaskState.failed, TaskState.canceled]:
391
+ received_final_task = True
205
392
  else:
206
393
  # Non-streaming: use normal client method (works fine)
207
394
  log.debug(
@@ -214,21 +401,51 @@ class A2AProxyComponent(BaseProxyComponent):
214
401
  await self._process_downstream_response(
215
402
  event, task_context, client, agent_name
216
403
  )
404
+ # Check if this is a final task (event is tuple of (Task, Optional[UpdateEvent]))
405
+ if isinstance(event, tuple) and len(event) > 0:
406
+ task = event[0]
407
+ if isinstance(task, Task) and task.status:
408
+ if task.status.state in [TaskState.completed, TaskState.failed, TaskState.canceled]:
409
+ received_final_task = True
217
410
  elif isinstance(request, CancelTaskRequest):
218
- # Forward cancel request to downstream agent
411
+ # Forward cancel request to downstream agent using the downstream task ID
412
+ # The request.params.id contains SAM's task ID, but we need to send
413
+ # the downstream agent's task ID for the cancel to work
414
+
415
+ if not task_context.downstream_task_id:
416
+ log.error(
417
+ "%s Cannot forward cancel request: downstream task ID not yet captured for SAM task %s",
418
+ log_identifier,
419
+ task_context.task_id,
420
+ )
421
+ # Create an error response
422
+ from a2a.types import InvalidRequestError
423
+ error = InvalidRequestError(
424
+ message=f"Cannot cancel task {task_context.task_id}: downstream task ID not available",
425
+ data={"taskId": task_context.task_id}
426
+ )
427
+ # Publish error response
428
+ await self._publish_error_response(error, task_context.a2a_context)
429
+ break
430
+
219
431
  log.info(
220
- "%s Forwarding cancel request for task %s to downstream agent.",
432
+ "%s Forwarding cancel request for task %s (SAM ID: %s, downstream ID: %s) to downstream agent.",
221
433
  log_identifier,
222
- request.params.id,
434
+ task_context.downstream_task_id,
435
+ task_context.task_id,
436
+ task_context.downstream_task_id,
223
437
  )
224
- # Use the modern client's cancel_task method
225
- # Note: Pass the entire params object (TaskIdParams) instead of just the id string
226
- # to work around an SDK bug where it doesn't properly handle string inputs
438
+
439
+ # Create new params with the downstream task ID
440
+ from a2a.types import TaskIdParams
441
+ downstream_params = TaskIdParams(id=task_context.downstream_task_id)
442
+
443
+ # Use the modern client's cancel_task method with the downstream task ID
227
444
  result = await client.cancel_task(
228
- request.params, context=call_context
445
+ downstream_params, context=call_context
229
446
  )
230
447
  # Publish the canceled task response
231
- await self._publish_final_response(result, task_context.a2a_context)
448
+ await self._publish_task_response(result, task_context.a2a_context)
232
449
  else:
233
450
  log.warning(
234
451
  "%s Unhandled request type for forwarding: %s",
@@ -262,6 +479,22 @@ class A2AProxyComponent(BaseProxyComponent):
262
479
 
263
480
  except A2AClientJSONRPCError as e:
264
481
  # Handle JSON-RPC protocol errors
482
+
483
+ # Special case: Task already in terminal state (canceled/completed/failed)
484
+ # This is not a fatal error - the cancellation is effectively a no-op
485
+ if (e.error.code == -32002 and
486
+ "cannot be canceled" in e.error.message.lower() and
487
+ isinstance(request, CancelTaskRequest)):
488
+ log.warning(
489
+ "%s Task %s is already in terminal state: %s. Treating as successful cancellation.",
490
+ log_identifier,
491
+ task_context.downstream_task_id,
492
+ e.error.message,
493
+ )
494
+ # Task is already done - return success (cancellation is effectively complete)
495
+ # We don't need to publish a response because the task already sent its final response
496
+ break
497
+
265
498
  log.error(
266
499
  "%s JSON-RPC error from agent '%s': %s",
267
500
  log_identifier,
@@ -335,6 +568,20 @@ class A2AProxyComponent(BaseProxyComponent):
335
568
  # and publish an error response.
336
569
  raise
337
570
 
571
+ # After retry loop completes - check if we received a final task
572
+ # This detects cases where the stream closes without error but also without final response
573
+ if not received_final_task and isinstance(request, (SendStreamingMessageRequest, SendMessageRequest)):
574
+ from ....common.a2a import create_error_response
575
+
576
+ error_msg = f"Remote agent '{agent_name}' disconnected without completing the task. Agent may have crashed."
577
+ log.error("%s %s", log_identifier, error_msg)
578
+
579
+ error = InternalError(message=error_msg, data={"agent_name": agent_name})
580
+ reply_topic = task_context.a2a_context.get("reply_to_topic")
581
+ if reply_topic:
582
+ response = create_error_response(error=error, request_id=task_context.a2a_context.get("jsonrpc_request_id"))
583
+ self._publish_a2a_message(response.model_dump(exclude_none=True), reply_topic)
584
+
338
585
  async def _handle_auth_error(
339
586
  self, agent_name: str, task_context: ProxyTaskContext
340
587
  ) -> bool:
@@ -486,18 +733,8 @@ class A2AProxyComponent(BaseProxyComponent):
486
733
  "'token_url', 'client_id', and 'client_secret'."
487
734
  )
488
735
 
489
- # SECURITY: Enforce HTTPS for token URL
490
- parsed_url = urlparse(token_url)
491
- if parsed_url.scheme != "https":
492
- log.error(
493
- "%s OAuth 2.0 token_url must use HTTPS for security. Got scheme: '%s'",
494
- log_identifier,
495
- parsed_url.scheme,
496
- )
497
- raise ValueError(
498
- f"{log_identifier} OAuth 2.0 token_url must use HTTPS for security. "
499
- f"Got: {parsed_url.scheme}://"
500
- )
736
+ # SECURITY: Enforce HTTPS for token URL using common utility
737
+ validate_https_url(token_url)
501
738
 
502
739
  # Step 3: Extract optional parameters
503
740
  scope = auth_config.get("scope", "")
@@ -515,49 +752,32 @@ class A2AProxyComponent(BaseProxyComponent):
515
752
  )
516
753
 
517
754
  try:
518
- # Step 5: Create temporary httpx client with 30-second timeout
519
- async with httpx.AsyncClient(timeout=30.0) as client:
520
- # Step 6: Execute POST request
521
- # SECURITY: client_secret is sent in POST body (not logged or in URL)
522
- response = await client.post(
523
- token_url,
524
- data={
525
- "grant_type": "client_credentials",
526
- "client_id": client_id,
527
- "client_secret": client_secret,
528
- "scope": scope,
529
- },
530
- headers={
531
- "Content-Type": "application/x-www-form-urlencoded",
532
- "Accept": "application/json",
533
- },
534
- )
535
- response.raise_for_status()
536
-
537
- # Step 7: Parse response
538
- token_response = response.json()
539
- access_token = token_response.get("access_token")
755
+ # Step 5: Fetch token using common OAuth client (no retry for A2A)
756
+ token_data = await self._oauth_client.fetch_client_credentials_token(
757
+ token_url=token_url,
758
+ client_id=client_id,
759
+ client_secret=client_secret,
760
+ scope=scope,
761
+ verify=True,
762
+ timeout=30.0,
763
+ )
540
764
 
541
- if not access_token:
542
- raise ValueError(
543
- f"{log_identifier} Token response missing 'access_token' field. "
544
- f"Response keys: {list(token_response.keys())}"
545
- )
765
+ access_token = token_data["access_token"]
546
766
 
547
- # Step 8: Cache the token
548
- await self._oauth_token_cache.set(
549
- agent_name, access_token, cache_duration
550
- )
767
+ # Step 6: Cache the token
768
+ await self._oauth_token_cache.set(
769
+ agent_name, access_token, cache_duration
770
+ )
551
771
 
552
- # Step 9: Log success
553
- log.info(
554
- "%s Successfully obtained OAuth 2.0 token (cached for %ds)",
555
- log_identifier,
556
- cache_duration,
557
- )
772
+ # Step 7: Log success
773
+ log.info(
774
+ "%s Successfully obtained OAuth 2.0 token (cached for %ds)",
775
+ log_identifier,
776
+ cache_duration,
777
+ )
558
778
 
559
- # Step 10: Return access token
560
- return access_token
779
+ # Step 8: Return access token
780
+ return access_token
561
781
 
562
782
  except httpx.HTTPStatusError as e:
563
783
  log.error(
@@ -597,6 +817,7 @@ class A2AProxyComponent(BaseProxyComponent):
597
817
  - static_bearer: Static bearer token authentication
598
818
  - static_apikey: Static API key authentication
599
819
  - oauth2_client_credentials: OAuth 2.0 Client Credentials flow with automatic token refresh
820
+ - oauth2_authorization_code: OAuth 2.0 Authorization Code flow
600
821
 
601
822
  For backward compatibility, legacy configurations without a 'type' field
602
823
  will have their type inferred from the 'scheme' field.
@@ -622,6 +843,20 @@ class A2AProxyComponent(BaseProxyComponent):
622
843
  log.error(f"Agent card not found for '{agent_name}' in registry.")
623
844
  return None
624
845
 
846
+ # Check if we should use the configured URL or the agent card URL
847
+ use_agent_card_url = agent_config.get("use_agent_card_url", True)
848
+ if not use_agent_card_url:
849
+ # Override the agent card URL with the configured URL
850
+ configured_url = agent_config.get("url")
851
+ log.info(
852
+ "%s Overriding agent card URL with configured URL for agent '%s': %s",
853
+ self.log_identifier,
854
+ agent_name,
855
+ configured_url,
856
+ )
857
+ # Create a modified copy of the agent card with the configured URL
858
+ agent_card = agent_card.model_copy(update={"url": configured_url})
859
+
625
860
  # Resolve timeout - ensure we always have a valid timeout value
626
861
  default_timeout = self.get_config("default_request_timeout_seconds", 300)
627
862
  agent_timeout = agent_config.get("request_timeout_seconds")
@@ -629,7 +864,18 @@ class A2AProxyComponent(BaseProxyComponent):
629
864
  agent_timeout = default_timeout
630
865
  log.info("Using timeout of %ss for agent '%s'.", agent_timeout, agent_name)
631
866
 
632
- # Create a new httpx client with the specific timeout for this agent
867
+ # Build custom headers for task invocation
868
+ # Note: We build headers here but apply them via the httpx client
869
+ # The A2A SDK's AuthInterceptor will add auth headers via the credential store,
870
+ # but we want custom headers to override those, so we apply them at the client level
871
+ task_headers = await self._build_headers(
872
+ agent_name=agent_name,
873
+ agent_config=agent_config,
874
+ custom_headers_key="task_headers",
875
+ use_auth=False, # Auth will be handled by AuthInterceptor below
876
+ )
877
+
878
+ # Create a new httpx client with the specific timeout and custom headers for this agent
633
879
  # httpx.Timeout requires explicit values for connect, read, write, and pool
634
880
  httpx_client_for_agent = httpx.AsyncClient(
635
881
  timeout=httpx.Timeout(
@@ -637,9 +883,18 @@ class A2AProxyComponent(BaseProxyComponent):
637
883
  read=agent_timeout,
638
884
  write=agent_timeout,
639
885
  pool=agent_timeout,
640
- )
886
+ ),
887
+ headers=task_headers if task_headers else None,
641
888
  )
642
889
 
890
+ if task_headers:
891
+ log.info(
892
+ "%s Applied %d custom task header(s) for agent '%s'",
893
+ self.log_identifier,
894
+ len(task_headers),
895
+ agent_name,
896
+ )
897
+
643
898
  # Setup authentication if configured
644
899
  auth_config = agent_config.get("authentication")
645
900
  if auth_config:
@@ -712,10 +967,63 @@ class A2AProxyComponent(BaseProxyComponent):
712
967
  )
713
968
  raise
714
969
 
970
+ elif auth_type == "oauth2_authorization_code":
971
+ # NEW: OAuth 2.0 Authorization Code Flow (enterprise feature)
972
+ # At this point, user has already authorized (checked in _forward_request)
973
+ # We just need to get the access token from enterprise helpers
974
+ try:
975
+ from solace_agent_mesh_enterprise.auth.a2a import get_access_token
976
+
977
+ # Get access token (enterprise handles refresh if needed)
978
+ access_token = await get_access_token(
979
+ component=self,
980
+ agent_name=agent_name,
981
+ task_context=task_context,
982
+ )
983
+
984
+ if not access_token:
985
+ raise ValueError(
986
+ f"No OAuth2 credential found for agent '{agent_name}'. "
987
+ "User authorization should have completed in _forward_request()."
988
+ )
989
+
990
+ # Find the OAuth2 authorization code scheme name from agent card
991
+ oauth_scheme_name = None
992
+ if agent_card and agent_card.security_schemes:
993
+ for scheme_name, scheme_wrapper in agent_card.security_schemes.items():
994
+ scheme = scheme_wrapper.root
995
+ if (hasattr(scheme, 'type') and scheme.type.lower() == 'oauth2' and
996
+ hasattr(scheme, 'flows') and scheme.flows and
997
+ scheme.flows.authorization_code):
998
+ oauth_scheme_name = scheme_name
999
+ break
1000
+
1001
+ # Fallback if not found
1002
+ if not oauth_scheme_name:
1003
+ oauth_scheme_name = "oauth2_authorization_code"
1004
+ log.warning(
1005
+ "%s No OAuth2 authorization code scheme found in agent card, using default name",
1006
+ self.log_identifier
1007
+ )
1008
+
1009
+ # Store in credential store for AuthInterceptor
1010
+ await self._credential_store.set_credentials(
1011
+ session_id, oauth_scheme_name, access_token
1012
+ )
1013
+
1014
+ except ImportError:
1015
+ log.error(
1016
+ "%s OAuth2 authorization code requires solace-agent-mesh-enterprise package",
1017
+ self.log_identifier,
1018
+ )
1019
+ raise ValueError(
1020
+ "OAuth2 authorization code requires solace-agent-mesh-enterprise package"
1021
+ )
1022
+
715
1023
  else:
716
1024
  raise ValueError(
717
1025
  f"Unsupported authentication type '{auth_type}' for agent '{agent_name}'. "
718
- f"Supported types: static_bearer, static_apikey, oauth2_client_credentials."
1026
+ f"Supported types: static_bearer, static_apikey, oauth2_client_credentials, oauth2_authorization_code."
719
1027
  )
720
1028
 
721
1029
  # Create ClientConfig for the modern client
@@ -981,6 +1289,79 @@ class A2AProxyComponent(BaseProxyComponent):
981
1289
  type(event_payload).__name__,
982
1290
  )
983
1291
 
1292
+ # Convert TextParts to AgentProgressUpdateData for intermediate status updates if configured
1293
+ # Only convert non-final status updates; final status updates are used to construct the final Task
1294
+ if isinstance(event_payload, TaskStatusUpdateEvent) and not event_payload.final:
1295
+ agent_config = self._get_agent_config(agent_name)
1296
+ convert_progress = agent_config.get("convert_progress_updates", True) if agent_config else True
1297
+
1298
+ # DEBUG: Log config lookup results
1299
+ log.info(
1300
+ "%s DEBUG convert_progress_updates: agent_name='%s', agent_config_name='%s', agent_config_keys=%s, convert_progress_value=%s, convert_progress=%s",
1301
+ log_identifier,
1302
+ agent_name,
1303
+ agent_config.get('name') if agent_config else None,
1304
+ list(agent_config.keys()) if agent_config else None,
1305
+ agent_config.get("convert_progress_updates") if agent_config else None,
1306
+ convert_progress,
1307
+ )
1308
+
1309
+ if convert_progress and event_payload.status and event_payload.status.message:
1310
+ message = event_payload.status.message
1311
+ original_parts = a2a.get_parts_from_message(message)
1312
+
1313
+ if original_parts:
1314
+ converted_parts = []
1315
+ text_parts_converted = 0
1316
+
1317
+ for part in original_parts:
1318
+ if isinstance(part, TextPart) and part.text:
1319
+ # Convert TextPart to DataPart with AgentProgressUpdateData
1320
+ progress_data = AgentProgressUpdateData(
1321
+ type="agent_progress_update",
1322
+ status_text=part.text
1323
+ )
1324
+ data_part = DataPart(
1325
+ kind="data",
1326
+ data=progress_data.model_dump(),
1327
+ metadata=part.metadata
1328
+ )
1329
+ converted_parts.append(data_part)
1330
+ text_parts_converted += 1
1331
+ else:
1332
+ # Keep non-text parts as-is
1333
+ converted_parts.append(part)
1334
+
1335
+ if text_parts_converted > 0:
1336
+ # Update the message with converted parts
1337
+ event_payload.status.message = a2a.update_message_parts(
1338
+ message, converted_parts
1339
+ )
1340
+ log.debug(
1341
+ "%s Converted %d TextPart(s) to AgentProgressUpdateData in status update",
1342
+ log_identifier,
1343
+ text_parts_converted,
1344
+ )
1345
+
1346
+ # Capture the downstream task ID before we replace it
1347
+ # This is needed for forwarding cancellation requests to the downstream agent
1348
+ downstream_id = None
1349
+ if hasattr(event_payload, "task_id") and event_payload.task_id:
1350
+ downstream_id = event_payload.task_id
1351
+ elif hasattr(event_payload, "id") and event_payload.id:
1352
+ downstream_id = event_payload.id
1353
+
1354
+ # Store the downstream task ID in the context if we haven't already
1355
+ if downstream_id and not task_context.downstream_task_id:
1356
+ task_context.downstream_task_id = downstream_id
1357
+ log.debug(
1358
+ "%s Captured downstream task ID: %s (SAM task ID: %s)",
1359
+ log_identifier,
1360
+ downstream_id,
1361
+ task_context.task_id,
1362
+ )
1363
+
1364
+ # Replace the downstream task ID with SAM's task ID for upstream responses
984
1365
  original_task_id = task_context.task_id
985
1366
  if hasattr(event_payload, "task_id") and event_payload.task_id:
986
1367
  event_payload.task_id = original_task_id
@@ -1031,20 +1412,123 @@ class A2AProxyComponent(BaseProxyComponent):
1031
1412
  Part(root=summary_message_part)
1032
1413
  )
1033
1414
 
1034
- if isinstance(event_payload, (Task, TaskStatusUpdateEvent)):
1035
- if isinstance(event_payload, Task):
1036
- await self._publish_final_response(
1037
- event_payload, task_context.a2a_context
1415
+ # Convert text-only TaskArtifactUpdateEvents to TaskStatusUpdateEvents
1416
+ # Some A2A agents send text content as artifacts, which SAM expects as status updates
1417
+ if isinstance(event_payload, TaskArtifactUpdateEvent):
1418
+ artifact = event_payload.artifact
1419
+ if a2a.is_text_only_artifact(artifact):
1420
+ log.info(
1421
+ "%s Converting text-only artifact to status update",
1422
+ log_identifier,
1423
+ )
1424
+ # Extract text from text-only artifact
1425
+ text_content = "\n".join(a2a.get_text_content_from_artifact(artifact))
1426
+
1427
+ # Convert to status update
1428
+ text_message = a2a.create_agent_text_message(
1429
+ text=text_content,
1430
+ task_id=event_payload.task_id,
1431
+ context_id=event_payload.context_id,
1432
+ )
1433
+
1434
+ status_event = TaskStatusUpdateEvent(
1435
+ task_id=event_payload.task_id,
1436
+ context_id=event_payload.context_id,
1437
+ kind="status-update",
1438
+ status=TaskStatus(state=TaskState.working, message=text_message),
1439
+ final=False,
1440
+ metadata=event_payload.metadata,
1038
1441
  )
1039
- else:
1040
- await self._publish_status_update(
1041
- event_payload, task_context.a2a_context
1442
+
1443
+ # Replace event_payload with the converted status update
1444
+ event_payload = status_event
1445
+ log.info(
1446
+ "%s Converted text-only artifact (length: %d bytes) to status update",
1447
+ log_identifier,
1448
+ len(text_content.encode("utf-8")),
1449
+ )
1450
+
1451
+ # Determine if this is a terminal event requiring cleanup
1452
+ should_cleanup_task = False
1453
+
1454
+ # Route based on event type
1455
+ if isinstance(event_payload, Task):
1456
+ # Discard initial Task events (non-completed states)
1457
+ # The final Task will be constructed from the final status update
1458
+ if event_payload.status.state != TaskState.completed:
1459
+ log.debug(
1460
+ "%s Discarding Task event with state=%s (not completed). Final Task will be constructed from final status update.",
1461
+ log_identifier,
1462
+ event_payload.status.state,
1463
+ )
1464
+ # Don't publish, don't cleanup - wait for final status update
1465
+ return
1466
+
1467
+ # Forward completed Task to reply topic
1468
+ await self._publish_task_response(event_payload, task_context.a2a_context)
1469
+
1470
+ # Completed Task is terminal - cleanup
1471
+ should_cleanup_task = True
1472
+ log.debug(
1473
+ "%s Task in terminal state: %s",
1474
+ log_identifier,
1475
+ event_payload.status.state,
1476
+ )
1477
+
1478
+ elif isinstance(event_payload, TaskStatusUpdateEvent):
1479
+ # Forward status update to status topic
1480
+ await self._publish_status_update(event_payload, task_context.a2a_context)
1481
+
1482
+ # Check if final event - construct and send Task
1483
+ if event_payload.final:
1484
+ log.info(
1485
+ "%s Received final status update (final=true). Constructing completed Task.",
1486
+ log_identifier,
1042
1487
  )
1488
+
1489
+ # Construct Task from final status update
1490
+ # Copy the status but ensure state is "completed"
1491
+ final_task_status = TaskStatus(
1492
+ state=TaskState.completed,
1493
+ message=event_payload.status.message if event_payload.status else None,
1494
+ )
1495
+
1496
+ final_task = Task(
1497
+ id=event_payload.task_id,
1498
+ context_id=event_payload.context_id,
1499
+ status=final_task_status,
1500
+ artifacts=None, # Artifacts come via separate events
1501
+ metadata=event_payload.metadata,
1502
+ )
1503
+
1504
+ # Add produced_artifacts metadata if any artifacts were processed
1505
+ if produced_artifacts:
1506
+ if not final_task.metadata:
1507
+ final_task.metadata = {}
1508
+ final_task.metadata["produced_artifacts"] = produced_artifacts
1509
+ log.info(
1510
+ "%s Added manifest of %d produced artifacts to final Task metadata.",
1511
+ log_identifier,
1512
+ len(produced_artifacts),
1513
+ )
1514
+
1515
+ # Publish the constructed Task
1516
+ await self._publish_task_response(final_task, task_context.a2a_context)
1517
+
1518
+ should_cleanup_task = True
1519
+ log.debug(
1520
+ "%s Published final Task constructed from status update",
1521
+ log_identifier,
1522
+ )
1523
+
1043
1524
  elif isinstance(event_payload, TaskArtifactUpdateEvent):
1525
+ # Forward artifact update to status topic
1044
1526
  await self._publish_artifact_update(event_payload, task_context.a2a_context)
1527
+
1045
1528
  elif isinstance(event_payload, Message):
1529
+ # Wrap Message in Task for gateway compatibility
1046
1530
  log.info(
1047
- "%s Received a direct Message response. Wrapping in a completed Task.",
1531
+ "%s Received direct Message response. Wrapping in completed Task.",
1048
1532
  log_identifier,
1049
1533
  )
1050
1534
  final_task = Task(
@@ -1053,7 +1537,7 @@ class A2AProxyComponent(BaseProxyComponent):
1053
1537
  status=TaskStatus(state=TaskState.completed, message=event_payload),
1054
1538
  )
1055
1539
 
1056
- # Add produced_artifacts metadata to the wrapped Task if any artifacts were processed
1540
+ # Add produced_artifacts metadata if any artifacts were processed
1057
1541
  if produced_artifacts:
1058
1542
  final_task.metadata = {"produced_artifacts": produced_artifacts}
1059
1543
  log.info(
@@ -1062,11 +1546,24 @@ class A2AProxyComponent(BaseProxyComponent):
1062
1546
  len(produced_artifacts),
1063
1547
  )
1064
1548
 
1065
- await self._publish_final_response(final_task, task_context.a2a_context)
1549
+ await self._publish_task_response(final_task, task_context.a2a_context)
1550
+ should_cleanup_task = True
1551
+
1066
1552
  else:
1067
1553
  log.warning(
1068
- f"Received unhandled response payload type: {type(event_payload)}"
1554
+ "%s Received unhandled response payload type: %s",
1555
+ log_identifier,
1556
+ type(event_payload).__name__,
1557
+ )
1558
+
1559
+ # Cleanup task state if terminal event detected
1560
+ if should_cleanup_task:
1561
+ log.info(
1562
+ "%s Terminal event detected for task %s. Cleaning up state.",
1563
+ log_identifier,
1564
+ task_context.task_id,
1069
1565
  )
1566
+ self._cleanup_task_state(task_context.task_id)
1070
1567
 
1071
1568
  def clear_client_cache(self):
1072
1569
  """