claude_swarm 1.0.0 → 1.0.2

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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +1 -1
  3. data/.claude/hooks/lint-code-files.rb +65 -0
  4. data/.rubocop.yml +22 -2
  5. data/CHANGELOG.md +21 -1
  6. data/CLAUDE.md +1 -1
  7. data/CONTRIBUTING.md +69 -0
  8. data/README.md +27 -2
  9. data/Rakefile +71 -3
  10. data/analyze_coverage.rb +94 -0
  11. data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
  12. data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
  13. data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
  14. data/docs/v2/README.md +308 -0
  15. data/docs/v2/guides/claude-code-agents.md +262 -0
  16. data/docs/v2/guides/complete-tutorial.md +3088 -0
  17. data/docs/v2/guides/getting-started.md +1456 -0
  18. data/docs/v2/guides/memory-adapters.md +998 -0
  19. data/docs/v2/guides/plugins.md +816 -0
  20. data/docs/v2/guides/quick-start-cli.md +1745 -0
  21. data/docs/v2/guides/rails-integration.md +1902 -0
  22. data/docs/v2/guides/swarm-memory.md +599 -0
  23. data/docs/v2/reference/cli.md +729 -0
  24. data/docs/v2/reference/ruby-dsl.md +2154 -0
  25. data/docs/v2/reference/yaml.md +1835 -0
  26. data/docs-team-swarm.yml +2222 -0
  27. data/examples/learning-assistant/assistant.md +7 -0
  28. data/examples/learning-assistant/example-memories/concept-example.md +90 -0
  29. data/examples/learning-assistant/example-memories/experience-example.md +66 -0
  30. data/examples/learning-assistant/example-memories/fact-example.md +76 -0
  31. data/examples/learning-assistant/example-memories/memory-index.md +78 -0
  32. data/examples/learning-assistant/example-memories/skill-example.md +168 -0
  33. data/examples/learning-assistant/learning_assistant.rb +34 -0
  34. data/examples/learning-assistant/learning_assistant.yml +20 -0
  35. data/examples/v2/dsl/01_basic.rb +44 -0
  36. data/examples/v2/dsl/02_core_parameters.rb +59 -0
  37. data/examples/v2/dsl/03_capabilities.rb +71 -0
  38. data/examples/v2/dsl/04_llm_parameters.rb +56 -0
  39. data/examples/v2/dsl/05_advanced_flags.rb +73 -0
  40. data/examples/v2/dsl/06_permissions.rb +80 -0
  41. data/examples/v2/dsl/07_mcp_server.rb +62 -0
  42. data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
  43. data/examples/v2/dsl/09_agent_hooks.rb +67 -0
  44. data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
  45. data/examples/v2/dsl/11_delegation.rb +60 -0
  46. data/examples/v2/dsl/12_complete_integration.rb +137 -0
  47. data/examples/v2/file_tools_swarm.yml +102 -0
  48. data/examples/v2/hooks/01_basic_hooks.rb +133 -0
  49. data/examples/v2/hooks/02_usage_tracking.rb +201 -0
  50. data/examples/v2/hooks/03_production_monitoring.rb +429 -0
  51. data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
  52. data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
  53. data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
  54. data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
  55. data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
  56. data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
  57. data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
  58. data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
  59. data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
  60. data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
  61. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
  62. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
  63. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
  64. data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
  65. data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
  66. data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
  67. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
  68. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
  69. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
  70. data/examples/v2/hooks/swarm_summary.sh +44 -0
  71. data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
  72. data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
  73. data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
  74. data/examples/v2/hooks/validate_bash.rb +59 -0
  75. data/examples/v2/multi_directory_permissions.yml +221 -0
  76. data/examples/v2/node_context_demo.rb +127 -0
  77. data/examples/v2/node_workflow.rb +173 -0
  78. data/examples/v2/path_resolution_demo.rb +216 -0
  79. data/examples/v2/simple-swarm-v2.rb +90 -0
  80. data/examples/v2/simple-swarm-v2.yml +62 -0
  81. data/examples/v2/swarm.yml +71 -0
  82. data/examples/v2/swarm_with_hooks.yml +61 -0
  83. data/examples/v2/swarm_with_hooks_simple.yml +25 -0
  84. data/examples/v2/think_tool_demo.rb +62 -0
  85. data/exe/swarm +6 -0
  86. data/lib/claude_swarm/claude_mcp_server.rb +0 -6
  87. data/lib/claude_swarm/cli.rb +10 -3
  88. data/lib/claude_swarm/commands/ps.rb +19 -20
  89. data/lib/claude_swarm/commands/show.rb +1 -1
  90. data/lib/claude_swarm/configuration.rb +10 -12
  91. data/lib/claude_swarm/mcp_generator.rb +10 -1
  92. data/lib/claude_swarm/orchestrator.rb +73 -49
  93. data/lib/claude_swarm/system_utils.rb +37 -11
  94. data/lib/claude_swarm/version.rb +1 -1
  95. data/lib/claude_swarm/worktree_manager.rb +1 -0
  96. data/lib/claude_swarm/yaml_loader.rb +22 -0
  97. data/lib/claude_swarm.rb +7 -3
  98. data/lib/swarm_cli/cli.rb +201 -0
  99. data/lib/swarm_cli/command_registry.rb +61 -0
  100. data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
  101. data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
  102. data/lib/swarm_cli/commands/migrate.rb +55 -0
  103. data/lib/swarm_cli/commands/run.rb +173 -0
  104. data/lib/swarm_cli/config_loader.rb +97 -0
  105. data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
  106. data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
  107. data/lib/swarm_cli/interactive_repl.rb +918 -0
  108. data/lib/swarm_cli/mcp_serve_options.rb +44 -0
  109. data/lib/swarm_cli/mcp_tools_options.rb +59 -0
  110. data/lib/swarm_cli/migrate_options.rb +54 -0
  111. data/lib/swarm_cli/migrator.rb +132 -0
  112. data/lib/swarm_cli/options.rb +151 -0
  113. data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
  114. data/lib/swarm_cli/ui/components/content_block.rb +120 -0
  115. data/lib/swarm_cli/ui/components/divider.rb +57 -0
  116. data/lib/swarm_cli/ui/components/panel.rb +62 -0
  117. data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
  118. data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
  119. data/lib/swarm_cli/ui/formatters/number.rb +58 -0
  120. data/lib/swarm_cli/ui/formatters/text.rb +77 -0
  121. data/lib/swarm_cli/ui/formatters/time.rb +73 -0
  122. data/lib/swarm_cli/ui/icons.rb +59 -0
  123. data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
  124. data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
  125. data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
  126. data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
  127. data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
  128. data/lib/swarm_cli/version.rb +5 -0
  129. data/lib/swarm_cli.rb +44 -0
  130. data/lib/swarm_memory/adapters/base.rb +141 -0
  131. data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
  132. data/lib/swarm_memory/chat_extension.rb +34 -0
  133. data/lib/swarm_memory/cli/commands.rb +306 -0
  134. data/lib/swarm_memory/core/entry.rb +37 -0
  135. data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
  136. data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
  137. data/lib/swarm_memory/core/path_normalizer.rb +75 -0
  138. data/lib/swarm_memory/core/semantic_index.rb +244 -0
  139. data/lib/swarm_memory/core/storage.rb +288 -0
  140. data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
  141. data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
  142. data/lib/swarm_memory/dsl/memory_config.rb +113 -0
  143. data/lib/swarm_memory/embeddings/embedder.rb +36 -0
  144. data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
  145. data/lib/swarm_memory/errors.rb +21 -0
  146. data/lib/swarm_memory/integration/cli_registration.rb +30 -0
  147. data/lib/swarm_memory/integration/configuration.rb +43 -0
  148. data/lib/swarm_memory/integration/registration.rb +31 -0
  149. data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
  150. data/lib/swarm_memory/optimization/analyzer.rb +244 -0
  151. data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
  152. data/lib/swarm_memory/prompts/memory.md.erb +109 -0
  153. data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
  154. data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
  155. data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
  156. data/lib/swarm_memory/search/semantic_search.rb +112 -0
  157. data/lib/swarm_memory/search/text_search.rb +42 -0
  158. data/lib/swarm_memory/search/text_similarity.rb +80 -0
  159. data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
  160. data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
  161. data/lib/swarm_memory/tools/load_skill.rb +313 -0
  162. data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
  163. data/lib/swarm_memory/tools/memory_delete.rb +99 -0
  164. data/lib/swarm_memory/tools/memory_edit.rb +185 -0
  165. data/lib/swarm_memory/tools/memory_glob.rb +160 -0
  166. data/lib/swarm_memory/tools/memory_grep.rb +247 -0
  167. data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
  168. data/lib/swarm_memory/tools/memory_read.rb +123 -0
  169. data/lib/swarm_memory/tools/memory_write.rb +231 -0
  170. data/lib/swarm_memory/utils.rb +50 -0
  171. data/lib/swarm_memory/version.rb +5 -0
  172. data/lib/swarm_memory.rb +166 -0
  173. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
  174. data/lib/swarm_sdk/agent/builder.rb +461 -0
  175. data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
  176. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  177. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
  178. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
  179. data/lib/swarm_sdk/agent/chat.rb +1159 -0
  180. data/lib/swarm_sdk/agent/context.rb +112 -0
  181. data/lib/swarm_sdk/agent/context_manager.rb +309 -0
  182. data/lib/swarm_sdk/agent/definition.rb +556 -0
  183. data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
  184. data/lib/swarm_sdk/configuration.rb +296 -0
  185. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  186. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  187. data/lib/swarm_sdk/context_compactor.rb +340 -0
  188. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  189. data/lib/swarm_sdk/hooks/context.rb +197 -0
  190. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  191. data/lib/swarm_sdk/hooks/error.rb +29 -0
  192. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  193. data/lib/swarm_sdk/hooks/registry.rb +147 -0
  194. data/lib/swarm_sdk/hooks/result.rb +150 -0
  195. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  196. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  197. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  198. data/lib/swarm_sdk/log_collector.rb +51 -0
  199. data/lib/swarm_sdk/log_stream.rb +69 -0
  200. data/lib/swarm_sdk/markdown_parser.rb +75 -0
  201. data/lib/swarm_sdk/model_aliases.json +5 -0
  202. data/lib/swarm_sdk/models.json +1 -0
  203. data/lib/swarm_sdk/models.rb +120 -0
  204. data/lib/swarm_sdk/node/agent_config.rb +49 -0
  205. data/lib/swarm_sdk/node/builder.rb +439 -0
  206. data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
  207. data/lib/swarm_sdk/node_context.rb +170 -0
  208. data/lib/swarm_sdk/node_orchestrator.rb +384 -0
  209. data/lib/swarm_sdk/permissions/config.rb +239 -0
  210. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  211. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  212. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  213. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  214. data/lib/swarm_sdk/plugin.rb +147 -0
  215. data/lib/swarm_sdk/plugin_registry.rb +101 -0
  216. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
  217. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  218. data/lib/swarm_sdk/result.rb +97 -0
  219. data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
  220. data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
  221. data/lib/swarm_sdk/swarm/builder.rb +586 -0
  222. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  223. data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
  224. data/lib/swarm_sdk/swarm.rb +982 -0
  225. data/lib/swarm_sdk/tools/bash.rb +274 -0
  226. data/lib/swarm_sdk/tools/clock.rb +44 -0
  227. data/lib/swarm_sdk/tools/delegate.rb +164 -0
  228. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  229. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  230. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
  231. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  232. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  233. data/lib/swarm_sdk/tools/edit.rb +150 -0
  234. data/lib/swarm_sdk/tools/glob.rb +158 -0
  235. data/lib/swarm_sdk/tools/grep.rb +228 -0
  236. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  237. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  238. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  239. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  240. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  241. data/lib/swarm_sdk/tools/read.rb +251 -0
  242. data/lib/swarm_sdk/tools/registry.rb +93 -0
  243. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
  244. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
  245. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
  246. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  247. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
  248. data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
  249. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  250. data/lib/swarm_sdk/tools/think.rb +95 -0
  251. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  252. data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
  253. data/lib/swarm_sdk/tools/write.rb +117 -0
  254. data/lib/swarm_sdk/utils.rb +50 -0
  255. data/lib/swarm_sdk/version.rb +5 -0
  256. data/lib/swarm_sdk.rb +157 -0
  257. data/llm.v2.txt +13407 -0
  258. data/rubocop/cop/security/no_reflection_methods.rb +47 -0
  259. data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
  260. data/swarm_cli.gemspec +57 -0
  261. data/swarm_memory.gemspec +28 -0
  262. data/swarm_sdk.gemspec +41 -0
  263. data/team.yml +1 -1
  264. data/team_full.yml +1875 -0
  265. data/{team_v2.yml → team_sdk.yml} +121 -52
  266. metadata +247 -4
  267. data/EXAMPLES.md +0 -164
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ class Swarm
5
+ # Handles the complex 5-pass agent initialization process
6
+ #
7
+ # Responsibilities:
8
+ # - Create all agent chat instances (pass 1)
9
+ # - Register delegation tools (pass 2)
10
+ # - Setup agent contexts (pass 3)
11
+ # - Configure hook system (pass 4)
12
+ # - Apply YAML hooks if present (pass 5)
13
+ #
14
+ # This encapsulates the complex initialization logic that was previously
15
+ # embedded in Swarm#initialize_agents.
16
+ class AgentInitializer
17
+ # rubocop:disable Metrics/ParameterLists
18
+ def initialize(swarm, agent_definitions, global_semaphore, hook_registry, scratchpad_storage, plugin_storages, config_for_hooks: nil)
19
+ # rubocop:enable Metrics/ParameterLists
20
+ @swarm = swarm
21
+ @agent_definitions = agent_definitions
22
+ @global_semaphore = global_semaphore
23
+ @hook_registry = hook_registry
24
+ @scratchpad_storage = scratchpad_storage
25
+ @plugin_storages = plugin_storages
26
+ @config_for_hooks = config_for_hooks
27
+ @agents = {}
28
+ @agent_contexts = {}
29
+ end
30
+
31
+ # Initialize all agents with their chat instances and tools
32
+ #
33
+ # This implements a 5-pass algorithm:
34
+ # 1. Create all Agent::Chat instances
35
+ # 2. Register delegation tools (agents can call each other)
36
+ # 3. Setup agent contexts for tracking
37
+ # 4. Configure hook system
38
+ # 5. Apply YAML hooks (if loaded from YAML)
39
+ #
40
+ # @return [Hash] agents hash { agent_name => Agent::Chat }
41
+ def initialize_all
42
+ pass_1_create_agents
43
+ pass_2_register_delegation_tools
44
+ pass_3_setup_contexts
45
+ pass_4_configure_hooks
46
+ pass_5_apply_yaml_hooks
47
+
48
+ @agents
49
+ end
50
+
51
+ # Provide access to agent contexts for Swarm
52
+ attr_reader :agent_contexts
53
+
54
+ # Create a tool that delegates work to another agent
55
+ #
56
+ # This method is public for testing delegation from Swarm.
57
+ #
58
+ # @param name [String] Delegate agent name
59
+ # @param description [String] Delegate agent description
60
+ # @param delegate_chat [Agent::Chat] The delegate's chat instance
61
+ # @param agent_name [Symbol] Name of the delegating agent
62
+ # @param delegating_chat [Agent::Chat, nil] The chat instance of the agent doing the delegating
63
+ # @return [Tools::Delegate] Delegation tool
64
+ def create_delegation_tool(name:, description:, delegate_chat:, agent_name:, delegating_chat: nil)
65
+ Tools::Delegate.new(
66
+ delegate_name: name,
67
+ delegate_description: description,
68
+ delegate_chat: delegate_chat,
69
+ agent_name: agent_name,
70
+ swarm: @swarm,
71
+ hook_registry: @hook_registry,
72
+ delegating_chat: delegating_chat,
73
+ )
74
+ end
75
+
76
+ private
77
+
78
+ # Pass 1: Create all agent chat instances
79
+ #
80
+ # This creates the Agent::Chat instances but doesn't wire them together yet.
81
+ # Each agent gets its own chat instance with configured tools.
82
+ def pass_1_create_agents
83
+ # Create plugin storages for agents
84
+ create_plugin_storages
85
+
86
+ tool_configurator = ToolConfigurator.new(@swarm, @scratchpad_storage, @plugin_storages)
87
+
88
+ @agent_definitions.each do |name, agent_definition|
89
+ chat = create_agent_chat(name, agent_definition, tool_configurator)
90
+ @agents[name] = chat
91
+
92
+ # Notify plugins that agent was initialized
93
+ notify_plugins_agent_initialized(name, chat, agent_definition, tool_configurator)
94
+ end
95
+ end
96
+
97
+ # Pass 2: Register agent delegation tools
98
+ #
99
+ # Now that all agents exist, we can create delegation tools
100
+ # that allow agents to call each other.
101
+ def pass_2_register_delegation_tools
102
+ @agent_definitions.each do |name, agent_definition|
103
+ register_delegation_tools(@agents[name], agent_definition.delegates_to, agent_name: name)
104
+ end
105
+ end
106
+
107
+ # Pass 3: Setup agent contexts
108
+ #
109
+ # Create Agent::Context for each agent to track delegations and metadata.
110
+ # This is needed regardless of whether logging is enabled.
111
+ def pass_3_setup_contexts
112
+ @agents.each do |agent_name, chat|
113
+ agent_definition = @agent_definitions[agent_name]
114
+ delegate_tool_names = agent_definition.delegates_to.map do |delegate_name|
115
+ "DelegateTaskTo#{delegate_name.to_s.capitalize}"
116
+ end
117
+
118
+ # Create agent context
119
+ context = Agent::Context.new(
120
+ name: agent_name,
121
+ delegation_tools: delegate_tool_names,
122
+ metadata: {},
123
+ )
124
+ @agent_contexts[agent_name] = context
125
+
126
+ # Always set agent context (needed for delegation tracking)
127
+ chat.setup_context(context) if chat.respond_to?(:setup_context)
128
+
129
+ # Configure logging callbacks if logging is enabled
130
+ next unless LogStream.emitter
131
+
132
+ chat.setup_logging if chat.respond_to?(:setup_logging)
133
+
134
+ # Emit validation warnings for this agent
135
+ emit_validation_warnings(agent_name, agent_definition)
136
+ end
137
+ end
138
+
139
+ # Emit validation warnings as log events
140
+ #
141
+ # This validates the agent definition and emits any warnings as log events
142
+ # through LogStream (so formatters can handle them).
143
+ #
144
+ # @param agent_name [Symbol] Agent name
145
+ # @param agent_definition [Agent::Definition] Agent definition to validate
146
+ # @return [void]
147
+ def emit_validation_warnings(agent_name, agent_definition)
148
+ warnings = agent_definition.validate
149
+
150
+ warnings.each do |warning|
151
+ case warning[:type]
152
+ when :model_not_found
153
+ LogStream.emit(
154
+ type: "model_lookup_warning",
155
+ agent: agent_name,
156
+ model: warning[:model],
157
+ error_message: warning[:error_message],
158
+ suggestions: warning[:suggestions],
159
+ timestamp: Time.now.utc.iso8601,
160
+ )
161
+ end
162
+ end
163
+ end
164
+
165
+ # Pass 4: Configure hook system
166
+ #
167
+ # Setup the callback system for each agent, integrating with RubyLLM callbacks.
168
+ def pass_4_configure_hooks
169
+ @agents.each do |agent_name, chat|
170
+ agent_definition = @agent_definitions[agent_name]
171
+
172
+ # Configure callback system (integrates with RubyLLM callbacks)
173
+ chat.setup_hooks(
174
+ registry: @hook_registry,
175
+ agent_definition: agent_definition,
176
+ swarm: @swarm,
177
+ ) if chat.respond_to?(:setup_hooks)
178
+ end
179
+ end
180
+
181
+ # Pass 5: Apply YAML hooks
182
+ #
183
+ # If the swarm was loaded from YAML with agent-specific hooks,
184
+ # apply them now via HooksAdapter.
185
+ def pass_5_apply_yaml_hooks
186
+ return unless @config_for_hooks
187
+
188
+ @agents.each do |agent_name, chat|
189
+ agent_def = @config_for_hooks.agents[agent_name]
190
+ next unless agent_def&.hooks
191
+
192
+ # Apply agent-specific hooks via Hooks::Adapter
193
+ Hooks::Adapter.apply_agent_hooks(chat, agent_name, agent_def.hooks, @swarm.name)
194
+ end
195
+ end
196
+
197
+ # Create Agent::Chat instance with rate limiting
198
+ #
199
+ # @param agent_name [Symbol] Agent name
200
+ # @param agent_definition [Agent::Definition] Agent definition object
201
+ # @param tool_configurator [ToolConfigurator] Tool configuration helper
202
+ # @return [Agent::Chat] Configured agent chat instance
203
+ def create_agent_chat(agent_name, agent_definition, tool_configurator)
204
+ chat = Agent::Chat.new(
205
+ definition: agent_definition.to_h,
206
+ agent_name: agent_name,
207
+ global_semaphore: @global_semaphore,
208
+ )
209
+
210
+ # Set agent name on provider for logging (if provider supports it)
211
+ chat.provider.agent_name = agent_name if chat.provider.respond_to?(:agent_name=)
212
+
213
+ # Register tools using ToolConfigurator
214
+ tool_configurator.register_all_tools(
215
+ chat: chat,
216
+ agent_name: agent_name,
217
+ agent_definition: agent_definition,
218
+ )
219
+
220
+ # Register MCP servers using McpConfigurator
221
+ if agent_definition.mcp_servers.any?
222
+ mcp_configurator = McpConfigurator.new(@swarm)
223
+ mcp_configurator.register_mcp_servers(chat, agent_definition.mcp_servers, agent_name: agent_name)
224
+ end
225
+
226
+ chat
227
+ end
228
+
229
+ # Register agent delegation tools
230
+ #
231
+ # Creates delegation tools that allow one agent to call another.
232
+ #
233
+ # @param chat [Agent::Chat] The chat instance
234
+ # @param delegate_names [Array<Symbol>] Names of agents to delegate to
235
+ # @param agent_name [Symbol] Name of the agent doing the delegating
236
+ def register_delegation_tools(chat, delegate_names, agent_name:)
237
+ return if delegate_names.empty?
238
+
239
+ delegate_names.each do |delegate_name|
240
+ delegate_name = delegate_name.to_sym
241
+
242
+ unless @agents.key?(delegate_name)
243
+ raise ConfigurationError, "Agent delegates to unknown agent '#{delegate_name}'"
244
+ end
245
+
246
+ # Create a tool that delegates to the specified agent
247
+ delegate_agent = @agents[delegate_name]
248
+ delegate_definition = @agent_definitions[delegate_name]
249
+
250
+ tool = create_delegation_tool(
251
+ name: delegate_name.to_s,
252
+ description: delegate_definition.description,
253
+ delegate_chat: delegate_agent,
254
+ agent_name: agent_name,
255
+ delegating_chat: chat,
256
+ )
257
+
258
+ chat.with_tool(tool)
259
+ end
260
+ end
261
+
262
+ # Create plugin storages for all agents
263
+ #
264
+ # Iterates through all registered plugins and asks each to create
265
+ # storage for agents that need it.
266
+ #
267
+ # @return [void]
268
+ def create_plugin_storages
269
+ PluginRegistry.all.each do |plugin|
270
+ @agent_definitions.each do |agent_name, agent_definition|
271
+ # Check if this plugin needs storage for this agent
272
+ next unless plugin.storage_enabled?(agent_definition)
273
+
274
+ # Get plugin config for this agent
275
+ config = get_plugin_config(agent_definition, plugin.name)
276
+ next unless config
277
+
278
+ # Parse config through plugin
279
+ parsed_config = plugin.parse_config(config)
280
+
281
+ # Create plugin storage
282
+ storage = plugin.create_storage(agent_name: agent_name, config: parsed_config)
283
+
284
+ # Store in plugin_storages: { plugin_name => { agent_name => storage } }
285
+ @plugin_storages[plugin.name] ||= {}
286
+ @plugin_storages[plugin.name][agent_name] = storage
287
+ end
288
+ end
289
+ end
290
+
291
+ # Get plugin-specific config from agent definition
292
+ #
293
+ # Plugins can store their config in agent definition under their plugin name.
294
+ # E.g., memory plugin looks for `agent_definition.memory`
295
+ #
296
+ # @param agent_definition [Agent::Definition] Agent definition
297
+ # @param plugin_name [Symbol] Plugin name
298
+ # @return [Object, nil] Plugin config or nil
299
+ def get_plugin_config(agent_definition, plugin_name)
300
+ # Try to call method named after plugin (e.g., .memory for :memory plugin)
301
+ if agent_definition.respond_to?(plugin_name)
302
+ agent_definition.public_send(plugin_name)
303
+ end
304
+ end
305
+
306
+ # Notify all plugins that an agent was initialized
307
+ #
308
+ # Plugins can register additional tools, mark tools immutable, etc.
309
+ #
310
+ # @param agent_name [Symbol] Agent name
311
+ # @param chat [Agent::Chat] Chat instance
312
+ # @param agent_definition [Agent::Definition] Agent definition
313
+ # @param tool_configurator [ToolConfigurator] Tool configurator
314
+ # @return [void]
315
+ def notify_plugins_agent_initialized(agent_name, chat, agent_definition, tool_configurator)
316
+ PluginRegistry.all.each do |plugin|
317
+ # Get plugin storage for this agent (if any)
318
+ plugin_storages = @plugin_storages[plugin.name] || {}
319
+ storage = plugin_storages[agent_name]
320
+
321
+ # Build context for plugin
322
+ context = {
323
+ storage: storage,
324
+ agent_definition: agent_definition,
325
+ tool_configurator: tool_configurator,
326
+ }
327
+
328
+ # Notify plugin
329
+ plugin.on_agent_initialized(agent_name: agent_name, agent: chat, context: context)
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ class Swarm
5
+ # AllAgentsBuilder for configuring settings that apply to all agents
6
+ #
7
+ # Settings configured here are applied to ALL agents, but can be overridden
8
+ # at the agent level. This is useful for shared configuration like:
9
+ # - Common provider/base_url (all agents use same proxy)
10
+ # - Shared timeout settings
11
+ # - Global permissions
12
+ #
13
+ # @example
14
+ # all_agents do
15
+ # provider :openai
16
+ # base_url "http://proxy.com/v1"
17
+ # timeout 180
18
+ # tools :Read, :Write
19
+ # coding_agent false
20
+ # end
21
+ class AllAgentsBuilder
22
+ attr_reader :hooks, :permissions_config, :tools_list
23
+
24
+ def initialize
25
+ @tools_list = []
26
+ @hooks = []
27
+ @permissions_config = {}
28
+ @model = nil
29
+ @provider = nil
30
+ @base_url = nil
31
+ @api_version = nil
32
+ @timeout = nil
33
+ @parameters = nil
34
+ @headers = nil
35
+ @coding_agent = nil
36
+ end
37
+
38
+ # Set model for all agents
39
+ def model(model_name)
40
+ @model = model_name
41
+ end
42
+
43
+ # Set provider for all agents
44
+ def provider(provider_name)
45
+ @provider = provider_name
46
+ end
47
+
48
+ # Set base URL for all agents
49
+ def base_url(url)
50
+ @base_url = url
51
+ end
52
+
53
+ # Set API version for all agents
54
+ def api_version(version)
55
+ @api_version = version
56
+ end
57
+
58
+ # Set timeout for all agents
59
+ def timeout(seconds)
60
+ @timeout = seconds
61
+ end
62
+
63
+ # Set parameters for all agents
64
+ def parameters(params)
65
+ @parameters = params
66
+ end
67
+
68
+ # Set headers for all agents
69
+ def headers(header_hash)
70
+ @headers = header_hash
71
+ end
72
+
73
+ # Set coding_agent flag for all agents
74
+ def coding_agent(enabled)
75
+ @coding_agent = enabled
76
+ end
77
+
78
+ # Add tools that all agents will have
79
+ def tools(*tool_names)
80
+ @tools_list.concat(tool_names)
81
+ end
82
+
83
+ # Add hook for all agents (agent-level events only)
84
+ #
85
+ # @example
86
+ # hook :pre_tool_use, matcher: "Write" do |ctx|
87
+ # # Applies to all agents
88
+ # end
89
+ def hook(event, matcher: nil, command: nil, timeout: nil, &block)
90
+ # Validate agent-level events
91
+ agent_events = [
92
+ :pre_tool_use,
93
+ :post_tool_use,
94
+ :user_prompt,
95
+ :agent_stop,
96
+ :first_message,
97
+ :pre_delegation,
98
+ :post_delegation,
99
+ :context_warning,
100
+ ]
101
+
102
+ unless agent_events.include?(event)
103
+ raise ArgumentError, "Invalid all_agents hook: #{event}. Swarm-level events (:swarm_start, :swarm_stop) cannot be used in all_agents block."
104
+ end
105
+
106
+ @hooks << { event: event, matcher: matcher, command: command, timeout: timeout, block: block }
107
+ end
108
+
109
+ # Configure permissions for all agents
110
+ #
111
+ # @example
112
+ # permissions do
113
+ # Write.allow_paths "tmp/**/*"
114
+ # Write.deny_paths "tmp/secrets/**"
115
+ # Bash.allow_commands "^git status$"
116
+ # end
117
+ def permissions(&block)
118
+ @permissions_config = PermissionsBuilder.build(&block)
119
+ end
120
+
121
+ # Convert to hash for merging with agent configs
122
+ #
123
+ # @return [Hash] Configuration hash
124
+ def to_h
125
+ {
126
+ model: @model,
127
+ provider: @provider,
128
+ base_url: @base_url,
129
+ api_version: @api_version,
130
+ timeout: @timeout,
131
+ parameters: @parameters,
132
+ headers: @headers,
133
+ coding_agent: @coding_agent,
134
+ tools: @tools_list,
135
+ permissions: @permissions_config,
136
+ }.compact
137
+ end
138
+ end
139
+ end
140
+ end