legate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +345 -0
  4. data/bin/legate +13 -0
  5. data/examples/00_quickstart.rb +51 -0
  6. data/examples/01_simple_agent.rb +105 -0
  7. data/examples/02_multi_tool_agent.rb +140 -0
  8. data/examples/03_custom_tool.rb +93 -0
  9. data/examples/04_agent_instructions.rb +84 -0
  10. data/examples/05_state_and_sessions.rb +91 -0
  11. data/examples/06_callbacks.rb +186 -0
  12. data/examples/07_async_jobs.rb +112 -0
  13. data/examples/08_loop_agent.rb +197 -0
  14. data/examples/09_sequential_workflow.rb +40 -0
  15. data/examples/10_parallel_workflow.rb +34 -0
  16. data/examples/11_agent_delegation.rb +24 -0
  17. data/examples/12_http_client_tool.rb +156 -0
  18. data/examples/13_authentication.rb +220 -0
  19. data/examples/14_mcp_client.rb +154 -0
  20. data/examples/15_mcp_server.rb +79 -0
  21. data/examples/16_webhooks.rb +91 -0
  22. data/examples/README_sequential_agents.md +164 -0
  23. data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
  24. data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
  25. data/examples/advanced/auth/excon_middleware.rb +317 -0
  26. data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
  27. data/examples/advanced/auth/fiber_auth_example.rb +281 -0
  28. data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
  29. data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
  30. data/examples/advanced/auth/oauth2_auth.rb +419 -0
  31. data/examples/advanced/auth/oidc_auth.rb +514 -0
  32. data/examples/advanced/auth/openweather_api.rb +251 -0
  33. data/examples/advanced/auth/openweather_tool.rb +153 -0
  34. data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
  35. data/examples/advanced/auth/service_account.rb +135 -0
  36. data/examples/advanced/auth/test_with_httpbin.rb +202 -0
  37. data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
  38. data/examples/advanced/callback_monitoring.rb +679 -0
  39. data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
  40. data/examples/advanced/mas/loop_workflow.rb +28 -0
  41. data/examples/advanced/mas/mock_planner.rb +77 -0
  42. data/examples/advanced/mas/proper_delegation_example.rb +276 -0
  43. data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
  44. data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
  45. data/examples/advanced/mcp/mcp_server_async.rb +76 -0
  46. data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
  47. data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
  48. data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
  49. data/examples/advanced/random_calculator.rb +104 -0
  50. data/examples/advanced/sleep_agent.rb +153 -0
  51. data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
  52. data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
  53. data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
  54. data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
  55. data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
  56. data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
  57. data/examples/tools/oauth2_example.rb +136 -0
  58. data/examples/tools/sleepy_tool.rb +42 -0
  59. data/lib/legate/activity_log.rb +71 -0
  60. data/lib/legate/agent.rb +959 -0
  61. data/lib/legate/agent_code_generator.rb +185 -0
  62. data/lib/legate/agent_definition.rb +812 -0
  63. data/lib/legate/agentic/decision.rb +49 -0
  64. data/lib/legate/agentic/loop.rb +134 -0
  65. data/lib/legate/agentic.rb +5 -0
  66. data/lib/legate/agents/loop_agent.rb +248 -0
  67. data/lib/legate/agents/parallel_agent.rb +163 -0
  68. data/lib/legate/agents/sequential_agent.rb +190 -0
  69. data/lib/legate/agents.rb +14 -0
  70. data/lib/legate/auth/config.rb +148 -0
  71. data/lib/legate/auth/coordinator.rb +218 -0
  72. data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
  73. data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
  74. data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
  75. data/lib/legate/auth/credential.rb +157 -0
  76. data/lib/legate/auth/encryption.rb +108 -0
  77. data/lib/legate/auth/error.rb +94 -0
  78. data/lib/legate/auth/exchanged_credential.rb +180 -0
  79. data/lib/legate/auth/excon_middleware.rb +285 -0
  80. data/lib/legate/auth/http_client_utils.rb +364 -0
  81. data/lib/legate/auth/manager.rb +531 -0
  82. data/lib/legate/auth/manager_store.rb +394 -0
  83. data/lib/legate/auth/middleware_factory.rb +290 -0
  84. data/lib/legate/auth/runner.rb +279 -0
  85. data/lib/legate/auth/scheme.rb +125 -0
  86. data/lib/legate/auth/schemes/api_key.rb +212 -0
  87. data/lib/legate/auth/schemes/google_service_account.rb +108 -0
  88. data/lib/legate/auth/schemes/http_bearer.rb +98 -0
  89. data/lib/legate/auth/schemes/oauth2.rb +396 -0
  90. data/lib/legate/auth/schemes/openid_connect.rb +346 -0
  91. data/lib/legate/auth/schemes/service_account.rb +388 -0
  92. data/lib/legate/auth/schemes.rb +40 -0
  93. data/lib/legate/auth/token_manager.rb +362 -0
  94. data/lib/legate/auth/token_store.rb +86 -0
  95. data/lib/legate/auth/tool_context_extension.rb +97 -0
  96. data/lib/legate/auth/tool_integration.rb +188 -0
  97. data/lib/legate/auth/url_guard.rb +81 -0
  98. data/lib/legate/auth.rb +453 -0
  99. data/lib/legate/callbacks/callback_context.rb +71 -0
  100. data/lib/legate/cli/agent_commands.rb +950 -0
  101. data/lib/legate/cli/auth_commands.rb +520 -0
  102. data/lib/legate/cli/base_command.rb +24 -0
  103. data/lib/legate/cli/deployment_commands.rb +934 -0
  104. data/lib/legate/cli/output_helper.rb +108 -0
  105. data/lib/legate/cli/session_commands.rb +138 -0
  106. data/lib/legate/cli/skaffold_commands.rb +223 -0
  107. data/lib/legate/cli/tool_commands.rb +261 -0
  108. data/lib/legate/cli/web_commands.rb +182 -0
  109. data/lib/legate/cli.rb +40 -0
  110. data/lib/legate/configuration/webhooks.rb +113 -0
  111. data/lib/legate/configuration.rb +39 -0
  112. data/lib/legate/definition_store.rb +23 -0
  113. data/lib/legate/errors.rb +118 -0
  114. data/lib/legate/event.rb +161 -0
  115. data/lib/legate/gemini_ai_beta_patch.rb +39 -0
  116. data/lib/legate/generators/agent_generator.rb +412 -0
  117. data/lib/legate/generators/code_validator.rb +48 -0
  118. data/lib/legate/generators/legate/install_generator.rb +35 -0
  119. data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
  120. data/lib/legate/generators/legate/templates/initializer.rb +18 -0
  121. data/lib/legate/generators/runtime_tool_loader.rb +76 -0
  122. data/lib/legate/generators/tool_generator.rb +408 -0
  123. data/lib/legate/generators.rb +11 -0
  124. data/lib/legate/global_definition_registry.rb +506 -0
  125. data/lib/legate/global_tool_manager.rb +135 -0
  126. data/lib/legate/llm/adapter.rb +69 -0
  127. data/lib/legate/llm/gemini.rb +172 -0
  128. data/lib/legate/llm/ollama.rb +80 -0
  129. data/lib/legate/llm.rb +34 -0
  130. data/lib/legate/mcp/client.rb +320 -0
  131. data/lib/legate/mcp/connection/sse.rb +292 -0
  132. data/lib/legate/mcp/connection/stdio.rb +273 -0
  133. data/lib/legate/mcp/connection_manager.rb +103 -0
  134. data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
  135. data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
  136. data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
  137. data/lib/legate/mcp/tool_wrapper.rb +138 -0
  138. data/lib/legate/mcp/util/schema_converter.rb +134 -0
  139. data/lib/legate/mcp.rb +23 -0
  140. data/lib/legate/plan_executor.rb +375 -0
  141. data/lib/legate/planner.rb +839 -0
  142. data/lib/legate/rails/railtie.rb +43 -0
  143. data/lib/legate/rails.rb +9 -0
  144. data/lib/legate/redaction.rb +32 -0
  145. data/lib/legate/session.rb +299 -0
  146. data/lib/legate/session_service/active_record.rb +300 -0
  147. data/lib/legate/session_service/base.rb +68 -0
  148. data/lib/legate/session_service/event_broadcast.rb +74 -0
  149. data/lib/legate/session_service/in_memory.rb +188 -0
  150. data/lib/legate/tool/metadata_dsl.rb +122 -0
  151. data/lib/legate/tool.rb +276 -0
  152. data/lib/legate/tool_code_generator.rb +103 -0
  153. data/lib/legate/tool_context.rb +350 -0
  154. data/lib/legate/tool_loader.rb +39 -0
  155. data/lib/legate/tool_registry.rb +73 -0
  156. data/lib/legate/tool_result.rb +61 -0
  157. data/lib/legate/tools/agent_tool.rb +187 -0
  158. data/lib/legate/tools/base/http_client.rb +319 -0
  159. data/lib/legate/tools/base/safe_url.rb +56 -0
  160. data/lib/legate/tools/base_async_job_tool.rb +91 -0
  161. data/lib/legate/tools/calculator.rb +89 -0
  162. data/lib/legate/tools/cat_facts.rb +81 -0
  163. data/lib/legate/tools/check_job_status_tool.rb +48 -0
  164. data/lib/legate/tools/current_time_tool.rb +64 -0
  165. data/lib/legate/tools/echo.rb +43 -0
  166. data/lib/legate/tools/http_request_tool.rb +105 -0
  167. data/lib/legate/tools/random_number_tool.rb +64 -0
  168. data/lib/legate/tools/read_webpage_tool.rb +92 -0
  169. data/lib/legate/tools/sleepy_tool.rb +74 -0
  170. data/lib/legate/tools/webhook_tool.rb +146 -0
  171. data/lib/legate/version.rb +5 -0
  172. data/lib/legate/web/app.rb +984 -0
  173. data/lib/legate/web/public/css/main.css +4980 -0
  174. data/lib/legate/web/public/images/favicon-256.png +0 -0
  175. data/lib/legate/web/public/images/favicon-32.png +0 -0
  176. data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
  177. data/lib/legate/web/public/images/legate-logo-light.png +0 -0
  178. data/lib/legate/web/public/js/legate.js +616 -0
  179. data/lib/legate/web/public/styles/main.scss +4402 -0
  180. data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
  181. data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
  182. data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
  183. data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
  184. data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
  185. data/lib/legate/web/routes/api_routes.rb +56 -0
  186. data/lib/legate/web/routes/authentication_routes.rb +1541 -0
  187. data/lib/legate/web/routes/core_routes.rb +111 -0
  188. data/lib/legate/web/routes/documentation_routes.rb +220 -0
  189. data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
  190. data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
  191. data/lib/legate/web/sass_compiler.rb +73 -0
  192. data/lib/legate/web/views/_active_session_info.slim +25 -0
  193. data/lib/legate/web/views/_activity_list.slim +55 -0
  194. data/lib/legate/web/views/_agent_card.slim +56 -0
  195. data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
  196. data/lib/legate/web/views/_agent_status_controls.slim +71 -0
  197. data/lib/legate/web/views/_agent_tool_table.slim +74 -0
  198. data/lib/legate/web/views/_chat_message.slim +95 -0
  199. data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
  200. data/lib/legate/web/views/_display_agent_description.slim +11 -0
  201. data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
  202. data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
  203. data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
  204. data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
  205. data/lib/legate/web/views/_display_agent_model.slim +17 -0
  206. data/lib/legate/web/views/_display_agent_name.slim +42 -0
  207. data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
  208. data/lib/legate/web/views/_display_agent_type.slim +65 -0
  209. data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
  210. data/lib/legate/web/views/_edit_agent_description.slim +16 -0
  211. data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
  212. data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
  213. data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
  214. data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
  215. data/lib/legate/web/views/_edit_agent_model.slim +23 -0
  216. data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
  217. data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
  218. data/lib/legate/web/views/_edit_agent_type.slim +67 -0
  219. data/lib/legate/web/views/_session_error.slim +4 -0
  220. data/lib/legate/web/views/_skeleton.slim +69 -0
  221. data/lib/legate/web/views/_tool_card.slim +9 -0
  222. data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
  223. data/lib/legate/web/views/agent.slim +436 -0
  224. data/lib/legate/web/views/agent_auth.slim +562 -0
  225. data/lib/legate/web/views/agents.slim +369 -0
  226. data/lib/legate/web/views/auth.slim +112 -0
  227. data/lib/legate/web/views/auth_credential_detail.slim +327 -0
  228. data/lib/legate/web/views/auth_credentials.slim +261 -0
  229. data/lib/legate/web/views/auth_debug.slim +94 -0
  230. data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
  231. data/lib/legate/web/views/auth_mapping_new.slim +123 -0
  232. data/lib/legate/web/views/auth_mappings.slim +120 -0
  233. data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
  234. data/lib/legate/web/views/auth_schemes.slim +259 -0
  235. data/lib/legate/web/views/auth_test.slim +418 -0
  236. data/lib/legate/web/views/chat.slim +192 -0
  237. data/lib/legate/web/views/docs_index.slim +105 -0
  238. data/lib/legate/web/views/docs_show.slim +105 -0
  239. data/lib/legate/web/views/error_404.slim +5 -0
  240. data/lib/legate/web/views/index.slim +148 -0
  241. data/lib/legate/web/views/layout.slim +144 -0
  242. data/lib/legate/web/views/tool_detail.slim +87 -0
  243. data/lib/legate/web/views/tools.slim +50 -0
  244. data/lib/legate/web/webhook_listener.rb +367 -0
  245. data/lib/legate/web.rb +9 -0
  246. data/lib/legate.rb +220 -0
  247. data/public/docs/advanced/callbacks.md +828 -0
  248. data/public/docs/advanced/mcp_schema_conversion.md +59 -0
  249. data/public/docs/authentication/api_reference/config.md +210 -0
  250. data/public/docs/authentication/api_reference/credential.md +246 -0
  251. data/public/docs/authentication/api_reference/encryption.md +218 -0
  252. data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
  253. data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
  254. data/public/docs/authentication/api_reference/index.md +30 -0
  255. data/public/docs/authentication/api_reference/scheme.md +250 -0
  256. data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
  257. data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
  258. data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
  259. data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
  260. data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
  261. data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
  262. data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
  263. data/public/docs/authentication/api_reference/token_manager.md +221 -0
  264. data/public/docs/authentication/api_reference/token_store.md +146 -0
  265. data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
  266. data/public/docs/authentication/guides/api_key.md +190 -0
  267. data/public/docs/authentication/guides/bearer.md +172 -0
  268. data/public/docs/authentication/guides/configuration.md +255 -0
  269. data/public/docs/authentication/guides/custom_flow.md +523 -0
  270. data/public/docs/authentication/guides/index.md +24 -0
  271. data/public/docs/authentication/guides/migration.md +435 -0
  272. data/public/docs/authentication/guides/oauth2.md +252 -0
  273. data/public/docs/authentication/guides/oidc.md +241 -0
  274. data/public/docs/authentication/guides/overview.md +155 -0
  275. data/public/docs/authentication/guides/secure_storage.md +301 -0
  276. data/public/docs/authentication/guides/service_account.md +228 -0
  277. data/public/docs/authentication/guides/token_lifecycle.md +295 -0
  278. data/public/docs/authentication/guides/web_ui_integration.md +504 -0
  279. data/public/docs/authentication/index.md +58 -0
  280. data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
  281. data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
  282. data/public/docs/authentication/troubleshooting/index.md +11 -0
  283. data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
  284. data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
  285. data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
  286. data/public/docs/cli/legate_cli_usage.md +363 -0
  287. data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
  288. data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
  289. data/public/docs/core_concepts/legate_configuration.md +116 -0
  290. data/public/docs/core_concepts/legate_definition_store.md +102 -0
  291. data/public/docs/core_concepts/legate_planner.md +94 -0
  292. data/public/docs/core_concepts/legate_session_service.md +104 -0
  293. data/public/docs/error_handling/legate_error_handling.md +122 -0
  294. data/public/docs/examples.md +199 -0
  295. data/public/docs/getting_started.md +111 -0
  296. data/public/docs/guides/agentic_agents.md +137 -0
  297. data/public/docs/guides/ai_code_generators.md +437 -0
  298. data/public/docs/guides/auto_loading.md +326 -0
  299. data/public/docs/guides/configuring_agent_webhooks.md +219 -0
  300. data/public/docs/guides/http_client_usage.md +264 -0
  301. data/public/docs/guides/llm_providers.md +137 -0
  302. data/public/docs/guides/mcp_client_integration.md +232 -0
  303. data/public/docs/guides/mcp_server_exposure.md +206 -0
  304. data/public/docs/guides/rails_integration.md +128 -0
  305. data/public/docs/guides/sending_outbound_webhooks.md +227 -0
  306. data/public/docs/guides/streaming.md +112 -0
  307. data/public/docs/guides/webhooks.md +288 -0
  308. data/public/docs/introduction.md +51 -0
  309. data/public/docs/multi_agent_systems/advanced_features.md +57 -0
  310. data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
  311. data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
  312. data/public/docs/multi_agent_systems/state_management.md +47 -0
  313. data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
  314. data/public/docs/tools/legate_built_in_tools.md +332 -0
  315. data/public/docs/tools/legate_tools_and_registry.md +263 -0
  316. data/public/docs/web_ui/legate_web_ui.md +137 -0
  317. metadata +823 -0
