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
@@ -10,7 +10,7 @@ import uuid
10
10
  import json
11
11
  import re
12
12
  import fnmatch
13
- from typing import Any, Dict, Optional, Union, TYPE_CHECKING
13
+ from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
14
14
  from datetime import datetime, timezone
15
15
  from google.adk.tools import ToolContext
16
16
 
@@ -32,6 +32,7 @@ from ...common.utils.embeds import (
32
32
  EMBED_REGEX,
33
33
  EMBED_CHAIN_DELIMITER,
34
34
  )
35
+ from ...common.utils.embeds.types import ResolutionMode
35
36
  from ...agent.utils.context_helpers import get_original_session_id
36
37
  from ...agent.adk.models.lite_llm import LiteLlm
37
38
  from google.adk.models import LlmRequest
@@ -43,6 +44,7 @@ log = logging.getLogger(__name__)
43
44
  CATEGORY_NAME = "Artifact Management"
44
45
  CATEGORY_DESCRIPTION = "List, read, create, update, and delete artifacts."
45
46
 
47
+
46
48
  async def _internal_create_artifact(
47
49
  filename: str,
48
50
  content: str,
@@ -173,6 +175,7 @@ async def _internal_create_artifact(
173
175
  timestamp=timestamp_for_artifact,
174
176
  schema_max_keys=max_keys_to_use,
175
177
  tool_context=tool_context,
178
+ suppress_visualization_signal=True, # Fenced blocks handle their own visualization signals
176
179
  )
177
180
  log.info(
178
181
  "%s Result from save_artifact_with_metadata: %s", log_identifier, result
@@ -360,6 +363,7 @@ async def load_artifact(
360
363
  version: int,
361
364
  load_metadata_only: bool = False,
362
365
  max_content_length: Optional[int] = None,
366
+ include_line_numbers: bool = False,
363
367
  tool_context: ToolContext = None,
364
368
  ) -> Dict[str, Any]:
365
369
  """
@@ -371,10 +375,13 @@ async def load_artifact(
371
375
 
372
376
  Args:
373
377
  filename: The name of the artifact to load. May contain embeds.
374
- version: The specific version number to load. Must be explicitly provided.
378
+ version: The specific version number to load. Must be explicitly provided. Versions are 0-indexed.
375
379
  load_metadata_only (bool): If True, load only the metadata JSON. Default False.
376
380
  max_content_length (Optional[int]): Maximum character length for text content.
377
381
  If None, uses app configuration. Range: 100-100,000.
382
+ include_line_numbers (bool): If True, prefix each line with its 1-based line number
383
+ followed by a TAB character for LLM viewing. Line numbers
384
+ are not stored in the artifact. Default False.
378
385
  tool_context: The context provided by the ADK framework.
379
386
 
380
387
  Returns:
@@ -413,6 +420,7 @@ async def load_artifact(
413
420
  version=version,
414
421
  load_metadata_only=load_metadata_only,
415
422
  max_content_length=max_content_length,
423
+ include_line_numbers=include_line_numbers,
416
424
  component=host_component,
417
425
  log_identifier_prefix="[BuiltinArtifactTool:load_artifact]",
418
426
  )
@@ -451,121 +459,6 @@ async def load_artifact(
451
459
  }
452
460
 
453
461
 
454
- async def signal_artifact_for_return(
455
- filename: str,
456
- version: int,
457
- tool_context: ToolContext = None,
458
- ) -> Dict[str, Any]:
459
- """
460
- Signals that a specific version of an artifact should be returned to the
461
- original caller as part of the final task result.
462
-
463
- Args:
464
- filename: The name of the artifact to return. May contain embeds.
465
- version: The specific version number to return. Must be explicitly provided.
466
- """
467
- if not tool_context:
468
- return {
469
- "status": "error",
470
- "filename": filename,
471
- "version": version,
472
- "message": "ToolContext is missing.",
473
- }
474
-
475
- log_identifier = (
476
- f"[BuiltinArtifactTool:signal_artifact_for_return:{filename}:{version}]"
477
- )
478
- log.debug("%s Processing request after potential embed resolution.", log_identifier)
479
-
480
- if version is None:
481
- return {
482
- "status": "error",
483
- "filename": filename,
484
- "version": None,
485
- "message": "Version parameter is required. Use list_artifacts() to find available versions.",
486
- }
487
-
488
- try:
489
- inv_context = tool_context._invocation_context
490
- artifact_service = inv_context.artifact_service
491
- host_component = getattr(inv_context.agent, "host_component", None)
492
-
493
- if not artifact_service:
494
- raise ValueError("ArtifactService is not available in the context.")
495
- if not host_component:
496
- raise ValueError("Host component is not available.")
497
-
498
- app_name = inv_context.app_name
499
- user_id = inv_context.user_id
500
- session_id = get_original_session_id(inv_context)
501
-
502
- versions = await artifact_service.list_versions(
503
- app_name=app_name,
504
- user_id=user_id,
505
- session_id=session_id,
506
- filename=filename,
507
- )
508
- if version not in versions:
509
- raise FileNotFoundError(
510
- f"Artifact '{filename}' version {version} not found."
511
- )
512
-
513
- a2a_context = tool_context.state.get("a2a_context", {})
514
- logical_task_id = a2a_context.get("logical_task_id")
515
- if not logical_task_id:
516
- raise ValueError("Could not determine logical_task_id for signaling.")
517
-
518
- with host_component.active_tasks_lock:
519
- task_execution_context = host_component.active_tasks.get(logical_task_id)
520
-
521
- if not task_execution_context:
522
- raise ValueError(
523
- f"TaskExecutionContext not found for task {logical_task_id}."
524
- )
525
-
526
- signal_data = {"filename": filename, "version": version}
527
- task_execution_context.add_artifact_signal(signal_data)
528
-
529
- log.info(
530
- "%s Added artifact signal to TaskExecutionContext for task %s.",
531
- log_identifier,
532
- logical_task_id,
533
- )
534
-
535
- # Also add a placeholder to state_delta. This acts as a trigger
536
- # for the host component to check the cache at the end of the turn.
537
- # The key is unique to avoid collisions, but the content is just a placeholder.
538
- trigger_key = f"temp:a2a_return_artifact:{uuid.uuid4().hex}"
539
- tool_context.actions.state_delta[trigger_key] = {"triggered": True}
540
- log.debug(
541
- "%s Set state_delta trigger key '%s' to ensure signal processing.",
542
- log_identifier,
543
- trigger_key,
544
- )
545
-
546
- return {
547
- "status": "success",
548
- "message": f"Artifact '{filename}' (version {version}) has been signaled for return.",
549
- }
550
-
551
- except FileNotFoundError as fnf_err:
552
- log.warning("%s Artifact not found: %s", log_identifier, fnf_err)
553
- return {
554
- "status": "error",
555
- "filename": filename,
556
- "version": version,
557
- "message": str(fnf_err),
558
- }
559
- except Exception as e:
560
- log.exception("%s Error signaling artifact for return: %s", log_identifier, e)
561
- return {
562
- "status": "error",
563
- "filename": filename,
564
- "version": version,
565
- "message": f"Failed to signal artifact for return: {e}",
566
- }
567
-
568
-
569
462
  async def apply_embed_and_create_artifact(
570
463
  output_filename: str,
571
464
  embed_directive: str,
@@ -659,6 +552,7 @@ async def apply_embed_and_create_artifact(
659
552
  format_spec=format_spec,
660
553
  context=gateway_context,
661
554
  log_identifier=log_identifier,
555
+ resolution_mode=ResolutionMode.TOOL_PARAMETER,
662
556
  config=embed_config,
663
557
  )
664
558
 
@@ -976,7 +870,7 @@ async def extract_content_from_artifact(
976
870
  mime_type=normalized_source_mime_type,
977
871
  content_bytes=source_artifact_content_bytes,
978
872
  )
979
-
873
+
980
874
  if is_text_based:
981
875
  try:
982
876
  artifact_text_content = source_artifact_content_bytes.decode("utf-8")
@@ -1648,17 +1542,20 @@ async def _notify_artifact_save(
1648
1542
  tool_context: ToolContext = None, # Keep tool_context for signature consistency
1649
1543
  ) -> Dict[str, Any]:
1650
1544
  """
1651
- An internal tool used by the system to confirm that a fenced artifact block
1652
- has been successfully saved. It performs no actions and simply returns its
1653
- arguments to get the result into the ADK history.
1545
+ CRITICAL: _notify_artifact_save is automatically invoked by the system as a side-effect when you create artifacts. You should NEVER call this tool yourself. The system will call it for you and provide the results in your next turn. If you manually invoke it, you are making an error."
1654
1546
  """
1655
- return {"filename": filename, "version": version, "status": status}
1547
+ return {
1548
+ "filename": filename,
1549
+ "version": version,
1550
+ "status": status,
1551
+ "message": "Artifact has been created and provided to the requester",
1552
+ }
1656
1553
 
1657
1554
 
1658
1555
  _notify_artifact_save_tool_def = BuiltinTool(
1659
1556
  name="_notify_artifact_save",
1660
1557
  implementation=_notify_artifact_save,
1661
- description="INTERNAL TOOL. This tool is used by the system to confirm that a fenced artifact block has been saved. You MUST NOT call this tool directly.",
1558
+ description="CRITICAL: _notify_artifact_save is automatically invoked by the system as a side-effect when you create artifacts. You should NEVER call this tool yourself. The system will call it for you and provide the results in your next turn. If you manually invoke it, you are making an error.",
1662
1559
  category="internal",
1663
1560
  required_scopes=[], # No scopes needed for an internal notification tool
1664
1561
  parameters=adk_types.Schema(
@@ -1730,7 +1627,7 @@ list_artifacts_tool_def = BuiltinTool(
1730
1627
  load_artifact_tool_def = BuiltinTool(
1731
1628
  name="load_artifact",
1732
1629
  implementation=load_artifact,
1733
- description="Loads the content or metadata of a specific artifact version. If load_metadata_only is True, loads the full metadata dictionary. Otherwise, loads text content (potentially truncated) or a summary for binary types.",
1630
+ description="Loads the content or metadata of a specific artifact version. If load_metadata_only is True, loads the full metadata dictionary. Otherwise, loads text content (potentially truncated) or a summary for binary types. Line numbers can be optionally included for precise line range identification.",
1734
1631
  category="artifact_management",
1735
1632
  category_name=CATEGORY_NAME,
1736
1633
  category_description=CATEGORY_DESCRIPTION,
@@ -1756,30 +1653,10 @@ load_artifact_tool_def = BuiltinTool(
1756
1653
  description="Optional. Maximum character length for text content. If None, uses app configuration. Range: 100-100,000.",
1757
1654
  nullable=True,
1758
1655
  ),
1759
- },
1760
- required=["filename", "version"],
1761
- ),
1762
- examples=[],
1763
- )
1764
-
1765
- signal_artifact_for_return_tool_def = BuiltinTool(
1766
- name="signal_artifact_for_return",
1767
- implementation=signal_artifact_for_return,
1768
- description="Signals the host component to return a specific artifact version to the original caller of the task. This tool does not load the artifact content itself; it just flags it for return.",
1769
- category="artifact_management",
1770
- category_name=CATEGORY_NAME,
1771
- category_description=CATEGORY_DESCRIPTION,
1772
- required_scopes=["tool:artifact:signal_return"],
1773
- parameters=adk_types.Schema(
1774
- type=adk_types.Type.OBJECT,
1775
- properties={
1776
- "filename": adk_types.Schema(
1777
- type=adk_types.Type.STRING,
1778
- description="The name of the artifact to return. May contain embeds.",
1779
- ),
1780
- "version": adk_types.Schema(
1781
- type=adk_types.Type.INTEGER,
1782
- description="The specific version number to return. Use list_artifacts() first to find available versions.",
1656
+ "include_line_numbers": adk_types.Schema(
1657
+ type=adk_types.Type.BOOLEAN,
1658
+ description="If True, prefix each line with its 1-based line number followed by a TAB character. Line numbers are for LLM viewing only and are not stored in the artifact. Default False.",
1659
+ nullable=True,
1783
1660
  ),
1784
1661
  },
1785
1662
  required=["filename", "version"],
@@ -1857,7 +1734,6 @@ tool_registry.register(_notify_artifact_save_tool_def)
1857
1734
  tool_registry.register(append_to_artifact_tool_def)
1858
1735
  tool_registry.register(list_artifacts_tool_def)
1859
1736
  tool_registry.register(load_artifact_tool_def)
1860
- tool_registry.register(signal_artifact_for_return_tool_def)
1861
1737
  tool_registry.register(apply_embed_and_create_artifact_tool_def)
1862
1738
  tool_registry.register(extract_content_from_artifact_tool_def)
1863
1739
 
@@ -1865,18 +1741,20 @@ tool_registry.register(extract_content_from_artifact_tool_def)
1865
1741
  async def delete_artifact(
1866
1742
  filename: str,
1867
1743
  version: Optional[int] = None,
1744
+ confirm_delete: bool = False,
1868
1745
  tool_context: ToolContext = None,
1869
1746
  ) -> Dict[str, Any]:
1870
1747
  """
1871
- Deletes a specific version of an artifact, or all versions if no version is specified.
1748
+ Deletes all versions of an artifact. Version-specific deletion is not currently supported.
1872
1749
 
1873
1750
  Args:
1874
1751
  filename: The name of the artifact to delete.
1875
- version: The specific version number to delete. If not provided, all versions will be deleted.
1752
+ version: Reserved for future use. Currently not supported - returns error if specified.
1753
+ confirm_delete: Must be set to True to confirm deletion. If False, returns confirmation prompt.
1876
1754
  tool_context: The context provided by the ADK framework.
1877
1755
 
1878
1756
  Returns:
1879
- A dictionary indicating the result of the deletion.
1757
+ A dictionary indicating the result of the deletion or requesting confirmation.
1880
1758
  """
1881
1759
  if not tool_context:
1882
1760
  return {
@@ -1885,9 +1763,7 @@ async def delete_artifact(
1885
1763
  "message": "ToolContext is missing, cannot delete artifact.",
1886
1764
  }
1887
1765
 
1888
- log_identifier = (
1889
- f"[BuiltinArtifactTool:delete_artifact:{filename}:{version or 'all'}]"
1890
- )
1766
+ log_identifier = f"[BuiltinArtifactTool:delete_artifact:{filename}]"
1891
1767
  log.debug("%s Processing request.", log_identifier)
1892
1768
 
1893
1769
  try:
@@ -1905,14 +1781,32 @@ async def delete_artifact(
1905
1781
  "ArtifactService does not support deleting artifacts."
1906
1782
  )
1907
1783
 
1784
+ # Error if version-specific deletion requested (not currently supported)
1908
1785
  if version is not None:
1909
- log.warning(
1910
- "%s Deleting a specific version (%s) is not supported by the current artifact service interface. "
1911
- "All versions of the artifact will be deleted.",
1912
- log_identifier,
1913
- version,
1914
- )
1786
+ return {
1787
+ "status": "error",
1788
+ "filename": filename,
1789
+ "version_requested": version,
1790
+ "message": f"Deleting a specific version ({version}) is not currently supported. Only deletion of ALL versions is supported. To delete all versions, omit 'version' and set confirm_delete=True.",
1791
+ }
1915
1792
 
1793
+ # Get version list for confirmation message
1794
+ versions = await artifact_service.list_versions(
1795
+ app_name=app_name, user_id=user_id, session_id=session_id, filename=filename
1796
+ )
1797
+
1798
+ # Require confirmation before deleting
1799
+ if not confirm_delete:
1800
+ count = len(versions) if versions else "unknown number of"
1801
+ return {
1802
+ "status": "confirmation_required",
1803
+ "filename": filename,
1804
+ "version_count": len(versions) if versions else None,
1805
+ "versions": versions,
1806
+ "message": f"WARNING: This operation is irreversible and will permanently delete artifact '{filename}' and ALL {count} version(s). To proceed, call this tool again with confirm_delete=True.",
1807
+ }
1808
+
1809
+ # Proceed with deletion
1916
1810
  await artifact_service.delete_artifact(
1917
1811
  app_name=app_name,
1918
1812
  user_id=user_id,
@@ -1920,17 +1814,12 @@ async def delete_artifact(
1920
1814
  filename=filename,
1921
1815
  )
1922
1816
 
1923
- log.info(
1924
- "%s Successfully deleted artifact '%s' version '%s'.",
1925
- log_identifier,
1926
- filename,
1927
- version or "all",
1928
- )
1817
+ log.info("%s Successfully deleted artifact '%s'.", log_identifier, filename)
1929
1818
  return {
1930
1819
  "status": "success",
1931
1820
  "filename": filename,
1932
- "version": version or "all",
1933
- "message": f"Artifact '{filename}' version '{version or 'all'}' deleted successfully.",
1821
+ "versions_deleted": len(versions) if versions else None,
1822
+ "message": f"Artifact '{filename}' deleted successfully.",
1934
1823
  }
1935
1824
 
1936
1825
  except FileNotFoundError as e:
@@ -1954,7 +1843,7 @@ async def delete_artifact(
1954
1843
  delete_artifact_tool_def = BuiltinTool(
1955
1844
  name="delete_artifact",
1956
1845
  implementation=delete_artifact,
1957
- description="Deletes a specific version of an artifact, or all versions if no version is specified.",
1846
+ description="Deletes all versions of an artifact. IMPORTANT: Requires explicit confirmation via confirm_delete=True parameter. The first call without confirmation will return details about what will be deleted.",
1958
1847
  category="artifact_management",
1959
1848
  category_name=CATEGORY_NAME,
1960
1849
  category_description=CATEGORY_DESCRIPTION,
@@ -1968,7 +1857,12 @@ delete_artifact_tool_def = BuiltinTool(
1968
1857
  ),
1969
1858
  "version": adk_types.Schema(
1970
1859
  type=adk_types.Type.INTEGER,
1971
- description="The specific version number to delete. If not provided, all versions will be deleted.",
1860
+ description="Reserved for future use. Version-specific deletion is not currently supported - will return error if specified.",
1861
+ nullable=True,
1862
+ ),
1863
+ "confirm_delete": adk_types.Schema(
1864
+ type=adk_types.Type.BOOLEAN,
1865
+ description="Must be set to True to actually perform the deletion. If False or omitted, returns a confirmation prompt with details about what will be deleted (including version count).",
1972
1866
  nullable=True,
1973
1867
  ),
1974
1868
  },
@@ -1978,3 +1872,648 @@ delete_artifact_tool_def = BuiltinTool(
1978
1872
  )
1979
1873
 
1980
1874
  tool_registry.register(delete_artifact_tool_def)
1875
+
1876
+
1877
+ def _perform_single_replacement(
1878
+ content: str,
1879
+ search_expr: str,
1880
+ replace_expr: str,
1881
+ is_regex: bool,
1882
+ regex_flags: str,
1883
+ log_identifier: str,
1884
+ strict_match_validation: bool = False,
1885
+ ) -> Tuple[str, int, Optional[str]]:
1886
+ """
1887
+ Performs a single search-and-replace operation.
1888
+
1889
+ Args:
1890
+ content: The text content to search/replace in
1891
+ search_expr: The search pattern (literal or regex)
1892
+ replace_expr: The replacement text
1893
+ is_regex: If True, search_expr is treated as regex
1894
+ regex_flags: Flags for regex behavior ('g', 'i', 'm', 's')
1895
+ log_identifier: Logging prefix
1896
+ strict_match_validation: If True, error on multiple matches without 'g' flag (for batch mode)
1897
+
1898
+ Returns:
1899
+ tuple: (new_content, match_count, error_message)
1900
+ error_message is None on success
1901
+ """
1902
+ match_count = 0
1903
+ new_content = content
1904
+
1905
+ if is_regex:
1906
+ # Parse regex flags
1907
+ flags_value = 0
1908
+ global_replace = False
1909
+
1910
+ if regex_flags:
1911
+ for flag_char in regex_flags.lower():
1912
+ if flag_char == "g":
1913
+ global_replace = True
1914
+ elif flag_char == "i":
1915
+ flags_value |= re.IGNORECASE
1916
+ elif flag_char == "m":
1917
+ flags_value |= re.MULTILINE
1918
+ elif flag_char == "s":
1919
+ flags_value |= re.DOTALL
1920
+ else:
1921
+ log.warning(
1922
+ "%s Ignoring unrecognized regexp flag: '%s'",
1923
+ log_identifier,
1924
+ flag_char,
1925
+ )
1926
+
1927
+ # Convert JavaScript-style capture groups ($1, $2) to Python style (\1, \2)
1928
+ # Also handle escaped dollar signs ($$) -> literal $
1929
+ python_replace_expr = replace_expr
1930
+ # First, protect escaped dollars: $$ -> a placeholder
1931
+ python_replace_expr = python_replace_expr.replace("$$", "\x00DOLLAR\x00")
1932
+ # Convert capture groups: $1 -> \1
1933
+ python_replace_expr = re.sub(r"\$(\d+)", r"\\\1", python_replace_expr)
1934
+ # Restore escaped dollars: placeholder -> $
1935
+ python_replace_expr = python_replace_expr.replace("\x00DOLLAR\x00", "$")
1936
+
1937
+ try:
1938
+ # Compile the regex pattern
1939
+ pattern = re.compile(search_expr, flags_value)
1940
+
1941
+ # Count matches first
1942
+ match_count = len(pattern.findall(content))
1943
+
1944
+ if match_count == 0:
1945
+ return content, 0, f"No matches found"
1946
+
1947
+ # Check for multiple matches without global flag (only in strict mode for batch operations)
1948
+ if strict_match_validation and match_count > 1 and not global_replace:
1949
+ return (
1950
+ content,
1951
+ match_count,
1952
+ f"Multiple matches found ({match_count}) but global flag 'g' not set",
1953
+ )
1954
+
1955
+ # Perform replacement
1956
+ count_limit = 0 if global_replace else 1
1957
+ new_content = pattern.sub(python_replace_expr, content, count=count_limit)
1958
+
1959
+ return new_content, match_count, None
1960
+
1961
+ except re.error as regex_err:
1962
+ return content, 0, f"Invalid regular expression: {regex_err}"
1963
+
1964
+ else:
1965
+ # Literal string replacement
1966
+ match_count = content.count(search_expr)
1967
+
1968
+ if match_count == 0:
1969
+ return content, 0, f"No matches found"
1970
+
1971
+ # Replace all occurrences for literal mode
1972
+ new_content = content.replace(search_expr, replace_expr)
1973
+ return new_content, match_count, None
1974
+
1975
+
1976
+ async def artifact_search_and_replace_regex(
1977
+ filename: str,
1978
+ search_expression: Optional[str] = None,
1979
+ replace_expression: Optional[str] = None,
1980
+ is_regexp: bool = False,
1981
+ version: Optional[str] = "latest",
1982
+ regexp_flags: Optional[str] = "",
1983
+ new_filename: Optional[str] = None,
1984
+ new_description: Optional[str] = None,
1985
+ replacements: Optional[List[Dict[str, Any]]] = None,
1986
+ tool_context: ToolContext = None,
1987
+ ) -> Dict[str, Any]:
1988
+ """
1989
+ Performs search and replace on an artifact's text content using either
1990
+ literal string matching or regular expressions. Note that this is run once across the entire artifact.
1991
+ If multiple replacements are needed, then set the 'g' flag in regexp_flags.
1992
+
1993
+ Handling Multi-line Search and Replace:
1994
+
1995
+ When searching for or replacing text that spans multiple lines:
1996
+
1997
+ - In literal mode (is_regexp=false): Include actual newline characters directly in your search_expression
1998
+ and replace_expression parameters. Do NOT use escape sequences like \n - the tool will search for those
1999
+ literal characters. Multi-line parameter values are fully supported in the XML parameter format.
2000
+
2001
+ - In regex mode (is_regexp=true): Use the regex pattern \n to match newline characters in your pattern.
2002
+
2003
+ For multiple independent replacements:
2004
+
2005
+ Use the replacements array parameter to perform all replacements atomically in a single tool call, which is more efficient than multiple sequential calls.
2006
+
2007
+ Args:
2008
+ filename: The name of the artifact to search/replace in.
2009
+ search_expression: The pattern to search for (regex if is_regexp=true, literal otherwise).
2010
+ replace_expression: The replacement text. For regex mode, supports capture groups ($1, $2, etc.). Use $$ to insert a literal dollar sign
2011
+ is_regexp: If True, treat search_expression as a regular expression. If False, treat as literal string.
2012
+ version: The version of the artifact to operate on. Can be an integer version number as a string or 'latest'. Defaults to 'latest'.
2013
+ regexp_flags: Flags for regex behavior (only used when is_regexp=true).
2014
+ String of letters: 'g' (global/replace-all), 'i' (case-insensitive), 'm' (multiline), 's' (dotall).
2015
+ Defaults to empty string (no flags).
2016
+ new_filename: Optional. If provided, saves the result as a new artifact with this name.
2017
+ new_description: Optional. Description for the new/updated artifact.
2018
+
2019
+ Returns:
2020
+ A dictionary containing the result status, filename, version, match count, and any error messages.
2021
+ """
2022
+ if not tool_context:
2023
+ return {
2024
+ "status": "error",
2025
+ "filename": filename,
2026
+ "message": "ToolContext is missing, cannot perform search and replace.",
2027
+ }
2028
+
2029
+ log_identifier = (
2030
+ f"[BuiltinArtifactTool:artifact_search_and_replace_regex:{filename}:{version}]"
2031
+ )
2032
+ log.debug("%s Processing request.", log_identifier)
2033
+
2034
+ # Validate parameter combinations
2035
+ if replacements is not None and (
2036
+ search_expression is not None or replace_expression is not None
2037
+ ):
2038
+ return {
2039
+ "status": "error",
2040
+ "filename": filename,
2041
+ "message": "Cannot provide both 'replacements' array and individual 'search_expression'/'replace_expression'. Use one or the other.",
2042
+ }
2043
+
2044
+ if replacements is None and (
2045
+ search_expression is None or replace_expression is None
2046
+ ):
2047
+ return {
2048
+ "status": "error",
2049
+ "filename": filename,
2050
+ "message": "Must provide either 'replacements' array or both 'search_expression' and 'replace_expression'.",
2051
+ }
2052
+
2053
+ if replacements is not None:
2054
+ if not isinstance(replacements, list) or len(replacements) == 0:
2055
+ return {
2056
+ "status": "error",
2057
+ "filename": filename,
2058
+ "message": "replacements must be a non-empty array.",
2059
+ }
2060
+
2061
+ # Validate each replacement entry
2062
+ for idx, repl in enumerate(replacements):
2063
+ if not isinstance(repl, dict):
2064
+ return {
2065
+ "status": "error",
2066
+ "filename": filename,
2067
+ "message": f"Replacement at index {idx} must be a dictionary.",
2068
+ }
2069
+ if "search" not in repl or "replace" not in repl or "is_regexp" not in repl:
2070
+ return {
2071
+ "status": "error",
2072
+ "filename": filename,
2073
+ "message": f"Replacement at index {idx} missing required fields: 'search', 'replace', 'is_regexp'.",
2074
+ }
2075
+
2076
+ # Validate inputs for single replacement mode
2077
+ if replacements is None and not search_expression:
2078
+ return {
2079
+ "status": "error",
2080
+ "filename": filename,
2081
+ "message": "search_expression cannot be empty.",
2082
+ }
2083
+
2084
+ # Determine output filename
2085
+ output_filename = new_filename if new_filename else filename
2086
+
2087
+ if new_filename and not is_filename_safe(new_filename):
2088
+ return {
2089
+ "status": "error",
2090
+ "filename": filename,
2091
+ "message": f"Invalid new_filename: '{new_filename}'. Filename must not contain path separators or traversal sequences.",
2092
+ }
2093
+
2094
+ try:
2095
+ inv_context = tool_context._invocation_context
2096
+ artifact_service = inv_context.artifact_service
2097
+ if not artifact_service:
2098
+ raise ValueError("ArtifactService is not available in the context.")
2099
+
2100
+ app_name = inv_context.app_name
2101
+ user_id = inv_context.user_id
2102
+ session_id = get_original_session_id(inv_context)
2103
+ host_component = getattr(inv_context.agent, "host_component", None)
2104
+
2105
+ # Load the source artifact
2106
+ log.debug(
2107
+ "%s Loading artifact '%s' version '%s'.", log_identifier, filename, version
2108
+ )
2109
+ load_result = await load_artifact_content_or_metadata(
2110
+ artifact_service=artifact_service,
2111
+ app_name=app_name,
2112
+ user_id=user_id,
2113
+ session_id=session_id,
2114
+ filename=filename,
2115
+ version=version,
2116
+ return_raw_bytes=True,
2117
+ component=host_component,
2118
+ log_identifier_prefix=log_identifier,
2119
+ )
2120
+
2121
+ if load_result.get("status") != "success":
2122
+ return {
2123
+ "status": "error",
2124
+ "filename": filename,
2125
+ "version": version,
2126
+ "message": f"Failed to load artifact: {load_result.get('message', 'Unknown error')}",
2127
+ }
2128
+
2129
+ source_bytes = load_result.get("raw_bytes")
2130
+ source_mime_type = load_result.get("mime_type", "application/octet-stream")
2131
+ actual_version = load_result.get("version", version)
2132
+
2133
+ # Verify it's a text-based artifact
2134
+ if not is_text_based_file(source_mime_type, source_bytes):
2135
+ return {
2136
+ "status": "error",
2137
+ "filename": filename,
2138
+ "version": actual_version,
2139
+ "message": f"Cannot perform search and replace on binary artifact of type '{source_mime_type}'. This tool only works with text-based content.",
2140
+ }
2141
+
2142
+ # Decode the content
2143
+ try:
2144
+ original_content = source_bytes.decode("utf-8")
2145
+ except UnicodeDecodeError as decode_err:
2146
+ log.error(
2147
+ "%s Failed to decode artifact content as UTF-8: %s",
2148
+ log_identifier,
2149
+ decode_err,
2150
+ )
2151
+ return {
2152
+ "status": "error",
2153
+ "filename": filename,
2154
+ "version": actual_version,
2155
+ "message": f"Failed to decode artifact content as UTF-8: {decode_err}",
2156
+ }
2157
+
2158
+ # Perform the search and replace
2159
+ if replacements:
2160
+ # Batch mode
2161
+ log.info(
2162
+ "%s Processing batch of %d replacements.",
2163
+ log_identifier,
2164
+ len(replacements),
2165
+ )
2166
+
2167
+ current_content = original_content
2168
+ replacement_results = []
2169
+ total_matches = 0
2170
+
2171
+ for idx, repl in enumerate(replacements):
2172
+ search_expr = repl["search"]
2173
+ replace_expr = repl["replace"]
2174
+ is_regex = repl["is_regexp"]
2175
+ regex_flags = repl.get("regexp_flags", "")
2176
+
2177
+ # Perform replacement on current state (with strict validation for batch mode)
2178
+ new_content, match_count, error_msg = _perform_single_replacement(
2179
+ current_content,
2180
+ search_expr,
2181
+ replace_expr,
2182
+ is_regex,
2183
+ regex_flags,
2184
+ log_identifier,
2185
+ strict_match_validation=True,
2186
+ )
2187
+
2188
+ if error_msg:
2189
+ # Rollback - return error with details
2190
+ log.warning(
2191
+ "%s Batch replacement failed at index %d: %s",
2192
+ log_identifier,
2193
+ idx,
2194
+ error_msg,
2195
+ )
2196
+
2197
+ # Mark all as skipped
2198
+ all_results = replacement_results + [
2199
+ {
2200
+ "search": repl["search"],
2201
+ "match_count": match_count,
2202
+ "status": "error",
2203
+ "error": error_msg,
2204
+ }
2205
+ ]
2206
+ # Add remaining as skipped
2207
+ for i in range(idx + 1, len(replacements)):
2208
+ all_results.append(
2209
+ {
2210
+ "search": replacements[i]["search"],
2211
+ "match_count": 0,
2212
+ "status": "skipped",
2213
+ }
2214
+ )
2215
+
2216
+ return {
2217
+ "status": "error",
2218
+ "filename": filename,
2219
+ "version": actual_version,
2220
+ "message": f"Batch replacement failed: No changes applied due to error in replacement {idx + 1}",
2221
+ "replacement_results": all_results,
2222
+ "failed_replacement": {
2223
+ "index": idx,
2224
+ "search": search_expr,
2225
+ "error": error_msg,
2226
+ },
2227
+ }
2228
+
2229
+ # Success - update state and continue
2230
+ current_content = new_content
2231
+ total_matches += match_count
2232
+ replacement_results.append(
2233
+ {
2234
+ "search": search_expr,
2235
+ "match_count": match_count,
2236
+ "status": "success",
2237
+ }
2238
+ )
2239
+
2240
+ log.debug(
2241
+ "%s Replacement %d/%d succeeded: %d matches",
2242
+ log_identifier,
2243
+ idx + 1,
2244
+ len(replacements),
2245
+ match_count,
2246
+ )
2247
+
2248
+ # All replacements succeeded
2249
+ final_content = current_content
2250
+ total_replacements = len(replacements)
2251
+
2252
+ log.info(
2253
+ "%s Batch replacement succeeded: %d operations, %d total matches",
2254
+ log_identifier,
2255
+ total_replacements,
2256
+ total_matches,
2257
+ )
2258
+
2259
+ else:
2260
+ # Single replacement mode (backward compatible)
2261
+ final_content, match_count, error_msg = _perform_single_replacement(
2262
+ original_content,
2263
+ search_expression,
2264
+ replace_expression,
2265
+ is_regexp,
2266
+ regexp_flags,
2267
+ log_identifier,
2268
+ )
2269
+
2270
+ if error_msg:
2271
+ # Check if it's a "no matches" error specifically
2272
+ if match_count == 0 and "No matches found" in error_msg:
2273
+ return {
2274
+ "status": "no_matches",
2275
+ "filename": filename,
2276
+ "version": actual_version,
2277
+ "match_count": 0,
2278
+ "message": f"No matches found for pattern '{search_expression}'. Artifact not modified.",
2279
+ }
2280
+ else:
2281
+ return {
2282
+ "status": "error",
2283
+ "filename": filename,
2284
+ "version": actual_version,
2285
+ "message": error_msg,
2286
+ }
2287
+
2288
+ total_replacements = 1
2289
+ total_matches = match_count
2290
+ replacement_results = None
2291
+
2292
+ # Prepare metadata for the new/updated artifact
2293
+ if replacements:
2294
+ new_metadata = {
2295
+ "source": f"artifact_search_and_replace_regex (batch) from '{filename}' v{actual_version}",
2296
+ "total_replacements": total_replacements,
2297
+ "total_matches": total_matches,
2298
+ }
2299
+ else:
2300
+ new_metadata = {
2301
+ "source": f"artifact_search_and_replace_regex from '{filename}' v{actual_version}",
2302
+ "search_expression": search_expression,
2303
+ "replace_expression": replace_expression,
2304
+ "is_regexp": is_regexp,
2305
+ "match_count": match_count,
2306
+ }
2307
+
2308
+ if regexp_flags and is_regexp:
2309
+ new_metadata["regexp_flags"] = regexp_flags
2310
+
2311
+ if new_description:
2312
+ new_metadata["description"] = new_description
2313
+ elif not new_filename:
2314
+ # If updating the same artifact, preserve original description if available
2315
+ try:
2316
+ metadata_load_result = await load_artifact_content_or_metadata(
2317
+ artifact_service=artifact_service,
2318
+ app_name=app_name,
2319
+ user_id=user_id,
2320
+ session_id=session_id,
2321
+ filename=filename,
2322
+ version=actual_version,
2323
+ load_metadata_only=True,
2324
+ component=host_component,
2325
+ log_identifier_prefix=log_identifier,
2326
+ )
2327
+ if metadata_load_result.get("status") == "success":
2328
+ original_metadata = metadata_load_result.get("metadata", {})
2329
+ if "description" in original_metadata:
2330
+ new_metadata["description"] = original_metadata["description"]
2331
+ except Exception as meta_err:
2332
+ log.warning(
2333
+ "%s Could not load original metadata to preserve description: %s",
2334
+ log_identifier,
2335
+ meta_err,
2336
+ )
2337
+
2338
+ # Save the result
2339
+ new_content_bytes = final_content.encode("utf-8")
2340
+ schema_max_keys = (
2341
+ host_component.get_config("schema_max_keys", DEFAULT_SCHEMA_MAX_KEYS)
2342
+ if host_component
2343
+ else DEFAULT_SCHEMA_MAX_KEYS
2344
+ )
2345
+
2346
+ save_result = await save_artifact_with_metadata(
2347
+ artifact_service=artifact_service,
2348
+ app_name=app_name,
2349
+ user_id=user_id,
2350
+ session_id=session_id,
2351
+ filename=output_filename,
2352
+ content_bytes=new_content_bytes,
2353
+ mime_type=source_mime_type,
2354
+ metadata_dict=new_metadata,
2355
+ timestamp=datetime.now(timezone.utc),
2356
+ schema_max_keys=schema_max_keys,
2357
+ tool_context=tool_context,
2358
+ )
2359
+
2360
+ if save_result.get("status") not in ["success", "partial_success"]:
2361
+ log.error(
2362
+ "%s Failed to save modified artifact: %s",
2363
+ log_identifier,
2364
+ save_result.get("message"),
2365
+ )
2366
+ return {
2367
+ "status": "error",
2368
+ "filename": filename,
2369
+ "version": actual_version,
2370
+ "message": f"Search and replace succeeded, but failed to save result: {save_result.get('message')}",
2371
+ }
2372
+
2373
+ result_version = save_result.get("data_version")
2374
+ log.info(
2375
+ "%s Successfully saved modified artifact '%s' as version %s.",
2376
+ log_identifier,
2377
+ output_filename,
2378
+ result_version,
2379
+ )
2380
+
2381
+ # Return appropriate response based on mode
2382
+ if replacements:
2383
+ return {
2384
+ "status": "success",
2385
+ "source_filename": filename,
2386
+ "source_version": actual_version,
2387
+ "output_filename": output_filename,
2388
+ "output_version": result_version,
2389
+ "total_replacements": total_replacements,
2390
+ "replacement_results": replacement_results,
2391
+ "total_matches": total_matches,
2392
+ "message": f"Batch replacement completed: {total_replacements} operations, {total_matches} total matches",
2393
+ }
2394
+ else:
2395
+ # Compute replacements_made for backward compatibility
2396
+ # For literal replacements, all matches are replaced
2397
+ # For regex without 'g' flag, only first match is replaced
2398
+ global_replace = "g" in (regexp_flags or "")
2399
+ replacements_made = (
2400
+ match_count if not is_regexp or global_replace else min(match_count, 1)
2401
+ )
2402
+
2403
+ return {
2404
+ "status": "success",
2405
+ "source_filename": filename,
2406
+ "source_version": actual_version,
2407
+ "output_filename": output_filename,
2408
+ "output_version": result_version,
2409
+ "match_count": match_count,
2410
+ "replacements_made": replacements_made,
2411
+ "message": f"Successfully performed {'regex' if is_regexp else 'literal'} search and replace. "
2412
+ f"Found {match_count} match(es), saved result as '{output_filename}' v{result_version}.",
2413
+ }
2414
+
2415
+ except FileNotFoundError as fnf_err:
2416
+ log.warning("%s Artifact not found: %s", log_identifier, fnf_err)
2417
+ return {
2418
+ "status": "error",
2419
+ "filename": filename,
2420
+ "version": version,
2421
+ "message": f"Artifact not found: {fnf_err}",
2422
+ }
2423
+ except Exception as e:
2424
+ log.exception(
2425
+ "%s Unexpected error during search and replace: %s", log_identifier, e
2426
+ )
2427
+ return {
2428
+ "status": "error",
2429
+ "filename": filename,
2430
+ "version": version,
2431
+ "message": f"Unexpected error: {e}",
2432
+ }
2433
+
2434
+
2435
+ artifact_search_and_replace_regex_tool_def = BuiltinTool(
2436
+ name="artifact_search_and_replace_regex",
2437
+ implementation=artifact_search_and_replace_regex,
2438
+ description="Performs search and replace on an artifact's text content using either literal string matching or regular expressions. Supports both single replacements and atomic batch replacements for efficiency.",
2439
+ category="artifact_management",
2440
+ category_name=CATEGORY_NAME,
2441
+ category_description=CATEGORY_DESCRIPTION,
2442
+ required_scopes=["tool:artifact:load", "tool:artifact:create"],
2443
+ parameters=adk_types.Schema(
2444
+ type=adk_types.Type.OBJECT,
2445
+ properties={
2446
+ "filename": adk_types.Schema(
2447
+ type=adk_types.Type.STRING,
2448
+ description="The name of the artifact to search/replace in.",
2449
+ ),
2450
+ "search_expression": adk_types.Schema(
2451
+ type=adk_types.Type.STRING,
2452
+ description="The pattern to search for (single replacement mode). If is_regexp is true, this is treated as a regular expression. Otherwise, it's a literal string. Do not use if 'replacements' is provided.",
2453
+ nullable=True,
2454
+ ),
2455
+ "replace_expression": adk_types.Schema(
2456
+ type=adk_types.Type.STRING,
2457
+ description="The replacement text (single replacement mode). For regex mode, supports capture group references using $1, $2, etc. Use $$ to insert a literal dollar sign. Do not use if 'replacements' is provided.",
2458
+ nullable=True,
2459
+ ),
2460
+ "is_regexp": adk_types.Schema(
2461
+ type=adk_types.Type.BOOLEAN,
2462
+ description="If true, treat search_expression as a regular expression. If false, treat as literal string. Only used in single replacement mode.",
2463
+ nullable=True,
2464
+ ),
2465
+ "version": adk_types.Schema(
2466
+ type=adk_types.Type.STRING,
2467
+ description="The version of the artifact to operate on. Can be an integer version number or 'latest'. Defaults to 'latest'.",
2468
+ nullable=True,
2469
+ ),
2470
+ "regexp_flags": adk_types.Schema(
2471
+ type=adk_types.Type.STRING,
2472
+ description="Flags for regex behavior (only used when is_regexp=true in single mode). String of letters: 'g' (global/replace all), 'i' (case-insensitive), 'm' (multiline), 's' (dotall). Example: 'gim'. Defaults to empty string.",
2473
+ nullable=True,
2474
+ ),
2475
+ "new_filename": adk_types.Schema(
2476
+ type=adk_types.Type.STRING,
2477
+ description="Optional. If provided, saves the result as a new artifact with this name instead of creating a new version of the original.",
2478
+ nullable=True,
2479
+ ),
2480
+ "new_description": adk_types.Schema(
2481
+ type=adk_types.Type.STRING,
2482
+ description="Optional. Description for the new/updated artifact.",
2483
+ nullable=True,
2484
+ ),
2485
+ "replacements": adk_types.Schema(
2486
+ type=adk_types.Type.ARRAY,
2487
+ items=adk_types.Schema(
2488
+ type=adk_types.Type.OBJECT,
2489
+ properties={
2490
+ "search": adk_types.Schema(
2491
+ type=adk_types.Type.STRING,
2492
+ description="The search pattern (literal string or regex).",
2493
+ ),
2494
+ "replace": adk_types.Schema(
2495
+ type=adk_types.Type.STRING,
2496
+ description="The replacement text. For regex mode, supports $1, $2, etc. Use $$ for literal $.",
2497
+ ),
2498
+ "is_regexp": adk_types.Schema(
2499
+ type=adk_types.Type.BOOLEAN,
2500
+ description="If true, 'search' is a regex pattern. If false, literal string.",
2501
+ ),
2502
+ "regexp_flags": adk_types.Schema(
2503
+ type=adk_types.Type.STRING,
2504
+ description="Flags for regex: 'g' (global), 'i' (case-insensitive), 'm' (multiline), 's' (dotall). Default: ''.",
2505
+ nullable=True,
2506
+ ),
2507
+ },
2508
+ required=["search", "replace", "is_regexp"],
2509
+ ),
2510
+ description="Optional. Array of replacement operations to perform atomically. Each operation is processed sequentially on the cumulative result. If any operation fails, all changes are rolled back. Do not use with 'search_expression' or 'replace_expression'.",
2511
+ nullable=True,
2512
+ ),
2513
+ },
2514
+ required=["filename"],
2515
+ ),
2516
+ examples=[],
2517
+ )
2518
+
2519
+ tool_registry.register(artifact_search_and_replace_regex_tool_def)