swarm_sdk 2.7.13 → 3.0.0.alpha1

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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
  4. data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
  5. data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
  6. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  7. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  8. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  9. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  10. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  11. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  12. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  13. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  14. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  15. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  16. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  17. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  18. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  19. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  20. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  24. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  25. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  26. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  27. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  28. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  29. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  30. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  31. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  32. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  33. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  34. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  35. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  36. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  37. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  38. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  39. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  40. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  41. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  42. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  43. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  45. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  46. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  47. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  48. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  49. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  50. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  51. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  52. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  53. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  54. data/lib/swarm_sdk/v3.rb +145 -0
  55. metadata +84 -148
  56. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  57. data/lib/swarm_sdk/agent/builder.rb +0 -680
  58. data/lib/swarm_sdk/agent/chat.rb +0 -1432
  59. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  60. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  61. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  62. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  63. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  64. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  65. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  66. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  67. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  68. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  69. data/lib/swarm_sdk/agent/context.rb +0 -115
  70. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  71. data/lib/swarm_sdk/agent/definition.rb +0 -581
  72. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  73. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  74. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  75. data/lib/swarm_sdk/agent_registry.rb +0 -146
  76. data/lib/swarm_sdk/builders/base_builder.rb +0 -553
  77. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  78. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  79. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  80. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  81. data/lib/swarm_sdk/config.rb +0 -367
  82. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  83. data/lib/swarm_sdk/configuration/translator.rb +0 -283
  84. data/lib/swarm_sdk/configuration.rb +0 -165
  85. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  86. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  87. data/lib/swarm_sdk/context_compactor.rb +0 -335
  88. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  89. data/lib/swarm_sdk/context_management/context.rb +0 -328
  90. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  91. data/lib/swarm_sdk/defaults.rb +0 -251
  92. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  93. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  94. data/lib/swarm_sdk/hooks/context.rb +0 -197
  95. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  96. data/lib/swarm_sdk/hooks/error.rb +0 -29
  97. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  98. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  99. data/lib/swarm_sdk/hooks/result.rb +0 -150
  100. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  101. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  102. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  103. data/lib/swarm_sdk/log_collector.rb +0 -227
  104. data/lib/swarm_sdk/log_stream.rb +0 -127
  105. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  106. data/lib/swarm_sdk/model_aliases.json +0 -8
  107. data/lib/swarm_sdk/models.json +0 -44002
  108. data/lib/swarm_sdk/models.rb +0 -161
  109. data/lib/swarm_sdk/node_context.rb +0 -245
  110. data/lib/swarm_sdk/observer/builder.rb +0 -81
  111. data/lib/swarm_sdk/observer/config.rb +0 -45
  112. data/lib/swarm_sdk/observer/manager.rb +0 -236
  113. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  114. data/lib/swarm_sdk/permissions/config.rb +0 -239
  115. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  116. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  117. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  118. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  119. data/lib/swarm_sdk/plugin.rb +0 -309
  120. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  121. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  122. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  123. data/lib/swarm_sdk/restore_result.rb +0 -65
  124. data/lib/swarm_sdk/result.rb +0 -212
  125. data/lib/swarm_sdk/snapshot.rb +0 -156
  126. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  127. data/lib/swarm_sdk/state_restorer.rb +0 -476
  128. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  129. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  130. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -195
  131. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  132. data/lib/swarm_sdk/swarm/executor.rb +0 -290
  133. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
  134. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  135. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
  136. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
  137. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  138. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  139. data/lib/swarm_sdk/swarm.rb +0 -843
  140. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  141. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  142. data/lib/swarm_sdk/tools/base.rb +0 -63
  143. data/lib/swarm_sdk/tools/bash.rb +0 -280
  144. data/lib/swarm_sdk/tools/clock.rb +0 -46
  145. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  146. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  147. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  148. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  149. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  150. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  151. data/lib/swarm_sdk/tools/edit.rb +0 -145
  152. data/lib/swarm_sdk/tools/glob.rb +0 -166
  153. data/lib/swarm_sdk/tools/grep.rb +0 -235
  154. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  155. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  156. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  157. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -100
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  182. data/lib/swarm_sdk/workflow.rb +0 -589
  183. data/lib/swarm_sdk.rb +0 -718
