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
@@ -1,9 +1,10 @@
1
1
  """
2
2
  API Router for submitting and managing tasks to agents.
3
+ Includes background task status endpoints.
3
4
  """
5
+ from __future__ import annotations
4
6
 
5
7
  import logging
6
- from datetime import datetime
7
8
  from typing import TYPE_CHECKING
8
9
 
9
10
  import yaml
@@ -14,13 +15,21 @@ from a2a.types import (
14
15
  SendStreamingMessageRequest,
15
16
  SendStreamingMessageSuccessResponse,
16
17
  )
17
- from fastapi import APIRouter, Depends, HTTPException, Response, status
18
+ from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
18
19
  from fastapi import Request as FastAPIRequest
20
+ from pydantic import BaseModel
19
21
  from sqlalchemy.orm import Session as DBSession
20
22
 
23
+ from ....gateway.http_sse.services.project_service import ProjectService
24
+
25
+ from ....agent.utils.artifact_helpers import (
26
+ get_artifact_info_list,
27
+ )
28
+
21
29
  from ....common import a2a
22
30
  from ....gateway.http_sse.dependencies import (
23
31
  get_db,
32
+ get_project_service_optional,
24
33
  get_sac_component,
25
34
  get_session_business_service,
26
35
  get_session_manager,
@@ -31,11 +40,12 @@ from ....gateway.http_sse.dependencies import (
31
40
  )
32
41
  from ....gateway.http_sse.repository.entities import Task
33
42
  from ....gateway.http_sse.repository.interfaces import ITaskRepository
43
+ from ....gateway.http_sse.repository.task_repository import TaskRepository
34
44
  from ....gateway.http_sse.services.session_service import SessionService
35
45
  from ....gateway.http_sse.services.task_service import TaskService
36
46
  from ....gateway.http_sse.session_manager import SessionManager
37
- from ....gateway.http_sse.shared.pagination import PaginationParams
38
- from ....gateway.http_sse.shared.types import UserId
47
+ from solace_agent_mesh.shared.api.pagination import PaginationParams
48
+ from solace_agent_mesh.shared.utils.types import UserId
39
49
  from ..utils.stim_utils import create_stim_from_task_data
40
50
 
41
51
  if TYPE_CHECKING:
@@ -46,20 +56,293 @@ router = APIRouter()
46
56
  log = logging.getLogger(__name__)
47
57
 
48
58
 
59
+ # Background Task Status Models and Endpoints
60
+ class TaskStatusResponse(BaseModel):
61
+ """Response model for task status queries."""
62
+ task: Task
63
+ is_running: bool
64
+ is_background: bool
65
+ can_reconnect: bool
66
+
67
+
68
+ @router.get("/tasks/{task_id}/status", response_model=TaskStatusResponse, tags=["Tasks"])
69
+ async def get_task_status(
70
+ task_id: str,
71
+ db: DBSession = Depends(get_db),
72
+ ):
73
+ """
74
+ Get the current status of a task.
75
+ Used by frontend to check if a background task is still running.
76
+
77
+ Args:
78
+ task_id: The task ID to query
79
+
80
+ Returns:
81
+ Task status information including whether it's running and can be reconnected to
82
+ """
83
+ log_prefix = f"[GET /api/v1/tasks/{task_id}/status] "
84
+ log.debug("%sQuerying task status", log_prefix)
85
+
86
+ repo = TaskRepository()
87
+ task = repo.find_by_id(db, task_id)
88
+
89
+ if not task:
90
+ raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
91
+
92
+ # Determine if task is still running
93
+ is_running = task.status in [None, "running", "pending"] and task.end_time is None
94
+
95
+ # Check if it's a background task
96
+ is_background = task.background_execution_enabled or False
97
+
98
+ # Can reconnect if it's a background task and still running
99
+ can_reconnect = is_background and is_running
100
+
101
+ log.info(
102
+ "%sTask status: running=%s, background=%s, can_reconnect=%s",
103
+ log_prefix,
104
+ is_running,
105
+ is_background,
106
+ can_reconnect,
107
+ )
108
+
109
+ return TaskStatusResponse(
110
+ task=task,
111
+ is_running=is_running,
112
+ is_background=is_background,
113
+ can_reconnect=can_reconnect
114
+ )
115
+
116
+
117
+ @router.get("/tasks/background/active", tags=["Tasks"])
118
+ async def get_active_background_tasks(
119
+ user_id: str = Query(..., description="User ID to filter tasks"),
120
+ db: DBSession = Depends(get_db),
121
+ ):
122
+ """
123
+ Get all active background tasks for a user.
124
+ Used by frontend on session load to detect running background tasks.
125
+
126
+ Args:
127
+ user_id: The user ID to filter by
128
+
129
+ Returns:
130
+ List of active background tasks
131
+ """
132
+ log_prefix = "[GET /api/v1/tasks/background/active] "
133
+ log.debug("%sQuerying active background tasks for user %s", log_prefix, user_id)
134
+
135
+ repo = TaskRepository()
136
+
137
+ # Get all background tasks
138
+ all_background_tasks = repo.find_background_tasks_by_status(db, status=None)
139
+
140
+ # Filter by user and running status
141
+ active_tasks = [
142
+ task for task in all_background_tasks
143
+ if task.user_id == user_id
144
+ and task.status in [None, "running", "pending"]
145
+ and task.end_time is None
146
+ ]
147
+
148
+ log.info("%sFound %d active background tasks for user %s", log_prefix, len(active_tasks), user_id)
149
+
150
+ return {
151
+ "tasks": active_tasks,
152
+ "count": len(active_tasks)
153
+ }
154
+
155
+
156
+ # =============================================================================
157
+ # Project Context Injection Helper
158
+ # =============================================================================
159
+
160
+
161
+ async def _inject_project_context(
162
+ project_id: str,
163
+ message_text: str,
164
+ user_id: str,
165
+ session_id: str,
166
+ project_service: ProjectService,
167
+ component: "WebUIBackendComponent",
168
+ log_prefix: str,
169
+ inject_full_context: bool = True,
170
+ ) -> str:
171
+ """
172
+ Helper function to inject project context and copy artifacts to session.
173
+
174
+ Args:
175
+ inject_full_context: If True, injects full project context (name, description, instructions).
176
+ If False, only copies new artifacts without modifying message text.
177
+ This allows existing sessions to get new project files without
178
+ re-injecting the full context on every message.
179
+
180
+ Returns the modified message text with project context injected (if inject_full_context=True).
181
+ """
182
+ if not project_id or not message_text:
183
+ return message_text
184
+
185
+ from ....gateway.http_sse.dependencies import SessionLocal
186
+ from ..utils.artifact_copy_utils import copy_project_artifacts_to_session
187
+
188
+ if SessionLocal is None:
189
+ log.warning(
190
+ "%sProject context injection skipped: database not configured", log_prefix
191
+ )
192
+ return message_text
193
+
194
+ db = SessionLocal()
195
+ artifact_service = None
196
+ should_clear_pending_flags = False
197
+
198
+ try:
199
+ project = project_service.get_project(db, project_id, user_id)
200
+ if not project:
201
+ return message_text
202
+
203
+ context_parts = []
204
+
205
+ # Only inject full context for new sessions
206
+ if inject_full_context:
207
+ # Start with clear workspace framing
208
+ context_parts.append(
209
+ f'You are working in the project workspace: "{project.name}"'
210
+ )
211
+
212
+ # Add system prompt if exists
213
+ if project.system_prompt and project.system_prompt.strip():
214
+ context_parts.append(f"\n{project.system_prompt.strip()}")
215
+
216
+ # Add project description if exists
217
+ if project.description and project.description.strip():
218
+ context_parts.append(f"\nProject Description: {project.description.strip()}")
219
+
220
+ # Always copy project artifacts to session (for both new and existing sessions)
221
+ # This ensures new project files are available to existing sessions
222
+ artifact_service = component.get_shared_artifact_service()
223
+ if artifact_service:
224
+ try:
225
+ artifacts_copied, new_artifact_names = await copy_project_artifacts_to_session(
226
+ project_id=project_id,
227
+ user_id=user_id,
228
+ session_id=session_id,
229
+ project_service=project_service,
230
+ component=component,
231
+ db=db,
232
+ log_prefix=log_prefix,
233
+ )
234
+
235
+ if inject_full_context and artifacts_copied > 0:
236
+ # need to clear the pending flags even if injection fails
237
+ should_clear_pending_flags = True
238
+
239
+ # Get artifact descriptions for context injection
240
+ if artifacts_copied > 0 or inject_full_context:
241
+ source_user_id = project.user_id
242
+ project_artifacts_session_id = f"project-{project.id}"
243
+
244
+ project_artifacts = await get_artifact_info_list(
245
+ artifact_service=artifact_service,
246
+ app_name=project_service.app_name,
247
+ user_id=source_user_id,
248
+ session_id=project_artifacts_session_id,
249
+ )
250
+
251
+ if project_artifacts:
252
+ # For new sessions - all files
253
+ all_artifact_descriptions = []
254
+ # For existing sessions - only new files
255
+ new_artifact_descriptions = []
256
+
257
+ for artifact_info in project_artifacts:
258
+ # Build description for all artifacts (for new sessions)
259
+ desc_str = f"- {artifact_info.filename}"
260
+ if artifact_info.description:
261
+ desc_str += f": {artifact_info.description}"
262
+ all_artifact_descriptions.append(desc_str)
263
+
264
+ # Track new artifacts for existing sessions
265
+ if artifact_info.filename in new_artifact_names:
266
+ new_artifact_descriptions.append(desc_str)
267
+
268
+ # Add artifact descriptions to context
269
+ files_added_header = (
270
+ "\nNew Files Added to Session:\n"
271
+ "The following files have been added to your session (in addition to any files already present):\n"
272
+ )
273
+
274
+ if inject_full_context and all_artifact_descriptions:
275
+ # New session: show all project files
276
+ artifacts_context = files_added_header + "\n".join(all_artifact_descriptions)
277
+ context_parts.append(artifacts_context)
278
+ elif not inject_full_context and new_artifact_descriptions:
279
+ # Existing session: notify about newly added files
280
+ new_files_context = files_added_header + "\n".join(new_artifact_descriptions)
281
+ context_parts.append(new_files_context)
282
+
283
+ except Exception as e:
284
+ log.warning(
285
+ "%sFailed to copy project artifacts to session: %s", log_prefix, e
286
+ )
287
+ # Do not fail the entire request, just log the warning
288
+
289
+ # Inject all gathered context into the message, ending with user query
290
+ # Only modify message text if we're injecting full context (new sessions)
291
+ modified_message_text = message_text
292
+ if context_parts:
293
+ project_context = "\n".join(context_parts)
294
+ modified_message_text = f"{project_context}\n\nUSER QUERY:\n{message_text}"
295
+ log.debug("%sInjected full project context for project: %s", log_prefix, project_id)
296
+ else:
297
+ log.debug("%sSkipped full context injection for existing session, but ensured new artifacts are copied", log_prefix)
298
+
299
+ return modified_message_text
300
+
301
+ except Exception as e:
302
+ log.warning("%sFailed to inject project context: %s", log_prefix, e)
303
+ # Continue without injection - don't fail the request
304
+ return message_text
305
+ finally:
306
+ # Clear the pending project context flags from all artifacts
307
+ if should_clear_pending_flags and artifact_service:
308
+ from ..utils.artifact_copy_utils import clear_pending_project_context
309
+ try:
310
+ await clear_pending_project_context(
311
+ user_id=user_id,
312
+ session_id=session_id,
313
+ artifact_service=artifact_service,
314
+ app_name=project_service.app_name,
315
+ db=db,
316
+ log_prefix=log_prefix,
317
+ )
318
+ log.debug("%sCleared pending project context flags", log_prefix)
319
+ except Exception as e:
320
+ log.warning("%sFailed to clear pending project context flags: %s", log_prefix, e)
321
+
322
+ db.close()
323
+
324
+
49
325
  async def _submit_task(
50
326
  request: FastAPIRequest,
51
327
  payload: SendMessageRequest | SendStreamingMessageRequest,
52
328
  session_manager: SessionManager,
53
329
  component: "WebUIBackendComponent",
330
+ project_service: ProjectService | None,
54
331
  is_streaming: bool,
55
332
  session_service: SessionService | None = None,
56
333
  ):
57
- """Helper to submit a task, handling both streaming and non-streaming cases."""
334
+ """
335
+ Helper to submit a task, handling both streaming and non-streaming cases.
336
+
337
+ Also handles project context injection.
338
+ """
58
339
  log_prefix = f"[POST /api/v1/message:{'stream' if is_streaming else 'send'}] "
59
340
 
60
341
  agent_name = None
342
+ project_id = None
61
343
  if payload.params and payload.params.message and payload.params.message.metadata:
62
344
  agent_name = payload.params.message.metadata.get("agent_name")
345
+ project_id = payload.params.message.metadata.get("project_id")
63
346
 
64
347
  if not agent_name:
65
348
  raise HTTPException(
@@ -77,7 +360,7 @@ async def _submit_task(
77
360
  status_code=status.HTTP_401_UNAUTHORIZED,
78
361
  detail="User authentication failed or identity not found.",
79
362
  )
80
- log.info(
363
+ log.debug(
81
364
  "%sAuthenticated user identity: %s",
82
365
  log_prefix,
83
366
  user_identity.get("id", "unknown"),
@@ -99,6 +382,30 @@ async def _submit_task(
99
382
  user_id = user_identity.get("id")
100
383
  from ....gateway.http_sse.dependencies import SessionLocal
101
384
 
385
+ # If project_id not in metadata, check if session has a project_id in database
386
+ # This handles cases where sessions are moved to projects after creation
387
+ if not project_id and session_service and frontend_session_id:
388
+ if SessionLocal is not None:
389
+ db = SessionLocal()
390
+ try:
391
+ session_details = session_service.get_session_details(
392
+ db, frontend_session_id, user_id
393
+ )
394
+ if session_details and session_details.project_id:
395
+ project_id = session_details.project_id
396
+ log.info(
397
+ "%sFound project_id %s from session database for session %s",
398
+ log_prefix,
399
+ project_id,
400
+ frontend_session_id,
401
+ )
402
+ except Exception as e:
403
+ log.warning(
404
+ "%sFailed to lookup session project_id: %s", log_prefix, e
405
+ )
406
+ finally:
407
+ db.close()
408
+
102
409
  if frontend_session_id:
103
410
  session_id = frontend_session_id
104
411
  log.info(
@@ -107,7 +414,7 @@ async def _submit_task(
107
414
  else:
108
415
  # Create new session when frontend doesn't provide one
109
416
  session_id = session_manager.create_new_session_id(request)
110
- log.info(
417
+ log.debug(
111
418
  "%sNo valid session ID from frontend, created new session: %s",
112
419
  log_prefix,
113
420
  session_id,
@@ -123,9 +430,10 @@ async def _submit_task(
123
430
  user_id=user_id,
124
431
  agent_id=agent_name,
125
432
  session_id=session_id,
433
+ project_id=project_id,
126
434
  )
127
435
  db.commit()
128
- log.info(
436
+ log.debug(
129
437
  "%sCreated session in database: %s", log_prefix, session_id
130
438
  )
131
439
  except Exception as e:
@@ -140,8 +448,87 @@ async def _submit_task(
140
448
  "%sUsing ClientID: %s, SessionID: %s", log_prefix, client_id, session_id
141
449
  )
142
450
 
143
- # Use the helper to get the unwrapped parts from the incoming message.
144
- a2a_parts = a2a.get_parts_from_message(payload.params.message)
451
+ # Extract message text and apply project context injection
452
+ message_text = ""
453
+ if payload.params and payload.params.message:
454
+ parts = a2a.get_parts_from_message(payload.params.message)
455
+ for part in parts:
456
+ if hasattr(part, "text"):
457
+ message_text = part.text
458
+ break
459
+
460
+ # Project context injection - always inject for project sessions to ensure new files are available
461
+ # Skip if project_service is None (persistence disabled)
462
+ modified_message = payload.params.message
463
+ if project_service and project_id and message_text:
464
+ # Determine if we should inject full context:
465
+ should_inject_full_context = not frontend_session_id
466
+
467
+ # Check if there are artifacts with pending project context
468
+ if frontend_session_id and not should_inject_full_context:
469
+ from ..utils.artifact_copy_utils import has_pending_project_context
470
+ from ....gateway.http_sse.dependencies import SessionLocal
471
+
472
+ artifact_service = component.get_shared_artifact_service()
473
+ if artifact_service and SessionLocal:
474
+ db = SessionLocal()
475
+ try:
476
+ has_pending = await has_pending_project_context(
477
+ user_id=client_id,
478
+ session_id=session_id,
479
+ artifact_service=artifact_service,
480
+ app_name=component.gateway_id,
481
+ db=db,
482
+ )
483
+ if has_pending:
484
+ should_inject_full_context = True
485
+ log.info(
486
+ "%sDetected pending project context for session %s, will inject full context",
487
+ log_prefix,
488
+ session_id,
489
+ )
490
+ finally:
491
+ db.close()
492
+
493
+ modified_message_text = await _inject_project_context(
494
+ project_id=project_id,
495
+ message_text=message_text,
496
+ user_id=user_id,
497
+ session_id=session_id,
498
+ project_service=project_service,
499
+ component=component,
500
+ log_prefix=log_prefix,
501
+ inject_full_context=should_inject_full_context,
502
+ )
503
+
504
+ # Update the message with project context if it was modified
505
+ if modified_message_text != message_text:
506
+ # Create new text part with project context
507
+ new_text_part = a2a.create_text_part(modified_message_text)
508
+
509
+ # Get existing parts and replace the first text part with the modified one
510
+ existing_parts = a2a.get_parts_from_message(payload.params.message)
511
+ new_parts = []
512
+ text_part_replaced = False
513
+
514
+ for part in existing_parts:
515
+ if hasattr(part, "text") and not text_part_replaced:
516
+ new_parts.append(new_text_part)
517
+ text_part_replaced = True
518
+ else:
519
+ new_parts.append(part)
520
+
521
+ # If no text part was found, add the new text part at the beginning
522
+ if not text_part_replaced:
523
+ new_parts.insert(0, new_text_part)
524
+
525
+ # Update the message with the new parts
526
+ modified_message = a2a.update_message_parts(
527
+ payload.params.message, new_parts
528
+ )
529
+
530
+ # Use the helper to get the unwrapped parts from the modified message (with project context if applied).
531
+ a2a_parts = a2a.get_parts_from_message(modified_message)
145
532
 
146
533
  external_req_ctx = {
147
534
  "app_name_for_artifacts": component.gateway_id,
@@ -151,12 +538,24 @@ async def _submit_task(
151
538
  "target_agent_name": agent_name,
152
539
  }
153
540
 
541
+ # Extract additional metadata from the message (e.g., background execution settings)
542
+ # This metadata will be passed through to the A2A message for the task logger
543
+ additional_metadata = {}
544
+ if payload.params and payload.params.message and payload.params.message.metadata:
545
+ msg_metadata = payload.params.message.metadata
546
+ # Pass through background execution settings
547
+ if msg_metadata.get("backgroundExecutionEnabled"):
548
+ additional_metadata["backgroundExecutionEnabled"] = msg_metadata.get("backgroundExecutionEnabled")
549
+ if msg_metadata.get("maxExecutionTimeMs"):
550
+ additional_metadata["maxExecutionTimeMs"] = msg_metadata.get("maxExecutionTimeMs")
551
+
154
552
  task_id = await component.submit_a2a_task(
155
553
  target_agent_name=agent_name,
156
554
  a2a_parts=a2a_parts,
157
555
  external_request_context=external_req_ctx,
158
556
  user_identity=user_identity,
159
557
  is_streaming=is_streaming,
558
+ metadata=additional_metadata if additional_metadata else None,
160
559
  )
161
560
 
162
561
  log.info("%sTask submitted successfully. TaskID: %s", log_prefix, task_id)
@@ -275,8 +674,8 @@ async def search_tasks(
275
674
  )
276
675
 
277
676
 
278
- @router.get("/tasks/{task_id}", tags=["Tasks"])
279
- async def get_task_as_stim_file(
677
+ @router.get("/tasks/{task_id}/events", tags=["Tasks"])
678
+ async def get_task_events(
280
679
  task_id: str,
281
680
  request: FastAPIRequest,
282
681
  db: DBSession = Depends(get_db),
@@ -285,9 +684,11 @@ async def get_task_as_stim_file(
285
684
  repo: ITaskRepository = Depends(get_task_repository),
286
685
  ):
287
686
  """
288
- Retrieves the complete event history for a single task and returns it as a `.stim` file.
687
+ Retrieves the complete event history for a task and all its child tasks as JSON.
688
+ Returns events in the same format as the SSE stream for workflow visualization.
689
+ Recursively loads all descendant tasks to enable full workflow rendering.
289
690
  """
290
- log_prefix = f"[GET /api/v1/tasks/{task_id}] "
691
+ log_prefix = f"[GET /api/v1/tasks/{task_id}/events] "
291
692
  log.info("%sRequest from user %s", log_prefix, user_id)
292
693
 
293
694
  try:
@@ -307,8 +708,255 @@ async def get_task_as_stim_file(
307
708
  detail="You do not have permission to view this task.",
308
709
  )
309
710
 
310
- # Format into .stim structure
311
- stim_data = create_stim_from_task_data(task, events)
711
+ # Transform task events into A2AEventSSEPayload format for the frontend
712
+ # Need to reconstruct the SSE structure from stored data
713
+ formatted_events = []
714
+
715
+ for event in events:
716
+ # event.payload contains the raw A2A JSON-RPC message
717
+ # event.created_time is epoch milliseconds
718
+ # event.direction is simplified (request, response, status, error, etc)
719
+
720
+ # Convert timestamp from epoch milliseconds to ISO 8601
721
+ from datetime import datetime, timezone
722
+ timestamp_dt = datetime.fromtimestamp(event.created_time / 1000, tz=timezone.utc)
723
+ timestamp_iso = timestamp_dt.isoformat()
724
+
725
+ # Extract metadata from payload using similar logic to SSE component
726
+ payload = event.payload
727
+ message_id = payload.get("id")
728
+ source_entity = "unknown"
729
+ target_entity = "unknown"
730
+ method = "N/A"
731
+
732
+ # Parse based on direction
733
+ if event.direction == "request":
734
+ # It's a request - extract target from message metadata
735
+ method = payload.get("method", "N/A")
736
+ if "params" in payload and "message" in payload.get("params", {}):
737
+ message = payload["params"]["message"]
738
+ if isinstance(message, dict) and "metadata" in message:
739
+ target_entity = message["metadata"].get("agent_name", "unknown")
740
+ elif event.direction in ["status", "response", "error"]:
741
+ # It's a response - extract source from result metadata
742
+ if "result" in payload:
743
+ result = payload["result"]
744
+ if isinstance(result, dict):
745
+ # Check for agent_name in metadata
746
+ if "metadata" in result:
747
+ source_entity = result["metadata"].get("agent_name", "unknown")
748
+ # For status updates, check the message inside
749
+ if "message" in result:
750
+ message = result["message"]
751
+ if isinstance(message, dict) and "metadata" in message:
752
+ if source_entity == "unknown":
753
+ source_entity = message["metadata"].get("agent_name", "unknown")
754
+
755
+ # Map stored direction to SSE direction format
756
+ direction_map = {
757
+ "request": "request",
758
+ "response": "task",
759
+ "status": "status-update",
760
+ "error": "error_response",
761
+ }
762
+ sse_direction = direction_map.get(event.direction, event.direction)
763
+
764
+ # Build the A2AEventSSEPayload structure
765
+ formatted_event = {
766
+ "event_type": "a2a_message",
767
+ "timestamp": timestamp_iso,
768
+ "solace_topic": event.topic,
769
+ "direction": sse_direction,
770
+ "source_entity": source_entity,
771
+ "target_entity": target_entity,
772
+ "message_id": message_id,
773
+ "task_id": task_id,
774
+ "payload_summary": {
775
+ "method": method,
776
+ "params_preview": None,
777
+ },
778
+ "full_payload": payload,
779
+ }
780
+ formatted_events.append(formatted_event)
781
+
782
+ # Use database-level query to get all related tasks efficiently
783
+ related_task_ids = repo.find_all_by_parent_chain(db, task_id)
784
+ log.info(
785
+ "%sFound %d related tasks for task_id %s",
786
+ log_prefix,
787
+ len(related_task_ids),
788
+ task_id,
789
+ )
790
+
791
+ # Load and format all related tasks
792
+ all_tasks = {}
793
+ all_tasks[task_id] = {
794
+ "events": formatted_events,
795
+ "initial_request_text": task.initial_request_text or "",
796
+ }
797
+
798
+ # Load remaining related tasks
799
+ for tid in related_task_ids:
800
+ if tid == task_id:
801
+ continue # Already loaded
802
+
803
+ task_result = repo.find_by_id_with_events(db, tid)
804
+ if not task_result:
805
+ continue
806
+
807
+ related_task, related_events = task_result
808
+
809
+ # Check permissions for each related task
810
+ if related_task.user_id != user_id and not can_read_all:
811
+ log.warning(
812
+ "%sSkipping related task %s due to permission check",
813
+ log_prefix,
814
+ tid,
815
+ )
816
+ continue
817
+
818
+ # Format events for this related task
819
+ related_formatted_events = []
820
+
821
+ for event in related_events:
822
+ from datetime import datetime, timezone
823
+
824
+ timestamp_dt = datetime.fromtimestamp(
825
+ event.created_time / 1000, tz=timezone.utc
826
+ )
827
+ timestamp_iso = timestamp_dt.isoformat()
828
+ payload = event.payload
829
+ message_id = payload.get("id")
830
+ source_entity = "unknown"
831
+ target_entity = "unknown"
832
+ method = "N/A"
833
+
834
+ if event.direction == "request":
835
+ method = payload.get("method", "N/A")
836
+ if "params" in payload and "message" in payload.get("params", {}):
837
+ message = payload["params"]["message"]
838
+ if isinstance(message, dict) and "metadata" in message:
839
+ target_entity = message["metadata"].get(
840
+ "agent_name", "unknown"
841
+ )
842
+ elif event.direction in ["status", "response", "error"]:
843
+ if "result" in payload:
844
+ result = payload["result"]
845
+ if isinstance(result, dict):
846
+ if "metadata" in result:
847
+ source_entity = result["metadata"].get(
848
+ "agent_name", "unknown"
849
+ )
850
+ if "message" in result:
851
+ message = result["message"]
852
+ if isinstance(message, dict) and "metadata" in message:
853
+ if source_entity == "unknown":
854
+ source_entity = message["metadata"].get(
855
+ "agent_name", "unknown"
856
+ )
857
+
858
+ direction_map = {
859
+ "request": "request",
860
+ "response": "task",
861
+ "status": "status-update",
862
+ "error": "error_response",
863
+ }
864
+ sse_direction = direction_map.get(event.direction, event.direction)
865
+
866
+ formatted_event = {
867
+ "event_type": "a2a_message",
868
+ "timestamp": timestamp_iso,
869
+ "solace_topic": event.topic,
870
+ "direction": sse_direction,
871
+ "source_entity": source_entity,
872
+ "target_entity": target_entity,
873
+ "message_id": message_id,
874
+ "task_id": tid,
875
+ "payload_summary": {"method": method, "params_preview": None},
876
+ "full_payload": payload,
877
+ }
878
+ related_formatted_events.append(formatted_event)
879
+
880
+ all_tasks[tid] = {
881
+ "events": related_formatted_events,
882
+ "initial_request_text": related_task.initial_request_text or "",
883
+ }
884
+
885
+ # Return all tasks (parent + children) for the frontend to process
886
+ return {"tasks": all_tasks}
887
+
888
+ except HTTPException:
889
+ # Re-raise HTTPExceptions (404, 403, etc.) without modification
890
+ raise
891
+ except Exception as e:
892
+ log.exception("%sError retrieving task events: %s", log_prefix, e)
893
+ raise HTTPException(
894
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
895
+ detail="An error occurred while retrieving the task events.",
896
+ )
897
+
898
+
899
+ @router.get("/tasks/{task_id}", tags=["Tasks"])
900
+ async def get_task_as_stim_file(
901
+ task_id: str,
902
+ request: FastAPIRequest,
903
+ db: DBSession = Depends(get_db),
904
+ user_id: UserId = Depends(get_user_id),
905
+ user_config: dict = Depends(get_user_config),
906
+ repo: ITaskRepository = Depends(get_task_repository),
907
+ ):
908
+ """
909
+ Retrieves the complete event history for a task and all its child tasks, returning it as a `.stim` file.
910
+ """
911
+ log_prefix = f"[GET /api/v1/tasks/{task_id}] "
912
+ log.info("%sRequest from user %s", log_prefix, user_id)
913
+
914
+ try:
915
+ # Find all related task IDs (parent chain + all children)
916
+ related_task_ids = repo.find_all_by_parent_chain(db, task_id)
917
+
918
+ if not related_task_ids:
919
+ raise HTTPException(
920
+ status_code=status.HTTP_404_NOT_FOUND,
921
+ detail=f"Task with ID '{task_id}' not found.",
922
+ )
923
+
924
+ # Load all tasks and their events
925
+ tasks_dict = {}
926
+ events_dict = {}
927
+ can_read_all = user_config.get("scopes", {}).get("tasks:read:all", False)
928
+
929
+ for tid in related_task_ids:
930
+ result = repo.find_by_id_with_events(db, tid)
931
+ if result:
932
+ task, events = result
933
+
934
+ # Check permissions for each task
935
+ if task.user_id != user_id and not can_read_all:
936
+ raise HTTPException(
937
+ status_code=status.HTTP_403_FORBIDDEN,
938
+ detail="You do not have permission to view this task.",
939
+ )
940
+
941
+ tasks_dict[tid] = task
942
+ events_dict[tid] = events
943
+
944
+ if task_id not in tasks_dict:
945
+ raise HTTPException(
946
+ status_code=status.HTTP_404_NOT_FOUND,
947
+ detail=f"Task with ID '{task_id}' not found.",
948
+ )
949
+
950
+ # Determine the root task (the one without a parent)
951
+ root_task_id = task_id
952
+ for tid, task in tasks_dict.items():
953
+ if task.parent_task_id is None:
954
+ root_task_id = tid
955
+ break
956
+
957
+ # Format into .stim structure with all tasks
958
+ from ..utils.stim_utils import create_stim_from_task_hierarchy
959
+ stim_data = create_stim_from_task_hierarchy(tasks_dict, events_dict, root_task_id)
312
960
 
313
961
  yaml_content = yaml.dump(
314
962
  stim_data,
@@ -320,8 +968,8 @@ async def get_task_as_stim_file(
320
968
 
321
969
  return Response(
322
970
  content=yaml_content,
323
- media_type="application/x-yaml",
324
- headers={"Content-Disposition": f'attachment; filename="{task_id}.stim"'},
971
+ media_type="application/yaml",
972
+ headers={"Content-Disposition": f'attachment; filename="{root_task_id}.stim"'},
325
973
  )
326
974
 
327
975
  except HTTPException:
@@ -341,6 +989,7 @@ async def send_task_to_agent(
341
989
  payload: SendMessageRequest,
342
990
  session_manager: SessionManager = Depends(get_session_manager),
343
991
  component: "WebUIBackendComponent" = Depends(get_sac_component),
992
+ project_service: ProjectService | None = Depends(get_project_service_optional),
344
993
  ):
345
994
  """
346
995
  Submits a non-streaming task request to the specified agent.
@@ -351,6 +1000,7 @@ async def send_task_to_agent(
351
1000
  payload=payload,
352
1001
  session_manager=session_manager,
353
1002
  component=component,
1003
+ project_service=project_service,
354
1004
  is_streaming=False,
355
1005
  session_service=None,
356
1006
  )
@@ -362,6 +1012,7 @@ async def subscribe_task_from_agent(
362
1012
  payload: SendStreamingMessageRequest,
363
1013
  session_manager: SessionManager = Depends(get_session_manager),
364
1014
  component: "WebUIBackendComponent" = Depends(get_sac_component),
1015
+ project_service: ProjectService | None = Depends(get_project_service_optional),
365
1016
  session_service: SessionService = Depends(get_session_business_service),
366
1017
  ):
367
1018
  """
@@ -374,6 +1025,7 @@ async def subscribe_task_from_agent(
374
1025
  payload=payload,
375
1026
  session_manager=session_manager,
376
1027
  component=component,
1028
+ project_service=project_service,
377
1029
  is_streaming=True,
378
1030
  session_service=session_service,
379
1031
  )