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,436 @@
1
+ / File: lib/legate/web/views/agent.slim
2
+ - agent_data = @view_agent_data
3
+ - is_running = agent_data[:running]
4
+ - agent_name = agent_data[:name]
5
+
6
+ section.section
7
+ .container
8
+ / Breadcrumb Navigation
9
+ nav.breadcrumb.mb-4(aria-label="breadcrumbs")
10
+ ul
11
+ li
12
+ a(href="/agents")
13
+ span.icon.is-small
14
+ i.fas.fa-robot
15
+ span Agents
16
+ li.is-active
17
+ a(href="#" aria-current="page")= agent_name
18
+
19
+ / Hero Header - Streamlined Agent Profile
20
+ .agent-header-hero.mb-5
21
+ / Top row: Name/badges on left, Status/action on right
22
+ .agent-header-top-row
23
+ / Left side: Agent identity (name, badges)
24
+ div#agent-name-display.agent-header-identity
25
+ == slim :_display_agent_name, locals: { agent_data: agent_data }
26
+
27
+ / Right side: Status and action button (same row)
28
+ div#agent-status-controls-container.agent-header-actions
29
+ == slim :_agent_status_controls, locals: { agent_data: agent_data }
30
+
31
+ / Description - integrated into header
32
+ div#agent-description-display.agent-header-description hx-swap="outerHTML"
33
+ == slim :_display_agent_description, locals: { agent_data: agent_data }
34
+
35
+ / Stats bar - tool count, last run, status
36
+ .agent-stats-bar
37
+ a.agent-stat(href="#tools" data-tab-link="tools")
38
+ span.icon.is-small
39
+ i.fas.fa-tools
40
+ span.agent-stat-value= agent_data[:tool_count] || 0
41
+ span.agent-stat-label Tool#{'s' if (agent_data[:tool_count] || 0) != 1}
42
+ .agent-stat
43
+ span.icon.is-small
44
+ i.fas.fa-clock
45
+ - last_run_display = 'Never'
46
+ - if agent_data[:last_run_at]
47
+ - begin
48
+ - last_run_time = Time.parse(agent_data[:last_run_at].to_s)
49
+ - diff_seconds = (Time.now - last_run_time).to_i
50
+ - if diff_seconds < 60
51
+ - last_run_display = "#{diff_seconds}s ago"
52
+ - elsif diff_seconds < 3600
53
+ - last_run_display = "#{diff_seconds / 60}m ago"
54
+ - elsif diff_seconds < 86400
55
+ - last_run_display = "#{diff_seconds / 3600}h ago"
56
+ - else
57
+ - last_run_display = "#{diff_seconds / 86400}d ago"
58
+ - rescue => e
59
+ - last_run_display = 'Unknown'
60
+ span.agent-stat-value= last_run_display
61
+ span.agent-stat-label Last Run
62
+ .agent-stat
63
+ - if is_running
64
+ span.icon.is-small.has-text-success
65
+ i.fas.fa-check-circle
66
+ span.agent-stat-value.has-text-success Active
67
+ - else
68
+ span.icon.is-small.has-text-grey
69
+ i.fas.fa-pause-circle
70
+ span.agent-stat-value.has-text-grey Idle
71
+ span.agent-stat-label Status
72
+
73
+ / Collapsible Agent Details (Type & Hierarchy) - simplified
74
+ - sub_count = agent_data[:sub_agent_names]&.size || 0
75
+ - if sub_count > 0
76
+ details.agent-details-collapsible
77
+ summary.agent-details-summary
78
+ span.icon.is-small
79
+ i.fas.fa-chevron-right
80
+ span Sub-Agents
81
+ span.tag.is-light.is-small.ml-2= "#{sub_count} configured"
82
+ .agent-details-content
83
+ div#agent-hierarchy-display hx-swap="outerHTML"
84
+ == slim :_display_agent_hierarchy, locals: { agent_data: agent_data }
85
+
86
+ .custom-rounded-tabs-container
87
+ .tabs
88
+ ul
89
+ li data-tab="execute"
90
+ a href="#execute"
91
+ span.icon.is-small
92
+ i.fas.fa-play-circle aria-hidden="true"
93
+ span Execute
94
+ / Chat opens the dedicated full-screen chat experience (not embedded —
95
+ / the chat is a full 3-zone shell and must not be crammed into a tab).
96
+ li.tab-link-external
97
+ a href="/agents/#{agent_name}/chat"
98
+ span.icon.is-small
99
+ i.fas.fa-comments aria-hidden="true"
100
+ span Chat
101
+ span.icon.is-small.tab-external-icon
102
+ i.fas.fa-arrow-up-right-from-square aria-hidden="true"
103
+ li data-tab="config"
104
+ a href="#config"
105
+ span.icon.is-small
106
+ i.fas.fa-cogs aria-hidden="true"
107
+ span Config
108
+ li data-tab="tools"
109
+ a href="#tools"
110
+ span.icon.is-small
111
+ i.fas.fa-tools aria-hidden="true"
112
+ span Tools
113
+ li data-tab="auth"
114
+ a href="#auth"
115
+ span.icon.is-small
116
+ i.fas.fa-shield-alt aria-hidden="true"
117
+ span Authentication
118
+
119
+ div#tab-content
120
+
121
+ div.content-tab#execute-tab
122
+ .execute-tab-layout
123
+ / Task Input Section
124
+ .execute-input-section
125
+ .box.execute-input-box
126
+ h3.title.is-5
127
+ span.icon.mr-2
128
+ i.fas.fa-terminal
129
+ | Execute Task
130
+ form#execute-task-form( hx-post="/agents/#{agent_name}/execute"
131
+ hx-target="#task-result-content"
132
+ hx-swap="innerHTML"
133
+ hx-indicator="#execute-task-button span.icon.htmx-indicator" )
134
+ .field
135
+ label.label Task (JSON Input)
136
+ p.help.is-size-7 Format: `{"task": "..."}` or `{"tool_name": "...", ...}`
137
+ .control
138
+ textarea.textarea.is-code-editor#direct-task-json-input name="task_json" placeholder='{"task": "Ask me anything..."}' required=true rows="8"
139
+ .field.is-grouped.is-grouped-right.mt-4
140
+ .control
141
+ button.button.is-link.is-light.is-small( type="button" id="generate-example-task-btn"
142
+ hx-get="/agents/#{agent_name}/generate_example_task"
143
+ hx-indicator="#generate-example-task-btn .htmx-indicator"
144
+ hx-swap="none" )
145
+ span.icon.is-small.htmx-indicator
146
+ i.fas.fa-spinner.fa-spin
147
+ span.icon.is-small
148
+ i.fas.fa-wand-magic-sparkles
149
+ span Example
150
+ .control
151
+ button.button.is-light.is-small(type="button" _=%q{on click put "<span class='terminal-placeholder'>Ready for output...</span>" into #task-result-content})
152
+ span.icon.is-small
153
+ i.fas.fa-eraser
154
+ span Clear
155
+ .control
156
+ button.button.is-success.is-medium#execute-task-button(type="submit" disabled=!is_running)
157
+ span.icon.is-small.htmx-indicator.mr-1
158
+ i.fas.fa-spinner.fa-pulse
159
+ span.icon.is-small
160
+ i.fas.fa-play
161
+ span = is_running ? 'Execute' : 'Start Agent First'
162
+
163
+ / Result Section - Terminal Style
164
+ .execute-result-section
165
+ .terminal-window
166
+ .terminal-header
167
+ .terminal-buttons
168
+ span.terminal-btn.terminal-btn-close
169
+ span.terminal-btn.terminal-btn-minimize
170
+ span.terminal-btn.terminal-btn-maximize
171
+ span.terminal-title Output
172
+ #task-result.terminal-body
173
+ #task-result-content
174
+ span.terminal-placeholder Ready for output...
175
+
176
+ div.content-tab.is-hidden#config-tab
177
+ div#agent-configuration-content
178
+ == slim :_display_agent_configuration, locals: { agent_data: agent_data }
179
+
180
+ div.content-tab.is-hidden#tools-tab
181
+ .box
182
+ .level.mb-4
183
+ .level-left
184
+ h3.title.is-5 Configured Tools
185
+ .level-right
186
+ button.button.is-link.is-light( hx-get="/agents/#{agent_name}/edit/tools"
187
+ hx-target="#agent-tools-table-container"
188
+ hx-swap="outerHTML")
189
+ span.icon.is-small.htmx-indicator
190
+ i.fas.fa-spinner.fa-spin
191
+ span.icon.is-small
192
+ i.fas.fa-pencil-alt
193
+ span Edit Tools
194
+
195
+ div#agent-tools-table-container
196
+ == slim :_agent_tool_table, locals: { agent_data: agent_data, view_configured_tools: view_configured_tools, mcp_tool_results: mcp_tool_results }
197
+
198
+ div.content-tab.is-hidden#auth-tab
199
+ .box.auth-tab-panel
200
+ .level.mb-4
201
+ .level-left
202
+ h3.title.is-5.mb-0 Authentication
203
+ .level-right
204
+ a.button.is-primary.is-light href="/agents/#{agent_name}/auth"
205
+ span.icon.is-small
206
+ i.fas.fa-shield-alt
207
+ span Manage Authentication
208
+ p.has-text-grey.mb-4
209
+ | This agent can have scheme assignments, credential assignments, and URL mappings
210
+ | that override the global authentication defaults.
211
+ .auth-tab-status-row
212
+ span.config-label Status
213
+ #auth-status-indicator.auth-tab-status-value
214
+ == slim :_skeleton, locals: { type: :auth_status }
215
+ .buttons.mt-4
216
+ a.button.is-primary href="/agents/#{agent_name}/auth"
217
+ span.icon
218
+ i.fas.fa-cogs
219
+ span Configure
220
+ button.button onclick="testAgentAuth()"
221
+ span.icon
222
+ i.fas.fa-vial
223
+ span Test
224
+ #auth-test-results.mt-4 style="display: none;"
225
+
226
+
227
+ .mt-6.has-text-centered
228
+ a.button.is-link.is-light(href="/agents")
229
+ span.icon
230
+ i.fas.fa-arrow-left
231
+ span Back to Agents List
232
+
233
+ javascript:
234
+ document.addEventListener('DOMContentLoaded', function () {
235
+ const tabs = document.querySelectorAll('.custom-rounded-tabs-container .tabs li');
236
+ const tabContentBoxes = document.querySelectorAll('#tab-content > .content-tab');
237
+
238
+ function activateTab(tabElement) {
239
+ if (!tabElement) return;
240
+
241
+ tabs.forEach(item => item.classList.remove('is-active'));
242
+ tabContentBoxes.forEach(box => box.classList.add('is-hidden'));
243
+
244
+ tabElement.classList.add('is-active');
245
+ const targetTab = tabElement.getAttribute('data-tab');
246
+ const targetContent = document.getElementById(targetTab + '-tab');
247
+
248
+ if (targetContent) {
249
+ targetContent.classList.remove('is-hidden');
250
+ const editorsInTab = targetContent.querySelectorAll('textarea');
251
+ editorsInTab.forEach(textarea => {
252
+ if (textarea.id && window.codeMirrorInstances && window.codeMirrorInstances[textarea.id]) {
253
+ setTimeout(() => {
254
+ if(window.codeMirrorInstances[textarea.id]) {
255
+ window.codeMirrorInstances[textarea.id].refresh();
256
+ }
257
+ }, 50);
258
+ }
259
+ });
260
+ } else {
261
+ console.error("Could not find tab content for:", targetTab);
262
+ }
263
+
264
+ if (history.pushState) {
265
+ history.pushState(null, null, '#' + targetTab);
266
+ } else {
267
+ window.location.hash = targetTab;
268
+ }
269
+ }
270
+
271
+ tabs.forEach(tab => {
272
+ if (!tab.dataset.tab) return; // skip navigating links (e.g. Chat → dedicated page)
273
+ const anchor = tab.querySelector('a');
274
+ if (anchor) {
275
+ anchor.addEventListener('click', function(event) {
276
+ event.preventDefault();
277
+ activateTab(tab);
278
+ });
279
+ }
280
+ });
281
+
282
+ let activatedFromHash = false;
283
+ if (window.location.hash) {
284
+ const hash = window.location.hash.substring(1);
285
+ const targetTabElement = document.querySelector(`.custom-rounded-tabs-container .tabs li[data-tab="${hash}"]`);
286
+ if (targetTabElement) {
287
+ activateTab(targetTabElement);
288
+ activatedFromHash = true;
289
+ } else {
290
+ console.warn(`Tab for hash #${hash} not found.`);
291
+ }
292
+ }
293
+ if (!activatedFromHash) {
294
+ const defaultTab = document.querySelector('.custom-rounded-tabs-container .tabs li[data-tab="execute"]');
295
+ if (defaultTab) {
296
+ activateTab(defaultTab);
297
+ } else {
298
+ console.warn("Default execute tab not found! Activating first available tab.");
299
+ if (tabs.length > 0) {
300
+ activateTab(tabs[0]);
301
+ } else {
302
+ console.error("No tabs found to activate.");
303
+ }
304
+ }
305
+ }
306
+
307
+ // Quick Actions Dropdown Toggle
308
+ document.querySelectorAll('.agent-quick-actions-dropdown .dropdown-trigger button').forEach(function(button) {
309
+ button.addEventListener('click', function(event) {
310
+ event.stopPropagation();
311
+ const dropdown = button.closest('.dropdown');
312
+ dropdown.classList.toggle('is-active');
313
+ });
314
+ });
315
+
316
+ // Close dropdown when clicking outside
317
+ document.addEventListener('click', function(event) {
318
+ if (!event.target.closest('.agent-quick-actions-dropdown')) {
319
+ document.querySelectorAll('.agent-quick-actions-dropdown.is-active').forEach(function(dropdown) {
320
+ dropdown.classList.remove('is-active');
321
+ });
322
+ }
323
+ });
324
+
325
+ // Close dropdown on Escape key
326
+ document.addEventListener('keydown', function(event) {
327
+ if (event.key === 'Escape') {
328
+ document.querySelectorAll('.agent-quick-actions-dropdown.is-active').forEach(function(dropdown) {
329
+ dropdown.classList.remove('is-active');
330
+ });
331
+ }
332
+ });
333
+
334
+ // Handle tab navigation from stats bar and dropdown
335
+ document.querySelectorAll('[data-tab-link]').forEach(function(link) {
336
+ link.addEventListener('click', function(event) {
337
+ event.preventDefault();
338
+ const tabName = this.getAttribute('data-tab-link');
339
+ const targetTab = document.querySelector(`.custom-rounded-tabs-container .tabs li[data-tab="${tabName}"]`);
340
+ if (targetTab) {
341
+ activateTab(targetTab);
342
+ // Close dropdown if open
343
+ document.querySelectorAll('.agent-quick-actions-dropdown.is-active').forEach(function(dropdown) {
344
+ dropdown.classList.remove('is-active');
345
+ });
346
+ }
347
+ });
348
+ });
349
+
350
+ // Delete agent confirmation
351
+ document.querySelectorAll('[data-action="delete-agent"]').forEach(function(link) {
352
+ link.addEventListener('click', function(event) {
353
+ event.preventDefault();
354
+ const agentName = this.getAttribute('data-agent-name');
355
+ if (confirm(`Are you sure you want to delete the agent "${agentName}"? This action cannot be undone.`)) {
356
+ Legate.apiRequest(`/agents/${encodeURIComponent(agentName)}`, { method: 'DELETE' })
357
+ .then(function () { window.location.href = '/agents'; })
358
+ .catch(function () { alert('Failed to delete agent. Please try again.'); });
359
+ }
360
+ });
361
+ });
362
+
363
+ });
364
+
365
+ // Persist a (possibly runtime-created) agent to agents/<name>.rb so it
366
+ // survives a restart. Uses the global showToast() from the layout.
367
+ function saveAgentToDisk(name) {
368
+ Legate.apiRequest('/agents/' + name + '/save', { method: 'POST' })
369
+ .then(d => showToast('Saved to ' + d.path + ' — survives restart.', 'is-success'))
370
+ .catch(e => showToast(e.message || 'Failed to save agent.', 'is-danger'));
371
+ }
372
+
373
+ // Authentication status loading
374
+ function loadAuthStatus() {
375
+ Legate.apiRequest(`/agents/${encodeURIComponent('#{agent_name}')}/auth/status`)
376
+ .then(data => {
377
+ const indicator = document.getElementById('auth-status-indicator');
378
+ if (data.status === 'success') {
379
+ indicator.innerHTML = '<span class="icon is-large has-text-success"><i class="fas fa-check-circle fa-2x"></i></span><br>Configured';
380
+ } else if (data.status === 'warning') {
381
+ indicator.innerHTML = '<span class="icon is-large has-text-warning"><i class="fas fa-exclamation-triangle fa-2x"></i></span><br>Using Defaults';
382
+ } else {
383
+ indicator.innerHTML = '<span class="icon is-large has-text-danger"><i class="fas fa-times-circle fa-2x"></i></span><br>Issues Found';
384
+ }
385
+ })
386
+ .catch(error => {
387
+ console.error('Error loading auth status:', error);
388
+ const indicator = document.getElementById('auth-status-indicator');
389
+ indicator.innerHTML = '<span class="icon is-large has-text-grey"><i class="fas fa-question-circle fa-2x"></i></span><br>Unknown';
390
+ });
391
+ }
392
+
393
+ // Test agent authentication
394
+ function testAgentAuth() {
395
+ const resultsDiv = document.getElementById('auth-test-results');
396
+ resultsDiv.style.display = 'block';
397
+ resultsDiv.innerHTML = '<div class="notification is-info">Running authentication tests...</div>';
398
+
399
+ Legate.apiRequest(`/agents/${encodeURIComponent('#{agent_name}')}/auth/test`, { method: 'POST' })
400
+ .then(data => {
401
+ if (data.success !== undefined) {
402
+ let html = `<div class="notification is-${data.success ? 'success' : 'danger'}">`;
403
+ html += `<strong>Test Results:</strong> ${data.success ? 'PASSED' : 'FAILED'}<br>`;
404
+ html += `Agent-specific Authentication: ${data.has_agent_auth ? 'Yes' : 'No (using global defaults)'}`;
405
+ html += '</div>';
406
+
407
+ if (data.tests && data.tests.length > 0) {
408
+ html += '<div class="content is-small"><strong>Details:</strong><ul>';
409
+ data.tests.forEach(test => {
410
+ const statusClass = test.status === 'passed' ? 'has-text-success' :
411
+ test.status === 'warning' ? 'has-text-warning' : 'has-text-danger';
412
+ html += `<li class="${statusClass}"><strong>${escapeHtml(test.name)}:</strong> ${escapeHtml(test.message)}</li>`;
413
+ });
414
+ html += '</ul></div>';
415
+ }
416
+
417
+ resultsDiv.innerHTML = html;
418
+ } else {
419
+ Legate.renderError(resultsDiv, data.error);
420
+ }
421
+ })
422
+ .catch(error => {
423
+ console.error('Error testing auth:', error);
424
+ resultsDiv.innerHTML = '<div class="notification is-danger">Error running authentication tests</div>';
425
+ });
426
+ }
427
+
428
+ // Load auth status when auth tab is activated
429
+ document.addEventListener('DOMContentLoaded', function() {
430
+ const authTab = document.querySelector('.custom-rounded-tabs-container .tabs li[data-tab="auth"]');
431
+ if (authTab) {
432
+ authTab.querySelector('a').addEventListener('click', function() {
433
+ setTimeout(loadAuthStatus, 100); // Small delay to ensure tab is active
434
+ });
435
+ }
436
+ });