swarm_sdk 2.7.14 → 3.0.0.alpha2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -1,361 +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
- finish_reason: context.metadata[:finish_reason] || "finished",
208
- duration: context.metadata[:duration],
209
- total_cost: context.metadata[:total_cost],
210
- total_tokens: context.metadata[:total_tokens],
211
- agents_involved: context.metadata[:agents_involved],
212
- per_agent_usage: context.metadata[:per_agent_usage],
213
- timestamp: context.metadata[:timestamp],
214
- )
215
- end
216
-
217
- # Emit user_prompt event
218
- def emit_user_prompt_event(context)
219
- return unless LogStream.emitter
220
-
221
- LogStream.emit(
222
- type: "user_prompt",
223
- agent: context.agent_name,
224
- swarm_id: @swarm_id,
225
- parent_swarm_id: @parent_swarm_id,
226
- prompt: context.metadata[:prompt],
227
- model: context.metadata[:model] || "unknown",
228
- provider: context.metadata[:provider] || "unknown",
229
- message_count: context.metadata[:message_count] || 0,
230
- tools: context.metadata[:tools] || [],
231
- delegates_to: context.metadata[:delegates_to] || [],
232
- source: context.metadata[:source] || "user",
233
- metadata: context.metadata,
234
- )
235
- end
236
-
237
- # Emit agent_step event (intermediate response with tool calls)
238
- def emit_agent_step_event(context)
239
- return unless LogStream.emitter
240
-
241
- metadata_without_duplicates = context.metadata.except(
242
- :model,
243
- :content,
244
- :tool_calls,
245
- :finish_reason,
246
- :usage,
247
- :tool_executions,
248
- :citations,
249
- :search_results,
250
- )
251
-
252
- LogStream.emit(
253
- type: "agent_step",
254
- agent: context.agent_name,
255
- swarm_id: @swarm_id,
256
- parent_swarm_id: @parent_swarm_id,
257
- model: context.metadata[:model],
258
- content: context.metadata[:content],
259
- tool_calls: context.metadata[:tool_calls],
260
- finish_reason: context.metadata[:finish_reason],
261
- usage: context.metadata[:usage],
262
- citations: context.metadata[:citations],
263
- search_results: context.metadata[:search_results],
264
- tool_executions: context.metadata[:tool_executions],
265
- metadata: metadata_without_duplicates,
266
- )
267
- end
268
-
269
- # Emit agent_stop event (final response)
270
- def emit_agent_stop_event(context)
271
- return unless LogStream.emitter
272
-
273
- metadata_without_duplicates = context.metadata.except(
274
- :model,
275
- :content,
276
- :tool_calls,
277
- :finish_reason,
278
- :usage,
279
- :tool_executions,
280
- :citations,
281
- :search_results,
282
- )
283
-
284
- LogStream.emit(
285
- type: "agent_stop",
286
- agent: context.agent_name,
287
- swarm_id: @swarm_id,
288
- parent_swarm_id: @parent_swarm_id,
289
- model: context.metadata[:model],
290
- content: context.metadata[:content],
291
- tool_calls: context.metadata[:tool_calls],
292
- finish_reason: context.metadata[:finish_reason],
293
- usage: context.metadata[:usage],
294
- citations: context.metadata[:citations],
295
- search_results: context.metadata[:search_results],
296
- tool_executions: context.metadata[:tool_executions],
297
- metadata: metadata_without_duplicates,
298
- )
299
- end
300
-
301
- # Emit tool_call event (pre_tool_use)
302
- def emit_tool_call_event(context)
303
- return unless LogStream.emitter
304
-
305
- LogStream.emit(
306
- type: "tool_call",
307
- agent: context.agent_name,
308
- swarm_id: @swarm_id,
309
- parent_swarm_id: @parent_swarm_id,
310
- tool_call_id: context.tool_call.id,
311
- tool: context.tool_call.name,
312
- arguments: context.tool_call.parameters,
313
- metadata: context.metadata,
314
- )
315
- end
316
-
317
- # Emit tool_result event (post_tool_use)
318
- def emit_tool_result_event(context)
319
- return unless LogStream.emitter
320
-
321
- LogStream.emit(
322
- type: "tool_result",
323
- agent: context.agent_name,
324
- swarm_id: @swarm_id,
325
- parent_swarm_id: @parent_swarm_id,
326
- tool_call_id: context.tool_result.tool_call_id,
327
- tool: context.tool_result.tool_name,
328
- result: context.tool_result.content,
329
- metadata: context.metadata,
330
- )
331
- end
332
-
333
- # Emit context_limit_warning event
334
- def emit_context_warning_event(context)
335
- return unless LogStream.emitter
336
-
337
- LogStream.emit(
338
- type: "context_limit_warning",
339
- agent: context.agent_name,
340
- swarm_id: @swarm_id,
341
- parent_swarm_id: @parent_swarm_id,
342
- model: context.metadata[:model] || "unknown",
343
- threshold: "#{context.metadata[:threshold]}%",
344
- current_usage: "#{context.metadata[:percentage]}%",
345
- tokens_used: context.metadata[:tokens_used],
346
- tokens_remaining: context.metadata[:tokens_remaining],
347
- context_limit: context.metadata[:context_limit],
348
- metadata: context.metadata,
349
- )
350
- end
351
-
352
- # Extract base name from delegation instance name
353
- #
354
- # @param instance_name [String, Symbol] Instance name (e.g., "agent@1234")
355
- # @return [Symbol] Base agent name (e.g., :agent)
356
- def extract_base_name(instance_name)
357
- instance_name.to_s.split("@").first.to_sym
358
- end
359
- end
360
- end
361
- end
@@ -1,290 +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
- # Configure SSL before creating the client so HTTPX connections use the right options
133
- configure_mcp_ssl(config)
134
-
135
- # Convert timeout from seconds to milliseconds
136
- # Use explicit config[:timeout] if provided, otherwise use global default
137
- timeout_seconds = config[:timeout] || SwarmSDK.config.mcp_request_timeout
138
- timeout_ms = timeout_seconds * 1000
139
-
140
- # Determine transport type
141
- transport_type = determine_transport_type(config[:type])
142
-
143
- # Build transport-specific configuration
144
- client_config = build_transport_config(transport_type, config)
145
-
146
- # Create and start MCP client
147
- RubyLLM::MCP.client(
148
- name: config[:name],
149
- transport_type: transport_type,
150
- request_timeout: timeout_ms,
151
- config: client_config,
152
- )
153
- end
154
-
155
- # Determine transport type from configuration
156
- #
157
- # @param type [Symbol, String, nil] Transport type from config
158
- # @return [Symbol] Normalized transport type
159
- def determine_transport_type(type)
160
- case type&.to_sym
161
- when :stdio then :stdio
162
- when :sse then :sse
163
- when :http, :streamable then :streamable
164
- else
165
- raise ArgumentError, "Unknown MCP transport type: #{type}"
166
- end
167
- end
168
-
169
- # Build stdio transport configuration
170
- #
171
- # @param config [Hash] MCP server configuration
172
- # @return [Hash] Stdio configuration
173
- def build_stdio_config(config)
174
- {
175
- command: config[:command],
176
- args: config[:args] || [],
177
- env: Utils.stringify_keys(config[:env] || {}),
178
- }
179
- end
180
-
181
- # Build SSE transport configuration
182
- #
183
- # @param config [Hash] MCP server configuration
184
- # @return [Hash] SSE configuration
185
- def build_sse_config(config)
186
- sse_config = {
187
- url: config[:url],
188
- headers: config[:headers] || {},
189
- version: config[:version]&.to_sym || :http2,
190
- }
191
-
192
- # Add reconnection options for resilient SSE connections
193
- sse_config[:reconnection] = build_reconnection_options(config)
194
-
195
- sse_config
196
- end
197
-
198
- # Build streamable (HTTP) transport configuration
199
- #
200
- # @param config [Hash] MCP server configuration
201
- # @return [Hash] Streamable configuration
202
- def build_streamable_config(config)
203
- streamable_config = {
204
- url: config[:url],
205
- headers: config[:headers] || {},
206
- version: config[:version]&.to_sym || :http2,
207
- }
208
-
209
- # Only include rate_limit if present
210
- streamable_config[:rate_limit] = config[:rate_limit] if config[:rate_limit]
211
-
212
- # Add reconnection options for resilient streamable connections
213
- streamable_config[:reconnection] = build_reconnection_options(config)
214
-
215
- streamable_config
216
- end
217
-
218
- # Build reconnection options from config or defaults
219
- #
220
- # Provides exponential backoff reconnection for SSE/streamable transports.
221
- # Can be customized per-server or uses global defaults.
222
- #
223
- # @param config [Hash] MCP server configuration
224
- # @return [Hash] Reconnection options
225
- def build_reconnection_options(config)
226
- reconnection_config = config[:reconnection] || {}
227
-
228
- {
229
- max_retries: reconnection_config[:max_retries] || Defaults::McpReconnection::MAX_RETRIES,
230
- initial_reconnection_delay: reconnection_config[:initial_delay] || Defaults::McpReconnection::INITIAL_DELAY_MS,
231
- reconnection_delay_grow_factor: reconnection_config[:delay_grow_factor] || Defaults::McpReconnection::DELAY_GROW_FACTOR,
232
- max_reconnection_delay: reconnection_config[:max_delay] || Defaults::McpReconnection::MAX_DELAY_MS,
233
- }
234
- end
235
-
236
- # Configure SSL options for MCP HTTPX connections
237
- #
238
- # Sets McpSslPatch.ssl_options based on per-server ssl_verify config
239
- # or global SwarmSDK.config.mcp_ssl_verify. Resets the thread-local
240
- # connection cache so build_connection picks up the new options.
241
- #
242
- # @param config [Hash] MCP server configuration
243
- # @option config [Boolean] :ssl_verify Override global SSL verify setting
244
- # @return [void]
245
- def configure_mcp_ssl(config)
246
- ssl_verify = config.fetch(:ssl_verify, SwarmSDK.config.mcp_ssl_verify)
247
- verify_mode = ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
248
-
249
- McpSslPatch.ssl_options = { verify_mode: verify_mode }
250
- McpSslPatch.reset_connection!
251
- end
252
-
253
- # Emit MCP server initialization start event
254
- #
255
- # @param agent_name [Symbol] Agent name
256
- # @param server_config [Hash] MCP server configuration
257
- # @param mode [Symbol] Initialization mode (:discovery or :optimized)
258
- # @return [void]
259
- def emit_mcp_init_start(agent_name, server_config, mode)
260
- LogStream.emit(
261
- type: "mcp_server_init_start",
262
- agent: agent_name,
263
- server_name: server_config[:name],
264
- transport_type: server_config[:type],
265
- mode: mode,
266
- )
267
- end
268
-
269
- # Emit MCP server initialization complete event
270
- #
271
- # @param agent_name [Symbol] Agent name
272
- # @param server_config [Hash] MCP server configuration
273
- # @param mode [Symbol] Initialization mode (:discovery or :optimized)
274
- # @param tool_count [Integer] Number of tools registered
275
- # @param tool_names [Array<String>] Names of registered tools
276
- # @return [void]
277
- def emit_mcp_init_complete(agent_name, server_config, mode, tool_count, tool_names)
278
- LogStream.emit(
279
- type: "mcp_server_init_complete",
280
- agent: agent_name,
281
- server_name: server_config[:name],
282
- transport_type: server_config[:type],
283
- mode: mode,
284
- tool_count: tool_count,
285
- tools: tool_names,
286
- )
287
- end
288
- end
289
- end
290
- end