@@ -1,360 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Logging callbacks for swarm events
6
- #
7
- # Extracted from Swarm to reduce class size and eliminate repetitive callback patterns.
8
- # These callbacks emit structured log events to LogStream for monitoring and debugging.
9
- module LoggingCallbacks
10
- # Register default logging callbacks for all swarm events
11
- #
12
- # Sets up low-priority callbacks that emit structured events to LogStream.
13
- # These callbacks only fire when LogStream.emitter is set (logging enabled).
14
- def register_default_logging_callbacks
15
- register_swarm_lifecycle_callbacks
16
- register_agent_lifecycle_callbacks
17
- register_tool_execution_callbacks
18
- register_context_warning_callback
19
- end
20
-
21
- # Setup logging infrastructure for an execution
22
- #
23
- # @param logs [Array] Log collection array
24
- # @yield [entry] Block called for each log entry
25
- def setup_logging(logs)
26
- # Force fresh subscription array for this execution
27
- Fiber[:log_subscriptions] = []
28
-
29
- # Subscribe to collect logs and forward to user's block
30
- LogCollector.subscribe do |entry|
31
- logs << entry
32
- yield(entry) if block_given?
33
- end
34
-
35
- # Set LogStream to use LogCollector as emitter
36
- LogStream.emitter = LogCollector
37
- end
38
-
39
- # Emit agent_start events if agents were initialized before logging was set up
40
- #
41
- # When agents are initialized BEFORE logging (e.g., via restore()),
42
- # we need to retroactively set up logging callbacks and emit agent_start events.
43
- def emit_retroactive_agent_start_events
44
- return if !@agents_initialized || @agent_start_events_emitted
45
-
46
- # Setup logging callbacks for all agents (they were skipped during initialization)
47
- setup_logging_for_all_agents
48
-
49
- # Emit agent_start events now that logging is ready
50
- emit_agent_start_events
51
- @agent_start_events_emitted = true
52
- end
53
-
54
- # Setup logging callbacks for all initialized agents
55
- #
56
- # Called after restore() when logging is enabled. Sets up logging callbacks
57
- # for each agent so that subsequent events are captured.
58
- def setup_logging_for_all_agents
59
- # Setup for PRIMARY agents
60
- @agents.each_value do |chat|
61
- chat.setup_logging if chat.respond_to?(:setup_logging)
62
- end
63
-
64
- # Setup for DELEGATION instances
65
- @delegation_instances.each_value do |chat|
66
- chat.setup_logging if chat.respond_to?(:setup_logging)
67
- end
68
- end
69
-
70
- # Emit agent_start events for all initialized agents
71
- #
72
- # Called retroactively when agents were initialized before logging was enabled.
73
- # Emits agent_start events so log stream captures complete agent lifecycle.
74
- def emit_agent_start_events
75
- return unless LogStream.emitter
76
-
77
- # Emit for PRIMARY agents
78
- @agents.each do |agent_name, chat|
79
- emit_agent_start_for(agent_name, chat, is_delegation: false)
80
- end
81
-
82
- # Emit for DELEGATION instances
83
- @delegation_instances.each do |instance_name, chat|
84
- base_name = extract_base_name(instance_name)
85
- emit_agent_start_for(instance_name.to_sym, chat, is_delegation: true, base_name: base_name)
86
- end
87
-
88
- # Mark as emitted to prevent duplicate emissions
89
- @agent_start_events_emitted = true
90
- end
91
-
92
- # Emit a single agent_start event
93
- #
94
- # @param agent_name [Symbol] Agent name (or instance name for delegations)
95
- # @param chat [Agent::Chat] Agent chat instance
96
- # @param is_delegation [Boolean] Whether this is a delegation instance
97
- # @param base_name [String, nil] Base agent name for delegations
98
- def emit_agent_start_for(agent_name, chat, is_delegation:, base_name: nil)
99
- base_name ||= agent_name
100
- agent_def = @agent_definitions[base_name]
101
-
102
- # Build plugin storage info using base name
103
- plugin_storage_info = {}
104
- @plugin_storages.each do |plugin_name, agent_storages|
105
- next unless agent_storages.key?(base_name)
106
-
107
- plugin_storage_info[plugin_name] = {
108
- enabled: true,
109
- config: agent_def.respond_to?(plugin_name) ? extract_plugin_config_info(agent_def.public_send(plugin_name)) : nil,
110
- }
111
- end
112
-
113
- LogStream.emit(
114
- type: "agent_start",
115
- agent: agent_name,
116
- swarm_id: @swarm_id,
117
- parent_swarm_id: @parent_swarm_id,
118
- swarm_name: @name,
119
- model: agent_def.model,
120
- provider: agent_def.provider || "openai",
121
- directory: agent_def.directory,
122
- system_prompt: agent_def.system_prompt,
123
- tools: chat.tool_names,
124
- delegates_to: agent_def.delegates_to,
125
- plugin_storages: plugin_storage_info,
126
- is_delegation_instance: is_delegation,
127
- base_agent: (base_name if is_delegation),
128
- timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
129
- )
130
- end
131
-
132
- private
133
-
134
- # Register swarm lifecycle callbacks (swarm_start, swarm_stop)
135
- def register_swarm_lifecycle_callbacks
136
- add_default_callback(:swarm_start, priority: -100) do |context|
137
- emit_swarm_start_event(context)
138
- end
139
-
140
- add_default_callback(:swarm_stop, priority: -100) do |context|
141
- emit_swarm_stop_event(context)
142
- end
143
- end
144
-
145
- # Register agent lifecycle callbacks (user_prompt, agent_step, agent_stop)
146
- def register_agent_lifecycle_callbacks
147
- add_default_callback(:user_prompt, priority: -100) do |context|
148
- emit_user_prompt_event(context)
149
- end
150
-
151
- add_default_callback(:agent_step, priority: -100) do |context|
152
- emit_agent_step_event(context)
153
- end
154
-
155
- add_default_callback(:agent_stop, priority: -100) do |context|
156
- emit_agent_stop_event(context)
157
- end
158
- end
159
-
160
- # Register tool execution callbacks (pre_tool_use, post_tool_use)
161
- def register_tool_execution_callbacks
162
- add_default_callback(:pre_tool_use, priority: -100) do |context|
163
- emit_tool_call_event(context)
164
- end
165
-
166
- add_default_callback(:post_tool_use, priority: -100) do |context|
167
- emit_tool_result_event(context)
168
- end
169
- end
170
-
171
- # Register context warning callback
172
- def register_context_warning_callback
173
- add_default_callback(:context_warning, priority: -100) do |context|
174
- emit_context_warning_event(context)
175
- end
176
- end
177
-
178
- # Emit swarm_start event
179
- def emit_swarm_start_event(context)
180
- return unless LogStream.emitter
181
-
182
- LogStream.emit(
183
- type: "swarm_start",
184
- agent: context.metadata[:lead_agent],
185
- swarm_id: @swarm_id,
186
- parent_swarm_id: @parent_swarm_id,
187
- swarm_name: context.metadata[:swarm_name],
188
- lead_agent: context.metadata[:lead_agent],
189
- prompt: context.metadata[:prompt],
190
- timestamp: context.metadata[:timestamp],
191
- )
192
- end
193
-
194
- # Emit swarm_stop event
195
- def emit_swarm_stop_event(context)
196
- return unless LogStream.emitter
197
-
198
- LogStream.emit(
199
- type: "swarm_stop",
200
- swarm_id: @swarm_id,
201
- parent_swarm_id: @parent_swarm_id,
202
- swarm_name: context.metadata[:swarm_name],
203
- lead_agent: context.metadata[:lead_agent],
204
- last_agent: context.metadata[:last_agent],
205
- content: context.metadata[:content],
206
- success: context.metadata[:success],
207
- duration: context.metadata[:duration],
208
- total_cost: context.metadata[:total_cost],
209
- total_tokens: context.metadata[:total_tokens],
210
- agents_involved: context.metadata[:agents_involved],
211
- per_agent_usage: context.metadata[:per_agent_usage],
212
- timestamp: context.metadata[:timestamp],
213
- )
214
- end
215
-
216
- # Emit user_prompt event
217
- def emit_user_prompt_event(context)
218
- return unless LogStream.emitter
219
-
220
- LogStream.emit(
221
- type: "user_prompt",
222
- agent: context.agent_name,
223
- swarm_id: @swarm_id,
224
- parent_swarm_id: @parent_swarm_id,
225
- prompt: context.metadata[:prompt],
226
- model: context.metadata[:model] || "unknown",
227
- provider: context.metadata[:provider] || "unknown",
228
- message_count: context.metadata[:message_count] || 0,
229
- tools: context.metadata[:tools] || [],
230
- delegates_to: context.metadata[:delegates_to] || [],
231
- source: context.metadata[:source] || "user",
232
- metadata: context.metadata,
233
- )
234
- end
235
-
236
- # Emit agent_step event (intermediate response with tool calls)
237
- def emit_agent_step_event(context)
238
- return unless LogStream.emitter
239
-
240
- metadata_without_duplicates = context.metadata.except(
241
- :model,
242
- :content,
243
- :tool_calls,
244
- :finish_reason,
245
- :usage,
246
- :tool_executions,
247
- :citations,
248
- :search_results,
249
- )
250
-
251
- LogStream.emit(
252
- type: "agent_step",
253
- agent: context.agent_name,
254
- swarm_id: @swarm_id,
255
- parent_swarm_id: @parent_swarm_id,
256
- model: context.metadata[:model],
257
- content: context.metadata[:content],
258
- tool_calls: context.metadata[:tool_calls],
259
- finish_reason: context.metadata[:finish_reason],
260
- usage: context.metadata[:usage],
261
- citations: context.metadata[:citations],
262
- search_results: context.metadata[:search_results],
263
- tool_executions: context.metadata[:tool_executions],
264
- metadata: metadata_without_duplicates,
265
- )
266
- end
267
-
268
- # Emit agent_stop event (final response)
269
- def emit_agent_stop_event(context)
270
- return unless LogStream.emitter
271
-
272
- metadata_without_duplicates = context.metadata.except(
273
- :model,
274
- :content,
275
- :tool_calls,
276
- :finish_reason,
277
- :usage,
278
- :tool_executions,
279
- :citations,
280
- :search_results,
281
- )
282
-
283
- LogStream.emit(
284
- type: "agent_stop",
285
- agent: context.agent_name,
286
- swarm_id: @swarm_id,
287
- parent_swarm_id: @parent_swarm_id,
288
- model: context.metadata[:model],
289
- content: context.metadata[:content],
290
- tool_calls: context.metadata[:tool_calls],
291
- finish_reason: context.metadata[:finish_reason],
292
- usage: context.metadata[:usage],
293
- citations: context.metadata[:citations],
294
- search_results: context.metadata[:search_results],
295
- tool_executions: context.metadata[:tool_executions],
296
- metadata: metadata_without_duplicates,
297
- )
298
- end
299
-
300
- # Emit tool_call event (pre_tool_use)
301
- def emit_tool_call_event(context)
302
- return unless LogStream.emitter
303
-
304
- LogStream.emit(
305
- type: "tool_call",
306
- agent: context.agent_name,
307
- swarm_id: @swarm_id,
308
- parent_swarm_id: @parent_swarm_id,
309
- tool_call_id: context.tool_call.id,
310
- tool: context.tool_call.name,
311
- arguments: context.tool_call.parameters,
312
- metadata: context.metadata,
313
- )
314
- end
315
-
316
- # Emit tool_result event (post_tool_use)
317
- def emit_tool_result_event(context)
318
- return unless LogStream.emitter
319
-
320
- LogStream.emit(
321
- type: "tool_result",
322
- agent: context.agent_name,
323
- swarm_id: @swarm_id,
324
- parent_swarm_id: @parent_swarm_id,
325
- tool_call_id: context.tool_result.tool_call_id,
326
- tool: context.tool_result.tool_name,
327
- result: context.tool_result.content,
328
- metadata: context.metadata,
329
- )
330
- end
331
-
332
- # Emit context_limit_warning event
333
- def emit_context_warning_event(context)
334
- return unless LogStream.emitter
335
-
336
- LogStream.emit(
337
- type: "context_limit_warning",
338
- agent: context.agent_name,
339
- swarm_id: @swarm_id,
340
- parent_swarm_id: @parent_swarm_id,
341
- model: context.metadata[:model] || "unknown",
342
- threshold: "#{context.metadata[:threshold]}%",
343
- current_usage: "#{context.metadata[:percentage]}%",
344
- tokens_used: context.metadata[:tokens_used],
345
- tokens_remaining: context.metadata[:tokens_remaining],
346
- context_limit: context.metadata[:context_limit],
347
- metadata: context.metadata,
348
- )
349
- end
350
-
351
- # Extract base name from delegation instance name
352
- #
353
- # @param instance_name [String, Symbol] Instance name (e.g., "agent@1234")
354
- # @return [Symbol] Base agent name (e.g., :agent)
355
- def extract_base_name(instance_name)
356
- instance_name.to_s.split("@").first.to_sym
357
- end
358
- end
359
- end
360
- end
@@ -1,270 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Handles MCP (Model Context Protocol) server configuration and client management
6
- #
7
- # Responsibilities:
8
- # - Register MCP servers for agents
9
- # - Initialize MCP clients (stdio, SSE, streamable transports)
10
- # - Build transport-specific configurations
11
- # - Track clients for cleanup
12
- #
13
- # This encapsulates all MCP-related logic that was previously in Swarm.
14
- class McpConfigurator
15
- def initialize(swarm)
16
- @swarm = swarm
17
- @mcp_clients = swarm.mcp_clients
18
- end
19
-
20
- # Register MCP servers for an agent
21
- #
22
- # Connects to MCP servers and registers their tools with the agent's chat instance.
23
- # Supports stdio, SSE, and HTTP (streamable) transports.
24
- #
25
- # ## Boot Optimization (Plan 025)
26
- #
27
- # - If tools specified: Create stubs without tools/list RPC (fast boot, lazy schema)
28
- # - If tools omitted: Call tools/list to discover all tools (discovery mode)
29
- #
30
- # @param chat [AgentChat] The agent's chat instance
31
- # @param mcp_server_configs [Array<Hash>] MCP server configurations
32
- # @param agent_name [Symbol] Agent name for tracking clients
33
- #
34
- # @example Fast boot mode
35
- # mcp_server :codebase, type: :stdio, command: "mcp-server", tools: [:search, :list]
36
- # # Creates tool stubs instantly, no tools/list RPC
37
- #
38
- # @example Discovery mode
39
- # mcp_server :codebase, type: :stdio, command: "mcp-server"
40
- # # Calls tools/list to discover all available tools
41
- def register_mcp_servers(chat, mcp_server_configs, agent_name:)
42
- return if mcp_server_configs.nil? || mcp_server_configs.empty?
43
-
44
- # Ensure MCP logging is configured before creating clients
45
- Swarm.apply_mcp_logging_configuration
46
-
47
- mcp_server_configs.each do |server_config|
48
- tools_config = server_config[:tools]
49
- mode = tools_config.nil? ? :discovery : :optimized
50
-
51
- # Emit event before initialization
52
- emit_mcp_init_start(agent_name, server_config, mode)
53
-
54
- client = initialize_mcp_client(server_config)
55
-
56
- # Store client for cleanup
57
- @mcp_clients[agent_name] << client
58
-
59
- if tools_config.nil?
60
- # Discovery mode: Fetch all tools from server (calls tools/list)
61
- # client.tools returns RubyLLM::Tool instances (already wrapped by internal Coordinator)
62
- all_tools = client.tools
63
- tool_names = all_tools.map { |t| t.respond_to?(:name) ? t.name : t.to_s }
64
-
65
- all_tools.each do |tool|
66
- chat.tool_registry.register(
67
- tool,
68
- source: :mcp,
69
- metadata: { server_name: server_config[:name] },
70
- )
71
- end
72
-
73
- # Emit completion event for discovery mode
74
- emit_mcp_init_complete(agent_name, server_config, mode, all_tools.size, tool_names)
75
- RubyLLM.logger.debug("SwarmSDK: Discovered and registered #{all_tools.size} tools from MCP server '#{server_config[:name]}'")
76
- else
77
- # Optimized mode: Create tool stubs without tools/list RPC (Plan 025)
78
- # Use client directly (it has internal coordinator)
79
- tool_names = tools_config.map(&:to_s)
80
-
81
- tools_config.each do |tool_name|
82
- stub = Tools::McpToolStub.new(
83
- client: client,
84
- name: tool_name.to_s,
85
- server_name: server_config[:name],
86
- )
87
- chat.tool_registry.register(
88
- stub,
89
- source: :mcp,
90
- metadata: { server_name: server_config[:name] },
91
- )
92
- end
93
-
94
- # Emit completion event for optimized mode
95
- emit_mcp_init_complete(agent_name, server_config, mode, tools_config.size, tool_names)
96
- RubyLLM.logger.debug("SwarmSDK: Registered #{tools_config.size} tool stubs from MCP server '#{server_config[:name]}' (lazy schema)")
97
- end
98
- rescue StandardError => e
99
- RubyLLM.logger.error("SwarmSDK: Failed to initialize MCP server '#{server_config[:name]}' for agent #{agent_name}: #{e.class.name}: #{e.message}")
100
- RubyLLM.logger.error("SwarmSDK: Backtrace: #{e.backtrace.first(5).join("\n ")}")
101
- raise ConfigurationError, "Failed to initialize MCP server '#{server_config[:name]}': #{e.message}"
102
- end
103
- end
104
-
105
- # Build transport-specific configuration for MCP client
106
- #
107
- # This method is public for testing delegation from Swarm.
108
- #
109
- # @param transport_type [Symbol] Transport type (:stdio, :sse, :streamable)
110
- # @param config [Hash] MCP server configuration
111
- # @return [Hash] Transport-specific configuration
112
- def build_transport_config(transport_type, config)
113
- case transport_type
114
- when :stdio
115
- build_stdio_config(config)
116
- when :sse
117
- build_sse_config(config)
118
- when :streamable
119
- build_streamable_config(config)
120
- else
121
- raise ArgumentError, "Unsupported transport type: #{transport_type}"
122
- end
123
- end
124
-
125
- private
126
-
127
- # Initialize an MCP client from configuration
128
- #
129
- # @param config [Hash] MCP server configuration
130
- # @return [RubyLLM::MCP::Client] Initialized MCP client
131
- def initialize_mcp_client(config)
132
- # Convert timeout from seconds to milliseconds
133
- # Use explicit config[:timeout] if provided, otherwise use global default
134
- timeout_seconds = config[:timeout] || SwarmSDK.config.mcp_request_timeout
135
- timeout_ms = timeout_seconds * 1000
136
-
137
- # Determine transport type
138
- transport_type = determine_transport_type(config[:type])
139
-
140
- # Build transport-specific configuration
141
- client_config = build_transport_config(transport_type, config)
142
-
143
- # Create and start MCP client
144
- RubyLLM::MCP.client(
145
- name: config[:name],
146
- transport_type: transport_type,
147
- request_timeout: timeout_ms,
148
- config: client_config,
149
- )
150
- end
151
-
152
- # Determine transport type from configuration
153
- #
154
- # @param type [Symbol, String, nil] Transport type from config
155
- # @return [Symbol] Normalized transport type
156
- def determine_transport_type(type)
157
- case type&.to_sym
158
- when :stdio then :stdio
159
- when :sse then :sse
160
- when :http, :streamable then :streamable
161
- else
162
- raise ArgumentError, "Unknown MCP transport type: #{type}"
163
- end
164
- end
165
-
166
- # Build stdio transport configuration
167
- #
168
- # @param config [Hash] MCP server configuration
169
- # @return [Hash] Stdio configuration
170
- def build_stdio_config(config)
171
- {
172
- command: config[:command],
173
- args: config[:args] || [],
174
- env: Utils.stringify_keys(config[:env] || {}),
175
- }
176
- end
177
-
178
- # Build SSE transport configuration
179
- #
180
- # @param config [Hash] MCP server configuration
181
- # @return [Hash] SSE configuration
182
- def build_sse_config(config)
183
- sse_config = {
184
- url: config[:url],
185
- headers: config[:headers] || {},
186
- version: config[:version]&.to_sym || :http2,
187
- }
188
-
189
- # Add reconnection options for resilient SSE connections
190
- sse_config[:reconnection] = build_reconnection_options(config)
191
-
192
- sse_config
193
- end
194
-
195
- # Build streamable (HTTP) transport configuration
196
- #
197
- # @param config [Hash] MCP server configuration
198
- # @return [Hash] Streamable configuration
199
- def build_streamable_config(config)
200
- streamable_config = {
201
- url: config[:url],
202
- headers: config[:headers] || {},
203
- version: config[:version]&.to_sym || :http2,
204
- }
205
-
206
- # Only include rate_limit if present
207
- streamable_config[:rate_limit] = config[:rate_limit] if config[:rate_limit]
208
-
209
- # Add reconnection options for resilient streamable connections
210
- streamable_config[:reconnection] = build_reconnection_options(config)
211
-
212
- streamable_config
213
- end
214
-
215
- # Build reconnection options from config or defaults
216
- #
217
- # Provides exponential backoff reconnection for SSE/streamable transports.
218
- # Can be customized per-server or uses global defaults.
219
- #
220
- # @param config [Hash] MCP server configuration
221
- # @return [Hash] Reconnection options
222
- def build_reconnection_options(config)
223
- reconnection_config = config[:reconnection] || {}
224
-
225
- {
226
- max_retries: reconnection_config[:max_retries] || Defaults::McpReconnection::MAX_RETRIES,
227
- initial_reconnection_delay: reconnection_config[:initial_delay] || Defaults::McpReconnection::INITIAL_DELAY_MS,
228
- reconnection_delay_grow_factor: reconnection_config[:delay_grow_factor] || Defaults::McpReconnection::DELAY_GROW_FACTOR,
229
- max_reconnection_delay: reconnection_config[:max_delay] || Defaults::McpReconnection::MAX_DELAY_MS,
230
- }
231
- end
232
-
233
- # Emit MCP server initialization start event
234
- #
235
- # @param agent_name [Symbol] Agent name
236
- # @param server_config [Hash] MCP server configuration
237
- # @param mode [Symbol] Initialization mode (:discovery or :optimized)
238
- # @return [void]
239
- def emit_mcp_init_start(agent_name, server_config, mode)
240
- LogStream.emit(
241
- type: "mcp_server_init_start",
242
- agent: agent_name,
243
- server_name: server_config[:name],
244
- transport_type: server_config[:type],
245
- mode: mode,
246
- )
247
- end
248
-
249
- # Emit MCP server initialization complete event
250
- #
251
- # @param agent_name [Symbol] Agent name
252
- # @param server_config [Hash] MCP server configuration
253
- # @param mode [Symbol] Initialization mode (:discovery or :optimized)
254
- # @param tool_count [Integer] Number of tools registered
255
- # @param tool_names [Array<String>] Names of registered tools
256
- # @return [void]
257
- def emit_mcp_init_complete(agent_name, server_config, mode, tool_count, tool_names)
258
- LogStream.emit(
259
- type: "mcp_server_init_complete",
260
- agent: agent_name,
261
- server_name: server_config[:name],
262
- transport_type: server_config[:type],
263
- mode: mode,
264
- tool_count: tool_count,
265
- tools: tool_names,
266
- )
267
- end
268
- end
269
- end
270
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Builder for swarm registry in DSL
6
- #
7
- # Supports registering external swarms for composable swarms pattern.
8
- #
9
- # @example
10
- # swarms do
11
- # register "code_review", file: "./swarms/code_review.rb"
12
- # register "testing", file: "./swarms/testing.yml", keep_context: false
13
- # end
14
- #
15
- # @example Inline swarm definition
16
- # swarms do
17
- # register "tester" do
18
- # lead :tester
19
- # agent :tester do
20
- # model "gpt-4o-mini"
21
- # system "You test code"
22
- # end
23
- # end
24
- # end
25
- #
26
- class SwarmRegistryBuilder
27
- attr_reader :registrations
28
-
29
- def initialize
30
- @registrations = []
31
- end
32
-
33
- # Register a swarm from file, YAML string, or inline block
34
- #
35
- # @param name [String, Symbol] Registration name
36
- # @param file [String, nil] Path to swarm file (.rb or .yml)
37
- # @param yaml [String, nil] YAML content string
38
- # @param keep_context [Boolean] Whether to preserve conversation state (default: true)
39
- # @yield Optional block for inline swarm definition
40
- # @raise [ArgumentError] If neither file, yaml, nor block provided
41
- def register(name, file: nil, yaml: nil, keep_context: true, &block)
42
- # Validate that exactly one source is provided
43
- sources = [file, yaml, block].compact
44
- if sources.empty?
45
- raise ArgumentError, "register '#{name}' requires either file:, yaml:, or a block"
46
- elsif sources.size > 1
47
- raise ArgumentError, "register '#{name}' accepts only one of: file:, yaml:, or block (got #{sources.size})"
48
- end
49
-
50
- # Determine source type and store
51
- source = if file
52
- { type: :file, value: file }
53
- elsif yaml
54
- { type: :yaml, value: yaml }
55
- else
56
- { type: :block, value: block }
57
- end
58
-
59
- @registrations << {
60
- name: name.to_s,
61
- source: source,
62
- keep_context: keep_context,
63
- }
64
- end
65
- end
66
- end
67
- end