@@ -0,0 +1,323 @@
1
+ # File: lib/legate/web/routes/agent_runtime_routes.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Legate
5
+ module Web
6
+ module AgentRuntimeRoutes
7
+ def self.registered(app)
8
+ # POST /agents/:name/start/detail - Start a runtime instance (from agent detail view).
9
+ app.post '/agents/:name/start/detail' do |name|
10
+ content_type :html
11
+ agent_instance = send(:_start_agent, name) # Renamed to avoid confusion
12
+
13
+ is_running = !agent_instance.nil?
14
+ agent_data_for_view = nil
15
+ definition_store = instance_variable_get(:@definition_store)
16
+
17
+ if agent_instance # Agent started successfully
18
+ # Ensure agent_data_for_view is a Hash, similar to the else block
19
+ definition = definition_store&.get_definition(name) # Re-fetch definition for consistency
20
+ agent_data_for_view = if definition
21
+ {
22
+ name: name,
23
+ description: definition[:description],
24
+ running: is_running,
25
+ model: definition[:model],
26
+ tool_count: definition[:tools]&.size || 0
27
+ }
28
+ else # Should not happen if agent started, but as a fallback
29
+ { name: name, description: 'Started (Def error)', running: is_running,
30
+ model: 'N/A', tool_count: 0 }
31
+ end
32
+ elsif definition_store # Agent failed to start, fetch definition for display
33
+ begin
34
+ definition = definition_store.get_definition(name)
35
+ agent_data_for_view = if definition
36
+ {
37
+ name: name, description: definition[:description],
38
+ running: false, model: definition[:model],
39
+ tool_count: definition[:tools]&.size || 0
40
+ }
41
+ else
42
+ { name: name, description: 'Error: Definition not found', running: false,
43
+ model: 'N/A', tool_count: 0 }
44
+ end
45
+ rescue Legate::DefinitionStore::StoreError => e
46
+ logger.error("Store error fetching definition after failed start for '#{name}' (from AgentRuntimeRoutes): #{e.message}")
47
+ agent_data_for_view = { name: name, description: 'Error retrieving definition', running: false,
48
+ model: 'N/A', tool_count: 0 }
49
+ end
50
+ else
51
+ agent_data_for_view = { name: name, description: 'Error: Store unavailable', running: false,
52
+ model: 'N/A', tool_count: 0 }
53
+ end
54
+
55
+ # agent_data_for_view is now consistently a Hash
56
+ # ... rest of the route uses agent_data_for_view[:running] ...
57
+
58
+ status_controls_html = slim(:_agent_status_controls, layout: false,
59
+ locals: { agent_data: agent_data_for_view })
60
+
61
+ execute_button_text = is_running ? 'Execute' : 'Execute (Requires Start)'
62
+ disabled_attr_string = is_running ? '' : 'disabled'
63
+ execute_button_oob_html = %(
64
+ <button class="button is-primary" id="execute-task-button" type="submit" #{disabled_attr_string} hx-swap-oob="outerHTML">
65
+ <span class="icon is-small"><i class="fas fa-play-circle"></i></span>
66
+ <span>#{execute_button_text}</span>
67
+ </button>
68
+ )
69
+ chat_input_oob_html = %(<input class="input" id="chat-input" type="text" name="message" placeholder="Enter your message..." required="true" autofocus #{disabled_attr_string} hx-swap-oob="outerHTML">)
70
+ chat_button_oob_html = %(<button class="button is-link" id="send-button" type="submit" #{disabled_attr_string} hx-swap-oob="outerHTML"><span>Send</span><span class="icon is-small htmx-indicator ml-2" id="send-button-indicator"><i class="fas fa-spinner fa-pulse"></i></span></button>)
71
+
72
+ # --- MODIFIED: Determine content for chat-status-help-container ---
73
+ new_chat_status_help_content = ''
74
+ if is_running # is_running reflects the new state of the agent
75
+ # Agent is running, attempt to ensure an active session for the help text
76
+ web_user_id = session[:web_user_id]
77
+ session_service = instance_variable_get(:@session_service)
78
+ active_session_object_for_help_check = nil
79
+
80
+ if web_user_id && session_service
81
+ # 1. Try to load session from Sinatra session store
82
+ stored_active_session_id = session.dig(:active_agent_sessions, name)
83
+ if stored_active_session_id
84
+ begin
85
+ potential_session = session_service.get_session(session_id: stored_active_session_id)
86
+ if potential_session && potential_session.user_id == web_user_id && potential_session.app_name == name
87
+ active_session_object_for_help_check = potential_session
88
+ logger.debug "OOB Chat Help (Start): Found valid session '#{stored_active_session_id}' from Sinatra session for agent '#{name}'."
89
+ else
90
+ logger.warn "OOB Chat Help (Start): Stored session ID '#{stored_active_session_id}' invalid/mismatched for agent '#{name}'. Clearing from Sinatra session."
91
+ session[:active_agent_sessions]&.delete(name)
92
+ end
93
+ rescue StandardError => e
94
+ logger.error("OOB Chat Help (Start): Error fetching stored session '#{stored_active_session_id}' for agent '#{name}': #{e.message}")
95
+ session[:active_agent_sessions]&.delete(name) # Clear if error
96
+ end
97
+ end
98
+
99
+ # 2. If no valid session from Sinatra store, try to find latest or create new
100
+ unless active_session_object_for_help_check
101
+ logger.debug "OOB Chat Help (Start): No valid session from Sinatra store. Attempting to find/create for agent '#{name}', user '#{web_user_id}'."
102
+ begin
103
+ user_agent_sessions = session_service.list_sessions(app_name: name, user_id: web_user_id)
104
+ if user_agent_sessions && !user_agent_sessions.empty?
105
+ latest_session = user_agent_sessions.sort_by(&:updated_at).last
106
+ if latest_session
107
+ active_session_object_for_help_check = latest_session
108
+ logger.info "OOB Chat Help (Start): Found latest existing session '#{latest_session.id}' for agent '#{name}'."
109
+ end
110
+ end
111
+
112
+ unless active_session_object_for_help_check
113
+ new_session = session_service.create_session(app_name: name, user_id: web_user_id,
114
+ initial_state: {})
115
+ active_session_object_for_help_check = new_session
116
+ logger.info "OOB Chat Help (Start): Created new session '#{new_session.id}' for agent '#{name}'."
117
+ end
118
+
119
+ # Store the found/created session ID in Sinatra session
120
+ if active_session_object_for_help_check
121
+ session[:active_agent_sessions] ||= {}
122
+ session[:active_agent_sessions][name] = active_session_object_for_help_check.id
123
+ logger.debug "OOB Chat Help (Start): Stored session '#{active_session_object_for_help_check.id}' in Sinatra session for agent '#{name}'."
124
+ end
125
+ rescue StandardError => e
126
+ logger.error "OOB Chat Help (Start): Critical error finding/creating session for agent '#{name}', user '#{web_user_id}': #{e.message}"
127
+ # If session creation fails, active_session_object_for_help_check will remain nil
128
+ end
129
+ end
130
+ else
131
+ logger.error "OOB Chat Help (Start): web_user_id or session_service not available. Cannot manage session for help text for agent '#{name}'."
132
+ end
133
+
134
+ # Final determination of help content based on whether an active session is now established
135
+ new_chat_status_help_content = if active_session_object_for_help_check
136
+ %(<p class="help is-hidden">No issues.</p>)
137
+ else
138
+ # This branch means session_service might be down, or a user_id is missing, or find/create failed critically.
139
+ %(<p id="chat-no-active-session-help" class="help is-warning">Chat session not active. Please try starting a new chat or reloading the page.</p>)
140
+ end
141
+ else
142
+ # Agent is not running
143
+ new_chat_status_help_content = %(<p id="chat-not-running-help" class="help is-danger">Agent must be running to chat.</p>)
144
+ end
145
+
146
+ chat_help_oob_html = %(<div id="chat-status-help-container" hx-swap-oob="innerHTML" class="mt-2">#{new_chat_status_help_content}</div>)
147
+ # --- END MODIFICATION ---
148
+
149
+ chat_panel_status_html = %(
150
+ <div id="agent-chat-panel-status-display" hx-swap-oob="innerHTML">
151
+ <p>
152
+ <strong>Status:</strong>
153
+ <span class="tag ml-2 #{agent_data_for_view[:running] ? 'is-success' : 'is-danger'}">
154
+ #{agent_data_for_view[:running] ? 'Running' : 'Stopped (Go to agent page to start)'}
155
+ </span>
156
+ </p>
157
+ </div>
158
+ )
159
+ status 200
160
+ status_controls_html + execute_button_oob_html + chat_input_oob_html + chat_button_oob_html + chat_help_oob_html + chat_panel_status_html
161
+ end
162
+
163
+ # POST /agents/:name/stop/detail - Stop a runtime instance (from agent detail view).
164
+ app.post '/agents/:name/stop/detail' do |name|
165
+ content_type :html
166
+ send(:_stop_agent, name) # Call private helper
167
+
168
+ # Explicitly set status variables after stopping
169
+ is_running = false
170
+ disabled_attr_string = 'disabled'
171
+ execute_button_text = 'Execute (Requires Start)'
172
+ # Ensure agent_data_for_view reflects running: false (already done in previous step)
173
+
174
+ agent_data_for_view = nil
175
+ definition_store = instance_variable_get(:@definition_store)
176
+ if definition_store
177
+ begin
178
+ agent_definition = definition_store.get_definition(name)
179
+ agent_data_for_view = if agent_definition
180
+ {
181
+ name: name, description: agent_definition[:description],
182
+ running: is_running, # Use the explicitly set 'is_running'
183
+ model: agent_definition[:model],
184
+ tool_count: agent_definition[:tools]&.size || 0
185
+ }
186
+ else
187
+ { name: name, description: 'Error: Definition not found', running: is_running,
188
+ model: 'N/A', tool_count: 0 }
189
+ end
190
+ rescue Legate::DefinitionStore::StoreError => e
191
+ logger.error("Store error fetching definition after stop detail for '#{name}' (from AgentRuntimeRoutes): #{e.message}")
192
+ agent_data_for_view = { name: name, description: 'Error retrieving definition', running: is_running,
193
+ model: 'N/A', tool_count: 0 }
194
+ end
195
+ else
196
+ agent_data_for_view = { name: name, description: 'Error: Store unavailable', running: is_running,
197
+ model: 'N/A', tool_count: 0 }
198
+ end
199
+
200
+ status_controls_html = slim(:_agent_status_controls, layout: false,
201
+ locals: { agent_data: agent_data_for_view })
202
+
203
+ execute_button_oob_html = %(<button class="button is-link" id="execute-task-button" type="submit" #{disabled_attr_string} hx-swap-oob="outerHTML"> <span class="icon is-small htmx-indicator mr-1"><i class="fas fa-spinner fa-pulse"></i></span> <span class="icon is-small"><i class="fas fa-play-circle"></i></span> <span>#{execute_button_text}</span> </button>)
204
+ chat_input_oob_html = %(<input class="input" id="chat-input" type="text" name="message" placeholder="Enter your message..." required="true" autofocus #{disabled_attr_string} hx-swap-oob="outerHTML">)
205
+ chat_button_oob_html = %(<button class="button is-link" id="send-button" type="submit" #{disabled_attr_string} hx-swap-oob="outerHTML"><span>Send</span><span class="icon is-small htmx-indicator ml-2" id="send-button-indicator"><i class="fas fa-spinner fa-pulse"></i></span></button>)
206
+
207
+ # --- MODIFIED: Determine content for chat-status-help-container ---
208
+ # is_running is false in this context (agent stop)
209
+ new_chat_status_help_content = ''
210
+ if is_running # This will be false
211
+ # This block should ideally not be reached if is_running is false,
212
+ # but keeping structure for clarity, will be optimized by the 'else'.
213
+ web_user_id = session[:web_user_id]
214
+ session_service = instance_variable_get(:@session_service)
215
+ active_session_details = nil
216
+ # 'name' is the agent_name from the route parameter
217
+
218
+ if web_user_id && session_service
219
+ active_session_id = session.dig(:active_agent_sessions, name)
220
+ if active_session_id
221
+ begin
222
+ potential_session = session_service.get_session(session_id: active_session_id)
223
+ active_session_details = potential_session if potential_session && potential_session.user_id == web_user_id && potential_session.app_name == name
224
+ rescue StandardError => e
225
+ logger.error("OOB Chat Help (Stop - Running Path, Unexpected): Error fetching session #{active_session_id} for agent #{name}: #{e.message}")
226
+ end
227
+ end
228
+ end
229
+
230
+ new_chat_status_help_content = if active_session_details
231
+ %(<p class="help is-hidden">No issues.</p>)
232
+ else
233
+ %(<p id="chat-no-active-session-help" class="help is-warning">No active chat session. Please start a new chat from the sidebar.</p>)
234
+ end
235
+ else
236
+ # Agent is not running (this is the expected path for agent stop)
237
+ new_chat_status_help_content = %(<p id="chat-not-running-help" class="help is-danger">Agent must be running to chat.</p>)
238
+ end
239
+
240
+ chat_help_oob_html = %(<div id="chat-status-help-container" hx-swap-oob="innerHTML" class="mt-2">#{new_chat_status_help_content}</div>)
241
+ # --- END MODIFICATION ---
242
+
243
+ chat_panel_status_html = %(
244
+ <div id="agent-chat-panel-status-display" hx-swap-oob="innerHTML">
245
+ <p>
246
+ <strong>Status:</strong>
247
+ <span class="tag ml-2 #{agent_data_for_view[:running] ? 'is-success' : 'is-danger'}">
248
+ #{agent_data_for_view[:running] ? 'Running' : 'Stopped (Go to agent page to start)'}
249
+ </span>
250
+ </p>
251
+ </div>
252
+ )
253
+ status 200
254
+ status_controls_html + execute_button_oob_html + chat_input_oob_html + chat_button_oob_html + chat_help_oob_html + chat_panel_status_html
255
+ end
256
+
257
+ # Start agent from main list view (hx-post from _agent_card.slim)
258
+ app.post '/agents/:name/start' do |name|
259
+ agent = send(:_start_agent, name)
260
+ definition_store = instance_variable_get(:@definition_store)
261
+ tool_manager = instance_variable_get(:@tool_manager)
262
+ available_tools = tool_manager&.tools&.map { |t| { name: t.name, description: t.description } } || []
263
+
264
+ # Re-fetch definition for display
265
+ agent_definition = definition_store&.get_definition(name) if definition_store
266
+ active_agents_hash = instance_variable_get(:@agents)
267
+
268
+ agent_data = if agent_definition
269
+ agent_definition.dup
270
+ else
271
+ { name: name, description: 'N/A', model: 'N/A', tools: [], configured_tools: [] }
272
+ end
273
+
274
+ agent_data[:configured_tools] ||= agent_data[:tools] || []
275
+ # Running state is determined by in-memory @agents hash
276
+ agent_data[:running] = active_agents_hash.key?(name)
277
+
278
+ if agent
279
+ logger.info "Agent '#{name}' started from main list (from AgentRuntimeRoutes)."
280
+ else
281
+ logger.error "Failed to start agent '#{name}' from main list (from AgentRuntimeRoutes)."
282
+ end
283
+
284
+ status 200
285
+ content_type :html
286
+ slim :_agent_card, layout: false, locals: { agent_info: agent_data, available_tools: available_tools }
287
+ end
288
+
289
+ # Stop agent from main list view (hx-post from _agent_card.slim)
290
+ app.post '/agents/:name/stop' do |name|
291
+ success = send(:_stop_agent, name)
292
+ definition_store = instance_variable_get(:@definition_store)
293
+ tool_manager = instance_variable_get(:@tool_manager)
294
+ available_tools = tool_manager&.tools&.map { |t| { name: t.name, description: t.description } } || []
295
+
296
+ # Re-fetch definition for display
297
+ agent_definition = definition_store&.get_definition(name) if definition_store
298
+ active_agents_hash = instance_variable_get(:@agents)
299
+
300
+ agent_data = if agent_definition
301
+ agent_definition.dup
302
+ else
303
+ { name: name, description: 'N/A', model: 'N/A', tools: [], configured_tools: [] }
304
+ end
305
+
306
+ agent_data[:configured_tools] ||= agent_data[:tools] || []
307
+ # Running state is determined by in-memory @agents hash
308
+ agent_data[:running] = active_agents_hash.key?(name)
309
+
310
+ if success
311
+ logger.info "Agent '#{name}' stopped from main list (from AgentRuntimeRoutes)."
312
+ else
313
+ logger.error "Failed to stop agent '#{name}' from main list (from AgentRuntimeRoutes)."
314
+ end
315
+
316
+ status 200
317
+ content_type :html
318
+ slim :_agent_card, layout: false, locals: { agent_info: agent_data, available_tools: available_tools }
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,56 @@
1
+ # File: lib/legate/web/routes/api_routes.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Legate
5
+ module Web
6
+ module ApiRoutes
7
+ def self.registered(app)
8
+ # GET /api/agents - List all defined agents and their status.
9
+ app.get '/api/agents' do
10
+ content_type :json
11
+ agents_data = []
12
+ current_app_instance = self # Get current app instance
13
+ definition_store = current_app_instance.instance_variable_get(:@definition_store)
14
+ active_agents_hash = current_app_instance.instance_variable_get(:@agents)
15
+
16
+ if definition_store
17
+ begin
18
+ agent_summaries = definition_store.list_definitions
19
+ agents_data = agent_summaries.map do |summary|
20
+ agent_name = summary[:name]
21
+ # @agents is keyed by the agent's STRING name; summary[:name] is a Symbol.
22
+ running_key = agent_name.to_s
23
+ is_running = active_agents_hash.key?(running_key)
24
+ current_model = if is_running && active_agents_hash[running_key]
25
+ active_agents_hash[running_key].model_name
26
+ else
27
+ summary[:model]
28
+ end
29
+ {
30
+ name: agent_name,
31
+ description: summary[:description] || 'N/A',
32
+ running: is_running,
33
+ model: current_model
34
+ }
35
+ end
36
+ rescue Legate::DefinitionStore::StoreError => e
37
+ logger.error("Store error fetching agent list for API (from ApiRoutes): #{e.message}")
38
+ agents_data = []
39
+ end
40
+ else
41
+ logger.error('Definition Store unavailable during GET /api/agents (from ApiRoutes)')
42
+ end
43
+ json agents: agents_data.sort_by { |a| a[:name] }
44
+ end
45
+
46
+ # GET /api/tools - List all available *native* tools known to the GlobalToolManager.
47
+ # Does not include MCP tools.
48
+ # Returns JSON: `{"tools": [{"name": ..., "description": ..., "parameters": [...]}, ...]}`
49
+ app.get '/api/tools' do
50
+ content_type :json
51
+ json tools: Legate::GlobalToolManager.list_all_tools
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end