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,108 @@
1
+ # File: lib/legate/cli/output_helper.rb
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+
6
+ module Legate
7
+ module CLI
8
+ # Helper module for CLI output control.
9
+ # Provides methods to conditionally output status messages and format results
10
+ # based on --quiet and --json flags.
11
+ module OutputHelper
12
+ # Write status/progress message (suppressed in quiet or json mode)
13
+ # @param message [String] The status message to display
14
+ # @param color [Symbol, nil] Optional color for Thor's say method
15
+ def status_message(message, color = nil)
16
+ return if quiet_mode?
17
+
18
+ say message, color
19
+ end
20
+
21
+ # Output final result (JSON format in --json mode, otherwise uses format_method or default)
22
+ # @param data [Object] The result data to output
23
+ # @param metadata [Hash] Additional metadata to include (e.g., session_id)
24
+ # @param format_method [Symbol] Method name to call for human-friendly formatting
25
+ def output_result(data, metadata: {}, format_method: nil)
26
+ if json_mode?
27
+ output_json(data, metadata)
28
+ elsif format_method && respond_to?(format_method, true)
29
+ send(format_method, data)
30
+ else
31
+ say data.inspect
32
+ end
33
+ end
34
+
35
+ # Output error (JSON format in --json mode, otherwise text to stderr)
36
+ # @param error [Exception, String] The error to output
37
+ # @param metadata [Hash] Additional metadata to include
38
+ def output_error(error, metadata: {})
39
+ if json_mode?
40
+ error_data = {
41
+ status: 'error',
42
+ error_class: error.is_a?(Exception) ? error.class.name : 'Error',
43
+ error_message: error.is_a?(Exception) ? error.message : error.to_s
44
+ }
45
+ error_data.merge!(metadata) unless metadata.empty?
46
+ puts JSON.generate(error_data)
47
+ else
48
+ message = error.is_a?(Exception) ? "#{error.class} - #{error.message}" : error.to_s
49
+ say message, :red
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Check if quiet mode is enabled (--quiet or --json)
56
+ # Thor options may use string or symbol keys depending on how they're accessed
57
+ def quiet_mode?
58
+ options['quiet'] || options[:quiet] || options['json'] || options[:json]
59
+ end
60
+
61
+ # Check if JSON output mode is enabled
62
+ def json_mode?
63
+ options['json'] || options[:json]
64
+ end
65
+
66
+ # Output data as JSON
67
+ def output_json(data, metadata)
68
+ result = normalize_for_json(data)
69
+ output = metadata.empty? ? result : metadata.merge(result: result)
70
+ puts JSON.generate(output)
71
+ end
72
+
73
+ # Normalize various data types to JSON-serializable format
74
+ def normalize_for_json(data)
75
+ case data
76
+ when Legate::Event
77
+ normalize_event(data)
78
+ when Hash
79
+ normalize_hash(data)
80
+ when Array
81
+ data.map { |item| normalize_for_json(item) }
82
+ else
83
+ data
84
+ end
85
+ end
86
+
87
+ def normalize_event(event)
88
+ {
89
+ role: event.role.to_s,
90
+ content: normalize_for_json(event.content),
91
+ tool_name: event.tool_name&.to_s,
92
+ timestamp: event.timestamp
93
+ }.compact
94
+ end
95
+
96
+ def normalize_hash(hash)
97
+ hash.transform_keys(&:to_s).transform_values do |v|
98
+ case v
99
+ when Symbol then v.to_s
100
+ when Hash then normalize_hash(v)
101
+ when Array then v.map { |item| normalize_for_json(item) }
102
+ else v
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,138 @@
1
+ # File: lib/legate/cli/session_commands.rb
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor'
5
+ require_relative 'base_command'
6
+ require 'json'
7
+ require_relative '../session'
8
+ require_relative '../session_service/in_memory'
9
+
10
+ module Legate
11
+ module CLI
12
+ # CLI commands for session management
13
+ class SessionCommands < BaseCommand
14
+ desc 'list [APP_NAME] [USER_ID]', 'List all sessions or filter by app_name and/or user_id'
15
+ method_option :app_name, type: :string, desc: 'Filter sessions by application name'
16
+ method_option :user_id, type: :string, desc: 'Filter sessions by user ID'
17
+ def list(*args)
18
+ # Parse positional arguments if provided
19
+ app_name = options[:app_name] || args[0]
20
+ user_id = options[:user_id] || args[1]
21
+
22
+ session_service = Legate::SessionService::InMemory.new
23
+ sessions = session_service.list_sessions(app_name: app_name, user_id: user_id)
24
+
25
+ if sessions.empty?
26
+ say "No sessions found#{app_name ? " for app '#{app_name}'" : ''}#{user_id ? " and user '#{user_id}'" : ''}.",
27
+ :yellow
28
+ return
29
+ end
30
+
31
+ say "Found #{sessions.length} session(s):", :bold
32
+ sessions.each do |session|
33
+ created_at = session.created_at.strftime('%Y-%m-%d %H:%M:%S')
34
+ say " ID: #{session.id}", :cyan
35
+ say " App: #{session.app_name}"
36
+ say " User: #{session.user_id}"
37
+ say " Created: #{created_at}"
38
+ say " Events: #{session.events.length}"
39
+ say ''
40
+ end
41
+ end
42
+
43
+ desc 'show SESSION_ID', 'Show details of a specific session including events'
44
+ def show(session_id)
45
+ session_service = Legate::SessionService::InMemory.new
46
+ session = session_service.get_session(session_id: session_id)
47
+
48
+ unless session
49
+ say "Session '#{session_id}' not found.", :red
50
+ return
51
+ end
52
+
53
+ created_at = session.created_at.strftime('%Y-%m-%d %H:%M:%S')
54
+ say 'Session Details:', :bold
55
+ say " ID: #{session.id}", :cyan
56
+ say " App: #{session.app_name}"
57
+ say " User: #{session.user_id}"
58
+ say " Created: #{created_at}"
59
+ say " Events: #{session.events.length}"
60
+
61
+ if session.events.empty?
62
+ say "\nNo events in this session.", :yellow
63
+ return
64
+ end
65
+
66
+ say "\nEvents:", :bold
67
+ session.events.each_with_index do |event, index|
68
+ say " #{index + 1}. [#{event.role}] #{event.content.inspect}"
69
+ end
70
+ end
71
+
72
+ desc 'delete SESSION_ID', 'Delete a specific session'
73
+ def delete(session_id)
74
+ session_service = Legate::SessionService::InMemory.new
75
+
76
+ # Check if session exists first
77
+ session = session_service.get_session(session_id: session_id)
78
+ unless session
79
+ say "Session '#{session_id}' not found.", :red
80
+ return
81
+ end
82
+
83
+ # Confirm deletion
84
+ if yes?("Are you sure you want to delete session '#{session_id}'? (y/n)")
85
+ if session_service.delete_session(session_id: session_id)
86
+ say "Session '#{session_id}' deleted successfully.", :green
87
+ else
88
+ say "Failed to delete session '#{session_id}'.", :red
89
+ end
90
+ else
91
+ say 'Deletion cancelled.', :yellow
92
+ end
93
+ end
94
+
95
+ desc 'execute AGENT_NAME TASK --session-id=SESSION_ID',
96
+ 'Execute a task using an agent with a specific session'
97
+ method_option :session_id, type: :string, desc: 'ID of an existing session to use'
98
+ method_option :user_id, type: :string, default: 'cli_user', desc: 'User ID for the session'
99
+ def execute(agent_name, task)
100
+ # First, check if the agent exists in the GlobalDefinitionRegistry
101
+ name_sym = agent_name.to_sym
102
+ agent_definition = Legate::GlobalDefinitionRegistry.find(name_sym)
103
+
104
+ unless agent_definition
105
+ say "Error: Agent definition '#{agent_name}' not found.", :red
106
+ exit(1)
107
+ end
108
+
109
+ # Use in-memory session service
110
+ session_service = Legate::SessionService::InMemory.new
111
+ session_id = options[:session_id]
112
+ legate_session = nil
113
+
114
+ if session_id
115
+ legate_session = session_service.get_session(session_id: session_id)
116
+ if legate_session
117
+ say "Using existing session: #{session_id}", :cyan
118
+ else
119
+ say "Warning: Session ID '#{session_id}' provided but not found. Starting a new session.", :yellow
120
+ session_id = nil # Force creation below
121
+ end
122
+ end
123
+
124
+ unless legate_session
125
+ # Create a new session
126
+ legate_session = session_service.create_session(app_name: agent_name, user_id: options[:user_id])
127
+ session_id = legate_session.id
128
+ say "Started new session: #{session_id}", :cyan
129
+ end
130
+
131
+ # Now execute the task using the agent_commands implementation
132
+ agent_commands = Legate::CLI::AgentCommands.new
133
+ agent_commands.invoke(:execute, [agent_name, task],
134
+ { session_id: session_id, session_service: session_service })
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,223 @@
1
+ # File: lib/legate/cli/skaffold_commands.rb
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor'
5
+ require_relative 'base_command'
6
+ require 'fileutils'
7
+
8
+ module Legate
9
+ module CLI
10
+ class SkaffoldCommands < BaseCommand
11
+ default_task :generate
12
+
13
+ desc 'generate [PROJECT_NAME]', 'Generate a new Legate project structure'
14
+ method_option :dir, type: :string, desc: 'Target directory (defaults to current or project_name)'
15
+
16
+ def generate(project_name = nil)
17
+ target_dir = options[:dir] || project_name || '.'
18
+ target_dir = File.expand_path(target_dir)
19
+
20
+ say "Skaffolding new Legate project in '#{target_dir}'...", :green
21
+
22
+ unless Dir.exist?(target_dir)
23
+ FileUtils.mkdir_p(target_dir)
24
+ say "Created directory: #{target_dir}", :cyan
25
+ end
26
+
27
+ # 1. Gemfile
28
+ create_file(File.join(target_dir, 'Gemfile'), <<~GEMFILE)
29
+ source 'https://rubygems.org'
30
+
31
+ gem 'legate'
32
+ gem 'dotenv' # For loading .env files
33
+ gem 'puma' # App server for the Web UI
34
+
35
+ group :test do
36
+ gem 'rspec'
37
+ end
38
+ GEMFILE
39
+
40
+ # 2. config.ru
41
+ create_file(File.join(target_dir, 'config.ru'), <<~CONFIGRU)
42
+ # frozen_string_literal: true
43
+
44
+ require 'rubygems'
45
+ require 'bundler/setup'
46
+
47
+ # Load environment variables early
48
+ require 'dotenv/load' if File.exist?('.env')
49
+
50
+ require 'legate'
51
+
52
+ # Configure Legate
53
+ Legate.configure do |config|
54
+ # Set your global configuration here
55
+ # config.default_model_name = 'gemini-3.5-flash'
56
+ #{' '}
57
+ # config.session_service = Legate::SessionService::InMemory.new
58
+ end
59
+
60
+ # Helper to recursively require files in specific directories
61
+ def recursive_require(base_dir, sub_dirs)
62
+ sub_dirs.each do |dir|
63
+ full_path = File.join(base_dir, dir)
64
+ next unless Dir.exist?(full_path)
65
+
66
+ Dir.glob(File.join(full_path, '**', '*.rb')).sort.each do |file|
67
+ # Skip tests/specs and tools inside agent directories if loading agents
68
+ next if file.include?('_spec.rb') || file.include?('_test.rb')
69
+ next if dir.include?('agents') && file.include?('/tools/')
70
+
71
+ begin
72
+ require file
73
+ rescue LoadError => e
74
+ puts "WARN: Failed to load \#{file}: \#{e.message}"
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # Load Agents
81
+ recursive_require(__dir__, ['lib/agents', 'agents/lib/agents', 'agents'])
82
+
83
+ # Load Tools
84
+ recursive_require(__dir__, ['lib/tools', 'agents/lib/tools', 'tools'])
85
+
86
+ # Check for Legate configuration for webhooks
87
+ if Legate.config.respond_to?(:webhooks) && Legate.config.webhooks.listener_enabled
88
+ require 'legate/web/webhook_listener'
89
+ # Mount the webhook listener if enabled
90
+ map Legate.config.webhooks.base_path do
91
+ run Legate::Web::WebhookListener.new
92
+ end
93
+ end
94
+
95
+ # Run the Legate Web UI
96
+ require 'legate/web/app'
97
+ run Legate::Web::App.new
98
+ CONFIGRU
99
+
100
+ # 3. Agents Directory & Sample Agent
101
+ agents_dir = File.join(target_dir, 'agents')
102
+ FileUtils.mkdir_p(agents_dir)
103
+ create_file(File.join(agents_dir, 'hello_world_agent.rb'), <<~RUBY)
104
+ require 'legate'
105
+
106
+ Legate::Agent.define do |a|
107
+ a.name :hello_world
108
+ a.description "A friendly agent that helps you verify your setup."
109
+ a.instruction "You are a helpful assistant. When asked to say hello, you should reply enthusiastically."
110
+ #{' '}
111
+ # Use built-in tools
112
+ a.use_tool :echo#{' '}
113
+ a.use_tool :sample_tool
114
+ #{' '}
115
+ # a.model_name 'gemini-1.5-pro'
116
+ end
117
+ RUBY
118
+
119
+ # A real, useful starter agent built on the SSRF-safe HTTP tools.
120
+ create_file(File.join(agents_dir, 'research_assistant_agent.rb'), <<~RUBY)
121
+ require 'legate'
122
+
123
+ # A research assistant that can look things up on the web and reason about
124
+ # what it finds. It uses Legate's built-in, SSRF-safe HTTP tools — no extra
125
+ # setup required beyond a GOOGLE_API_KEY for the LLM.
126
+ Legate::Agent.define do |a|
127
+ a.name :research_assistant
128
+ a.description "Researches a question by fetching and reading web pages, then summarizes the findings."
129
+ a.instruction <<~INSTRUCTION
130
+ You are a thorough research assistant.
131
+
132
+ When given a question or topic:
133
+ 1. Use read_webpage to fetch and read relevant pages the user provides
134
+ or that you can construct URLs for (e.g. documentation or APIs).
135
+ 2. Use http_request when you need a raw API response (JSON, status checks).
136
+ 3. Use current_time when the answer depends on today's date.
137
+
138
+ Always cite the URLs you read, distinguish facts from inference, and say
139
+ clearly when a source could not be reached or did not answer the question.
140
+ INSTRUCTION
141
+
142
+ a.use_tool :read_webpage
143
+ a.use_tool :http_request
144
+ a.use_tool :current_time
145
+
146
+ # a.model_name 'gemini-1.5-pro'
147
+ end
148
+ RUBY
149
+
150
+ # 4. Tools Directory
151
+ tools_dir = File.join(target_dir, 'tools')
152
+ FileUtils.mkdir_p(tools_dir)
153
+ create_file(File.join(tools_dir, '.keep'), '')
154
+ create_file(File.join(tools_dir, 'sample_tool.rb'), <<~RUBY)
155
+ require 'legate/tool'
156
+
157
+ class SampleTool < Legate::Tool
158
+ tool_description "Greets a user by name."
159
+ #{' '}
160
+ parameter :name,#{' '}
161
+ type: :string,#{' '}
162
+ description: "The name of the user to greet",#{' '}
163
+ required: true
164
+
165
+ private
166
+
167
+ def perform_execution(params, _context)
168
+ name = params[:name] || params['name']
169
+ { status: :success, result: "Hello, \#{name}! This is a custom tool." }
170
+ end
171
+ end
172
+
173
+ Legate::GlobalToolManager.register_tool(SampleTool)
174
+ RUBY
175
+
176
+ # 5. .env.example
177
+ create_file(File.join(target_dir, '.env.example'), <<~ENV)
178
+ # Copy this to .env and set your values
179
+ GOOGLE_API_KEY=your_api_key_here
180
+ LEGATE_LOG_LEVEL=INFO
181
+ ENV
182
+
183
+ # 6. bin/console (Optional helper)
184
+ bin_dir = File.join(target_dir, 'bin')
185
+ FileUtils.mkdir_p(bin_dir)
186
+ console_path = File.join(bin_dir, 'console')
187
+ create_file(console_path, <<~RUBY)
188
+ #!/usr/bin/env ruby
189
+ require 'bundler/setup'
190
+ require 'irb'
191
+ require 'dotenv/load'
192
+ require 'legate'
193
+
194
+ # Load agents/tools
195
+ Dir[File.join(__dir__, '..', 'agents', '*.rb')].each { |file| require file }
196
+ Dir[File.join(__dir__, '..', 'tools', '*.rb')].each { |file| require file }
197
+
198
+ puts "Legate Console loaded."
199
+ IRB.start
200
+ RUBY
201
+ FileUtils.chmod(0o755, console_path)
202
+
203
+ say "\nSkaffolding complete!", :green
204
+ say 'Next steps:', :yellow
205
+ say " 1. cd #{target_dir}", :cyan
206
+ say ' 2. cp .env.example .env (and configure it)', :cyan
207
+ say ' 3. bundle install', :cyan
208
+ say ' 4. bundle exec rackup (to start the Web UI)', :cyan
209
+ end
210
+
211
+ private
212
+
213
+ def create_file(path, content)
214
+ if File.exist?(path)
215
+ say "Skipping existing file: #{path}", :yellow
216
+ else
217
+ File.write(path, content)
218
+ say "Created file: #{path}", :green
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end