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,290 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Handles swarm execution orchestration
6
- #
7
- # Extracted from Swarm#execute to reduce complexity and eliminate code duplication.
8
- # The core execution loop, error handling, and cleanup logic are unified here.
9
- class Executor
10
- def initialize(swarm)
11
- @swarm = swarm
12
- end
13
-
14
- # Execute the swarm with a prompt
15
- #
16
- # @param prompt [String] User prompt
17
- # @param wait [Boolean] Block until completion (true) or return task (false)
18
- # @param logs [Array] Log collection array
19
- # @param has_logging [Boolean] Whether logging is enabled
20
- # @param original_fiber_storage [Hash] Original Fiber storage values to restore
21
- # @return [Async::Task] The execution task
22
- def run(prompt, wait:, logs:, has_logging:, original_fiber_storage:)
23
- @original_fiber_storage = original_fiber_storage
24
- if wait
25
- run_blocking(prompt, logs: logs, has_logging: has_logging)
26
- else
27
- run_async(prompt, logs: logs, has_logging: has_logging)
28
- end
29
- end
30
-
31
- private
32
-
33
- # Blocking execution using Sync
34
- def run_blocking(prompt, logs:, has_logging:)
35
- result = nil
36
- Sync do |task|
37
- start_time = Time.now
38
-
39
- result = if @swarm.execution_timeout
40
- execute_with_execution_timeout(task, prompt, logs, has_logging, start_time)
41
- else
42
- execute_in_task(prompt, logs: logs, has_logging: has_logging) do |lead, current_prompt|
43
- # Execute directly - no child task needed
44
- # This keeps execution in same fiber context for better control
45
- lead.ask(current_prompt)
46
- end
47
- end
48
- ensure
49
- # Always wait for observer tasks, even if main execution raises
50
- # This is INSIDE Sync block, so async tasks can still complete
51
- @swarm.wait_for_observers
52
- end
53
-
54
- result
55
- ensure
56
- # Restore original fiber storage (preserves parent context for nested swarms)
57
- restore_fiber_storage
58
- end
59
-
60
- # Non-blocking execution using parent async task
61
- def run_async(prompt, logs:, has_logging:)
62
- parent = Async::Task.current
63
- raise ConfigurationError, "wait: false requires an async context. Use Sync { swarm.execute(..., wait: false) }" unless parent
64
-
65
- # NOTE: The block receives |task| as the spawned Async::Task when arity > 0
66
- parent.async(finished: false) do |task|
67
- start_time = Time.now
68
-
69
- if @swarm.execution_timeout
70
- execute_with_execution_timeout(task, prompt, logs, has_logging, start_time)
71
- else
72
- execute_in_task(prompt, logs: logs, has_logging: has_logging) do |lead, current_prompt|
73
- # Execute directly - no child task needed
74
- lead.ask(current_prompt)
75
- end
76
- end
77
- end
78
- end
79
-
80
- # Core execution logic (unified, no duplication)
81
- #
82
- # @param prompt [String] Initial prompt
83
- # @param logs [Array] Log collection
84
- # @param has_logging [Boolean] Whether logging is enabled
85
- # @yield [lead, current_prompt] Block to execute LLM call
86
- # @return [Result] Execution result
87
- def execute_in_task(prompt, logs:, has_logging:, &block)
88
- start_time = Time.now
89
- result = nil
90
- swarm_stop_triggered = false
91
- current_prompt = prompt
92
-
93
- begin
94
- # Notify plugins that swarm is starting
95
- PluginRegistry.emit_event(:on_swarm_started, swarm: @swarm)
96
-
97
- result = execution_loop(current_prompt, logs, start_time, &block)
98
- swarm_stop_triggered = true
99
- rescue ConfigurationError, AgentNotFoundError, ExecutionTimeoutError, TurnTimeoutError
100
- # Re-raise configuration errors and timeouts - these should not be caught here
101
- # Timeouts are handled by execute_with_execution_timeout wrapper
102
- raise
103
- rescue TypeError => e
104
- result = handle_type_error(e, logs, start_time)
105
- rescue StandardError => e
106
- result = handle_standard_error(e, logs, start_time)
107
- ensure
108
- # Notify plugins that swarm is stopping (called even on error)
109
- PluginRegistry.emit_event(:on_swarm_stopped, swarm: @swarm)
110
-
111
- cleanup_after_execution(result, start_time, logs, swarm_stop_triggered, has_logging)
112
- end
113
-
114
- result
115
- end
116
-
117
- # Main execution loop with reprompting support
118
- def execution_loop(initial_prompt, logs, start_time)
119
- current_prompt = initial_prompt
120
-
121
- loop do
122
- lead = @swarm.agents[@swarm.lead_agent]
123
- response = yield(lead, current_prompt)
124
-
125
- # Check if swarm was finished by a hook (finish_swarm)
126
- if response.is_a?(Hash) && response[:__finish_swarm__]
127
- result = build_result(response[:message], logs, start_time)
128
- @swarm.trigger_swarm_stop(result)
129
- return result
130
- end
131
-
132
- result = build_result(response.content, logs, start_time)
133
-
134
- # Trigger swarm_stop hooks (for reprompt check and event emission)
135
- hook_result = @swarm.trigger_swarm_stop(result)
136
-
137
- # Check if hook requests reprompting
138
- if hook_result&.reprompt?
139
- current_prompt = hook_result.value
140
- # Continue loop with new prompt
141
- else
142
- # Exit loop - execution complete
143
- return result
144
- end
145
- end
146
- end
147
-
148
- # Build a Result object
149
- def build_result(content, logs, start_time)
150
- Result.new(
151
- content: content,
152
- agent: @swarm.lead_agent.to_s,
153
- logs: logs,
154
- duration: Time.now - start_time,
155
- )
156
- end
157
-
158
- # Handle TypeError (e.g., "String does not have #dig method")
159
- def handle_type_error(error, logs, start_time)
160
- if error.message.include?("does not have #dig method")
161
- agent_definition = @swarm.agent_definitions[@swarm.lead_agent]
162
- error_msg = if agent_definition.base_url
163
- "LLM API request failed: The proxy/server at '#{agent_definition.base_url}' returned an invalid response. " \
164
- "This usually means the proxy is unreachable, requires authentication, or returned an error in non-JSON format. " \
165
- "Original error: #{error.message}"
166
- else
167
- "LLM API request failed with unexpected response format. Original error: #{error.message}"
168
- end
169
-
170
- Result.new(
171
- content: nil,
172
- agent: @swarm.lead_agent.to_s,
173
- error: LLMError.new(error_msg),
174
- logs: logs,
175
- duration: Time.now - start_time,
176
- )
177
- else
178
- Result.new(
179
- content: nil,
180
- agent: @swarm.lead_agent.to_s,
181
- error: error,
182
- logs: logs,
183
- duration: Time.now - start_time,
184
- )
185
- end
186
- end
187
-
188
- # Handle StandardError
189
- def handle_standard_error(error, logs, start_time)
190
- Result.new(
191
- content: nil,
192
- agent: @swarm.lead_agent&.to_s || "unknown",
193
- error: error,
194
- logs: logs,
195
- duration: Time.now - start_time,
196
- )
197
- end
198
-
199
- # Cleanup after execution (ensure block logic)
200
- def cleanup_after_execution(result, start_time, logs, swarm_stop_triggered, has_logging)
201
- # Trigger swarm_stop if not already triggered (handles error cases)
202
- unless swarm_stop_triggered
203
- @swarm.trigger_swarm_stop_final(result, start_time, logs)
204
- end
205
-
206
- # Cleanup MCP clients after execution
207
- @swarm.cleanup
208
-
209
- # Cleanup observer subscriptions (matches MCP cleanup pattern)
210
- @swarm.cleanup_observers
211
-
212
- # Restore original Fiber storage (preserves parent context for nested swarms)
213
- restore_fiber_storage
214
-
215
- # Reset logging state for next execution if we set it up
216
- reset_logging if has_logging
217
- end
218
-
219
- # Restore Fiber-local storage to original values (preserves parent context)
220
- def restore_fiber_storage
221
- Fiber[:execution_id] = @original_fiber_storage[:execution_id]
222
- Fiber[:swarm_id] = @original_fiber_storage[:swarm_id]
223
- Fiber[:parent_swarm_id] = @original_fiber_storage[:parent_swarm_id]
224
- end
225
-
226
- # Reset logging state
227
- def reset_logging
228
- LogCollector.reset!
229
- LogStream.reset!
230
- end
231
-
232
- # Execute with execution timeout wrapper
233
- def execute_with_execution_timeout(task, prompt, logs, has_logging, start_time)
234
- # Use Async::Task.current to get the actual current task context
235
- current_task = Async::Task.current || task
236
-
237
- # Use barrier to track ALL child tasks spawned during execution
238
- # This includes RubyLLM's async tool execution (when max_concurrent_tools is set)
239
- barrier = Async::Barrier.new
240
-
241
- begin
242
- current_task.with_timeout(
243
- @swarm.execution_timeout,
244
- ExecutionTimeoutError,
245
- "Swarm execution timed out after #{@swarm.execution_timeout}s",
246
- ) do
247
- # Execute inside barrier to track child tasks (tool executions)
248
- barrier.async do
249
- execute_in_task(prompt, logs: logs, has_logging: has_logging) do |lead, current_prompt|
250
- lead.ask(current_prompt)
251
- end
252
- end.wait
253
- end
254
- rescue ExecutionTimeoutError => e
255
- # Stop ALL child tasks (interrupts ongoing tool executions and delegations)
256
- barrier.stop
257
-
258
- emit_execution_timeout_event(@swarm.execution_timeout)
259
- build_timeout_result(e, logs, Time.now - start_time)
260
- ensure
261
- # Cleanup barrier if not already stopped
262
- barrier.stop unless barrier.empty?
263
- end
264
- end
265
-
266
- # Emit execution timeout event
267
- def emit_execution_timeout_event(limit)
268
- LogStream.emit(
269
- type: "execution_timeout",
270
- swarm_id: @swarm.swarm_id,
271
- parent_swarm_id: @swarm.parent_swarm_id,
272
- limit: limit,
273
- message: "Swarm execution timed out after #{limit}s",
274
- )
275
- end
276
-
277
- # Build timeout result
278
- def build_timeout_result(error, logs, duration)
279
- Result.new(
280
- content: nil,
281
- agent: @swarm.lead_agent&.to_s || "unknown",
282
- error: error,
283
- logs: logs,
284
- duration: duration,
285
- metadata: { timeout: true },
286
- )
287
- end
288
- end
289
- end
290
- end
@@ -1,151 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Hook triggering methods for swarm lifecycle events
6
- #
7
- # Extracted from Swarm to reduce class size and centralize hook execution logic.
8
- # These methods build contexts and execute hooks via the hook registry.
9
- module HookTriggers
10
- # Add a default callback for an event
11
- #
12
- # @param event [Symbol] Event type (:pre_tool_use, :post_tool_use, etc.)
13
- # @param matcher [Hash, nil] Optional matcher to filter events
14
- # @param priority [Integer] Callback priority (higher = later)
15
- # @param block [Proc] Hook implementation
16
- # @return [self]
17
- def add_default_callback(event, matcher: nil, priority: 0, &block)
18
- @hook_registry.add_default(event, matcher: matcher, priority: priority, &block)
19
- self
20
- end
21
-
22
- # Trigger swarm_stop hooks and check for reprompt
23
- #
24
- # @param result [Result] The execution result
25
- # @return [Hooks::Result, nil] Hook result (reprompt action if applicable)
26
- def trigger_swarm_stop(result)
27
- context = build_swarm_stop_context(result)
28
- executor = Hooks::Executor.new(@hook_registry, logger: RubyLLM.logger)
29
- executor.execute_safe(event: :swarm_stop, context: context, callbacks: [])
30
- rescue StandardError => e
31
- LogStream.emit_error(e, source: "hook_triggers", context: "swarm_stop", agent: @lead_agent)
32
- RubyLLM.logger.debug("SwarmSDK: Error in swarm_stop hook: #{e.message}")
33
- nil
34
- end
35
-
36
- # Trigger swarm_stop for final event emission (called in ensure block)
37
- #
38
- # @param result [Result, nil] Execution result
39
- # @param start_time [Time] Execution start time
40
- # @param logs [Array] Collected logs
41
- # @return [void]
42
- def trigger_swarm_stop_final(result, start_time, logs)
43
- result ||= Result.new(
44
- content: nil,
45
- agent: @lead_agent&.to_s || "unknown",
46
- logs: logs,
47
- duration: Time.now - start_time,
48
- error: StandardError.new("Unknown error"),
49
- )
50
-
51
- context = build_swarm_stop_context(result)
52
- executor = Hooks::Executor.new(@hook_registry, logger: RubyLLM.logger)
53
- executor.execute_safe(event: :swarm_stop, context: context, callbacks: [])
54
- rescue StandardError => e
55
- LogStream.emit_error(e, source: "hook_triggers", context: "swarm_stop_final", agent: @lead_agent)
56
- RubyLLM.logger.debug("SwarmSDK: Error in swarm_stop final emission: #{e.message}")
57
- end
58
-
59
- private
60
-
61
- # Build swarm_stop context (DRY - used by both trigger methods)
62
- #
63
- # @param result [Result] Execution result
64
- # @return [Hooks::Context] Hook context for swarm_stop event
65
- def build_swarm_stop_context(result)
66
- Hooks::Context.new(
67
- event: :swarm_stop,
68
- agent_name: @lead_agent.to_s,
69
- swarm: self,
70
- metadata: {
71
- swarm_name: @name,
72
- lead_agent: @lead_agent,
73
- last_agent: result.agent,
74
- content: result.content,
75
- success: result.success?,
76
- duration: result.duration,
77
- total_cost: result.total_cost,
78
- total_tokens: result.total_tokens,
79
- agents_involved: result.agents_involved,
80
- per_agent_usage: result.per_agent_usage,
81
- result: result,
82
- timestamp: Time.now.utc.iso8601,
83
- },
84
- )
85
- end
86
-
87
- # Trigger swarm_start hooks when swarm execution begins
88
- #
89
- # @param prompt [String] The user's task prompt
90
- # @return [Hooks::Result, nil] Result with stdout to append (if exit 0) or nil
91
- # @raise [Hooks::Error] If hook halts execution
92
- def trigger_swarm_start(prompt)
93
- context = Hooks::Context.new(
94
- event: :swarm_start,
95
- agent_name: @lead_agent.to_s,
96
- swarm: self,
97
- metadata: {
98
- swarm_name: @name,
99
- lead_agent: @lead_agent,
100
- prompt: prompt,
101
- timestamp: Time.now.utc.iso8601,
102
- },
103
- )
104
-
105
- executor = Hooks::Executor.new(@hook_registry, logger: RubyLLM.logger)
106
- result = executor.execute_safe(event: :swarm_start, context: context, callbacks: [])
107
-
108
- # Halt execution if hook requests it
109
- raise Hooks::Error, "Swarm start halted by hook: #{result.value}" if result.halt?
110
-
111
- # Return result so caller can check for replace (stdout injection)
112
- result
113
- rescue StandardError => e
114
- LogStream.emit_error(e, source: "hook_triggers", context: "swarm_start", agent: @lead_agent)
115
- RubyLLM.logger.debug("SwarmSDK: Error in swarm_start hook: #{e.message}")
116
- raise
117
- end
118
-
119
- # Trigger first_message hooks when first user message is sent
120
- #
121
- # @param prompt [String] The first user message
122
- # @return [void]
123
- # @raise [Hooks::Error] If hook halts execution
124
- def trigger_first_message(prompt)
125
- return if @hook_registry.get_defaults(:first_message).empty?
126
-
127
- context = Hooks::Context.new(
128
- event: :first_message,
129
- agent_name: @lead_agent.to_s,
130
- swarm: self,
131
- metadata: {
132
- swarm_name: @name,
133
- lead_agent: @lead_agent,
134
- prompt: prompt,
135
- timestamp: Time.now.utc.iso8601,
136
- },
137
- )
138
-
139
- executor = Hooks::Executor.new(@hook_registry, logger: RubyLLM.logger)
140
- result = executor.execute_safe(event: :first_message, context: context, callbacks: [])
141
-
142
- # Halt execution if hook requests it
143
- raise Hooks::Error, "First message halted by hook: #{result.value}" if result.halt?
144
- rescue StandardError => e
145
- LogStream.emit_error(e, source: "hook_triggers", context: "first_message", agent: @lead_agent)
146
- RubyLLM.logger.debug("SwarmSDK: Error in first_message hook: #{e.message}")
147
- raise
148
- end
149
- end
150
- end
151
- end