swarm_sdk 2.7.12 → 2.7.14
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.
- checksums.yaml +4 -4
- data/lib/swarm_sdk/agent/builder.rb +25 -0
- data/lib/swarm_sdk/agent/chat.rb +59 -46
- data/lib/swarm_sdk/agent/definition.rb +8 -1
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +1 -0
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +15 -3
- data/lib/swarm_sdk/builders/base_builder.rb +5 -0
- data/lib/swarm_sdk/concerns/cleanupable.rb +3 -0
- data/lib/swarm_sdk/config.rb +2 -1
- data/lib/swarm_sdk/configuration/translator.rb +2 -0
- data/lib/swarm_sdk/observer/manager.rb +12 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +2 -0
- data/lib/swarm_sdk/result.rb +29 -0
- data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +27 -22
- data/lib/swarm_sdk/ruby_llm_patches/configuration_patch.rb +14 -1
- data/lib/swarm_sdk/ruby_llm_patches/init.rb +3 -0
- data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
- data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +11 -8
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +9 -0
- data/lib/swarm_sdk/swarm/executor.rb +176 -20
- data/lib/swarm_sdk/swarm/hook_triggers.rb +11 -0
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +1 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +20 -0
- data/lib/swarm_sdk/swarm.rb +132 -6
- data/lib/swarm_sdk/tools/delegate.rb +92 -47
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk.rb +3 -0
- metadata +2 -1
data/lib/swarm_sdk/swarm.rb
CHANGED
|
@@ -72,8 +72,7 @@ module SwarmSDK
|
|
|
72
72
|
# Default tools available to all agents
|
|
73
73
|
DEFAULT_TOOLS = ToolConfigurator::DEFAULT_TOOLS
|
|
74
74
|
|
|
75
|
-
attr_reader :name, :agents, :lead_agent, :mcp_clients, :delegation_instances, :agent_definitions, :swarm_id, :parent_swarm_id, :swarm_registry, :scratchpad_storage, :allow_filesystem_tools, :hook_registry, :global_semaphore, :plugin_storages, :config_for_hooks, :observer_configs, :execution_timeout
|
|
76
|
-
attr_accessor :delegation_call_stack
|
|
75
|
+
attr_reader :name, :agents, :lead_agent, :mcp_clients, :delegation_instances, :agent_definitions, :swarm_id, :parent_swarm_id, :swarm_registry, :scratchpad_storage, :allow_filesystem_tools, :hook_registry, :global_semaphore, :plugin_storages, :config_for_hooks, :observer_configs, :execution_timeout, :stop_signal_read
|
|
77
76
|
|
|
78
77
|
# Check if scratchpad tools are enabled
|
|
79
78
|
#
|
|
@@ -174,9 +173,6 @@ module SwarmSDK
|
|
|
174
173
|
# Swarm registry for managing sub-swarms (initialized later if needed)
|
|
175
174
|
@swarm_registry = nil
|
|
176
175
|
|
|
177
|
-
# Delegation call stack for circular dependency detection
|
|
178
|
-
@delegation_call_stack = []
|
|
179
|
-
|
|
180
176
|
# Shared semaphore for all agents
|
|
181
177
|
@global_semaphore = Async::Semaphore.new(@global_concurrency)
|
|
182
178
|
|
|
@@ -221,6 +217,13 @@ module SwarmSDK
|
|
|
221
217
|
# Observer agent configurations
|
|
222
218
|
@observer_configs = []
|
|
223
219
|
@observer_manager = nil
|
|
220
|
+
|
|
221
|
+
# Stop mechanism state
|
|
222
|
+
@stop_requested = false
|
|
223
|
+
@execution_barrier = nil
|
|
224
|
+
@stop_signal_read = nil
|
|
225
|
+
@stop_signal_write = nil
|
|
226
|
+
@active_agent_chats = {}
|
|
224
227
|
end
|
|
225
228
|
|
|
226
229
|
# Add an agent to the swarm
|
|
@@ -475,12 +478,17 @@ module SwarmSDK
|
|
|
475
478
|
|
|
476
479
|
# Wait for all observer tasks to complete
|
|
477
480
|
#
|
|
481
|
+
# If a stop was requested, stops observer tasks immediately instead of waiting.
|
|
478
482
|
# Called by Executor to wait for observer agents before cleanup.
|
|
479
483
|
# Safe to call even if no observers are configured.
|
|
480
484
|
#
|
|
481
485
|
# @return [void]
|
|
482
486
|
def wait_for_observers
|
|
483
|
-
@
|
|
487
|
+
if @stop_requested
|
|
488
|
+
stop_observers
|
|
489
|
+
else
|
|
490
|
+
@observer_manager&.wait_for_completion
|
|
491
|
+
end
|
|
484
492
|
end
|
|
485
493
|
|
|
486
494
|
# Cleanup observer subscriptions
|
|
@@ -494,6 +502,124 @@ module SwarmSDK
|
|
|
494
502
|
@observer_manager = nil
|
|
495
503
|
end
|
|
496
504
|
|
|
505
|
+
# Stop all swarm execution immediately
|
|
506
|
+
#
|
|
507
|
+
# Thread-safe method that signals the execution to stop. Uses IO.pipe
|
|
508
|
+
# for cross-thread signaling, which wakes the Async scheduler from any
|
|
509
|
+
# thread. The stop listener task then calls barrier.stop within the
|
|
510
|
+
# reactor to cancel all executing tasks.
|
|
511
|
+
#
|
|
512
|
+
# Safe to call from event callbacks, other threads, or signal handlers.
|
|
513
|
+
# No-op if no execution is in progress or stop was already requested.
|
|
514
|
+
#
|
|
515
|
+
# @return [void]
|
|
516
|
+
#
|
|
517
|
+
# @example Stop from event callback
|
|
518
|
+
# swarm.execute("Build auth") do |event|
|
|
519
|
+
# swarm.stop if event[:type] == "tool_call" && event[:tool] == "Dangerous"
|
|
520
|
+
# end
|
|
521
|
+
#
|
|
522
|
+
# @example Stop from another thread
|
|
523
|
+
# Thread.new { swarm.execute("Build auth") }
|
|
524
|
+
# sleep 10
|
|
525
|
+
# swarm.stop
|
|
526
|
+
def stop
|
|
527
|
+
return if @stop_requested
|
|
528
|
+
|
|
529
|
+
@stop_requested = true
|
|
530
|
+
begin
|
|
531
|
+
@stop_signal_write&.write("x") unless @stop_signal_write&.closed?
|
|
532
|
+
@stop_signal_write&.close unless @stop_signal_write&.closed?
|
|
533
|
+
rescue IOError, Errno::EPIPE
|
|
534
|
+
# Pipe already closed - normal during cleanup
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# Check if a stop has been requested
|
|
539
|
+
#
|
|
540
|
+
# @return [Boolean] true if stop was requested
|
|
541
|
+
def stop_requested?
|
|
542
|
+
@stop_requested
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Prepare stop signaling for a new execution
|
|
546
|
+
#
|
|
547
|
+
# Resets the stop flag and creates a new IO.pipe for signaling.
|
|
548
|
+
# Called by Executor at the start of each execution.
|
|
549
|
+
#
|
|
550
|
+
# @return [void]
|
|
551
|
+
def prepare_for_execution
|
|
552
|
+
@stop_requested = false
|
|
553
|
+
@stop_signal_read, @stop_signal_write = IO.pipe
|
|
554
|
+
@active_agent_chats = {}
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Close the stop signal pipe
|
|
558
|
+
#
|
|
559
|
+
# Called by Executor after execution completes.
|
|
560
|
+
#
|
|
561
|
+
# @return [void]
|
|
562
|
+
def cleanup_stop_signal
|
|
563
|
+
@stop_signal_read&.close unless @stop_signal_read&.closed?
|
|
564
|
+
@stop_signal_write&.close unless @stop_signal_write&.closed?
|
|
565
|
+
@stop_signal_read = nil
|
|
566
|
+
@stop_signal_write = nil
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Register the execution barrier for stop cancellation
|
|
570
|
+
#
|
|
571
|
+
# @param barrier [Async::Barrier] The barrier wrapping execution tasks
|
|
572
|
+
# @return [void]
|
|
573
|
+
def register_execution_barrier(barrier)
|
|
574
|
+
@execution_barrier = barrier
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Clear the execution barrier reference
|
|
578
|
+
#
|
|
579
|
+
# @return [void]
|
|
580
|
+
def clear_execution_barrier
|
|
581
|
+
@execution_barrier = nil
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Mark an agent as actively executing an LLM call
|
|
585
|
+
#
|
|
586
|
+
# Called by Agent::Chat#execute_ask to track which agents are mid-execution.
|
|
587
|
+
# Used during interruption to emit agent_stop events for active agents.
|
|
588
|
+
#
|
|
589
|
+
# @param name [Symbol] Agent name
|
|
590
|
+
# @param chat [Agent::Chat] Agent chat instance
|
|
591
|
+
# @return [void]
|
|
592
|
+
def mark_agent_active(name, chat)
|
|
593
|
+
@active_agent_chats[name] = chat
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Mark an agent as no longer actively executing
|
|
597
|
+
#
|
|
598
|
+
# @param name [Symbol] Agent name
|
|
599
|
+
# @return [void]
|
|
600
|
+
def mark_agent_inactive(name)
|
|
601
|
+
@active_agent_chats.delete(name)
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# Get a snapshot of currently active agent chats
|
|
605
|
+
#
|
|
606
|
+
# Returns a copy to avoid concurrent modification issues.
|
|
607
|
+
#
|
|
608
|
+
# @return [Hash{Symbol => Agent::Chat}] Copy of active agent chats
|
|
609
|
+
def active_agent_chats
|
|
610
|
+
@active_agent_chats.dup
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
# Stop all observer tasks immediately
|
|
614
|
+
#
|
|
615
|
+
# Interrupts in-flight observer LLM calls.
|
|
616
|
+
# Called during swarm interruption instead of wait_for_completion.
|
|
617
|
+
#
|
|
618
|
+
# @return [void]
|
|
619
|
+
def stop_observers
|
|
620
|
+
@observer_manager&.stop
|
|
621
|
+
end
|
|
622
|
+
|
|
497
623
|
# Create snapshot of current conversation state
|
|
498
624
|
#
|
|
499
625
|
# Returns a Snapshot object containing:
|
|
@@ -45,7 +45,7 @@ module SwarmSDK
|
|
|
45
45
|
# @param delegate_description [String] Description of the delegate agent
|
|
46
46
|
# @param delegate_chat [AgentChat, nil] The chat instance for the delegate agent (nil if delegating to swarm)
|
|
47
47
|
# @param agent_name [Symbol, String] Name of the agent using this tool
|
|
48
|
-
# @param swarm [Swarm] The swarm instance (provides hook_registry,
|
|
48
|
+
# @param swarm [Swarm] The swarm instance (provides hook_registry, swarm_registry)
|
|
49
49
|
# @param delegating_chat [Agent::Chat, nil] The chat instance of the agent doing the delegating (for accessing hooks)
|
|
50
50
|
# @param custom_tool_name [String, nil] Optional custom tool name (overrides auto-generated name)
|
|
51
51
|
# @param preserve_context [Boolean] Whether to preserve conversation context between delegations (default: true)
|
|
@@ -72,6 +72,16 @@ module SwarmSDK
|
|
|
72
72
|
# Use custom tool name if provided, otherwise generate using canonical method
|
|
73
73
|
@tool_name = custom_tool_name || self.class.tool_name_for(delegate_name)
|
|
74
74
|
@delegate_target = delegate_name.to_s
|
|
75
|
+
|
|
76
|
+
# Track concurrent delegations to this target.
|
|
77
|
+
# When multiple parallel tool calls target the same delegate, only the first
|
|
78
|
+
# preserves context; subsequent concurrent calls always clear context to
|
|
79
|
+
# prevent cross-contamination between independent parallel work.
|
|
80
|
+
#
|
|
81
|
+
# No Mutex needed: Async Fibers run on a single thread and only switch at
|
|
82
|
+
# explicit yield points (IO, sleep, semaphore.acquire). Integer increment
|
|
83
|
+
# and decrement never yield, so they are inherently atomic.
|
|
84
|
+
@active_count = 0
|
|
75
85
|
end
|
|
76
86
|
|
|
77
87
|
# Override description to return dynamic string based on delegate
|
|
@@ -122,19 +132,32 @@ module SwarmSDK
|
|
|
122
132
|
|
|
123
133
|
# Execute delegation with pre/post hooks
|
|
124
134
|
#
|
|
135
|
+
# Uses Fiber-local path tracking for circular dependency detection.
|
|
136
|
+
# Each concurrent delegation runs in its own Fiber (via Async), so the path
|
|
137
|
+
# is isolated per execution path. This correctly distinguishes parallel fan-out
|
|
138
|
+
# (A→B, A→B) from true circular dependencies (A→B→A).
|
|
139
|
+
#
|
|
125
140
|
# @param message [String] Message to send to the agent
|
|
126
141
|
# @param reset_context [Boolean] Whether to reset the agent's conversation history before delegation
|
|
127
142
|
# @return [String] Result from delegate agent or error message
|
|
128
143
|
def execute(message:, reset_context: false)
|
|
144
|
+
# Save the current delegation path so we can restore it after execution.
|
|
145
|
+
# The extended path (with our target) is only needed during chat.ask() so
|
|
146
|
+
# child Fibers (nested delegations) inherit it. After delegation returns,
|
|
147
|
+
# this Fiber's path should be unchanged.
|
|
148
|
+
saved_delegation_path = Fiber[:delegation_path]
|
|
149
|
+
|
|
129
150
|
# Access swarm infrastructure
|
|
130
|
-
call_stack = @swarm.delegation_call_stack
|
|
131
151
|
hook_registry = @swarm.hook_registry
|
|
132
152
|
swarm_registry = @swarm.swarm_registry
|
|
133
153
|
|
|
134
|
-
# Check for circular dependency
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
# Check for circular dependency using Fiber-local path
|
|
155
|
+
# Each Fiber inherits the parent's path, so nested delegations
|
|
156
|
+
# accumulate the full chain while parallel siblings remain isolated
|
|
157
|
+
delegation_path = saved_delegation_path || []
|
|
158
|
+
if delegation_path.include?(@delegate_target)
|
|
159
|
+
emit_circular_warning(delegation_path)
|
|
160
|
+
return "Error: Circular delegation detected: #{delegation_path.join(" -> ")} -> #{@delegate_target}. " \
|
|
138
161
|
"Please restructure your delegation to avoid infinite loops."
|
|
139
162
|
end
|
|
140
163
|
|
|
@@ -172,10 +195,10 @@ module SwarmSDK
|
|
|
172
195
|
# Determine delegation type and proceed
|
|
173
196
|
delegation_result = if @delegate_chat
|
|
174
197
|
# Delegate to agent
|
|
175
|
-
delegate_to_agent(message,
|
|
198
|
+
delegate_to_agent(message, reset_context: reset_context)
|
|
176
199
|
elsif swarm_registry&.registered?(@delegate_target)
|
|
177
200
|
# Delegate to registered swarm
|
|
178
|
-
delegate_to_swarm(message,
|
|
201
|
+
delegate_to_swarm(message, swarm_registry, reset_context: reset_context)
|
|
179
202
|
else
|
|
180
203
|
raise ConfigurationError, "Unknown delegation target: #{@delegate_target}"
|
|
181
204
|
end
|
|
@@ -246,6 +269,11 @@ module SwarmSDK
|
|
|
246
269
|
# Return error string for LLM
|
|
247
270
|
backtrace_str = backtrace_array.join("\n ")
|
|
248
271
|
"Error: #{@tool_name} encountered an error: #{e.class.name}: #{e.message}\nBacktrace:\n #{backtrace_str}"
|
|
272
|
+
ensure
|
|
273
|
+
# Restore the calling Fiber's delegation path.
|
|
274
|
+
# The extended path was only needed during chat.ask() so child Fibers
|
|
275
|
+
# (spawned for nested tool calls) could inherit it for circular detection.
|
|
276
|
+
Fiber[:delegation_path] = saved_delegation_path
|
|
249
277
|
end
|
|
250
278
|
|
|
251
279
|
private
|
|
@@ -254,28 +282,41 @@ module SwarmSDK
|
|
|
254
282
|
#
|
|
255
283
|
# Handles both eager Agent::Chat instances and lazy-loaded delegates.
|
|
256
284
|
# LazyDelegateChat instances are initialized on first access.
|
|
285
|
+
# Sets Fiber-local delegation path so child Fibers (nested delegations)
|
|
286
|
+
# inherit the full chain for circular dependency detection.
|
|
287
|
+
#
|
|
288
|
+
# Tracks concurrent delegations to this target. When multiple parallel
|
|
289
|
+
# tool calls target the same delegate (fan-out), only the first call
|
|
290
|
+
# preserves context; subsequent concurrent calls always clear context
|
|
291
|
+
# to prevent cross-contamination between independent parallel work.
|
|
292
|
+
# Context clearing happens inside Agent::Chat's ask_semaphore for safety.
|
|
257
293
|
#
|
|
258
294
|
# @param message [String] Message to send to the agent
|
|
259
|
-
# @param call_stack [Array] Delegation call stack for circular dependency detection
|
|
260
295
|
# @param reset_context [Boolean] Whether to reset the agent's conversation history before delegation
|
|
261
296
|
# @return [String] Result from agent
|
|
262
|
-
def delegate_to_agent(message,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
297
|
+
def delegate_to_agent(message, reset_context: false)
|
|
298
|
+
@active_count += 1
|
|
299
|
+
concurrent = @active_count > 1
|
|
300
|
+
|
|
301
|
+
# Set Fiber-local delegation path for this execution path
|
|
302
|
+
# Child Fibers (from nested delegations) inherit this path automatically
|
|
303
|
+
# We create a new array to avoid mutating the parent Fiber's reference
|
|
304
|
+
Fiber[:delegation_path] = (Fiber[:delegation_path] || []) + [@delegate_target]
|
|
305
|
+
|
|
306
|
+
# Resolve the chat instance (handles lazy loading)
|
|
307
|
+
chat = resolve_delegate_chat
|
|
308
|
+
|
|
309
|
+
# Determine if context should be cleared:
|
|
310
|
+
# - reset_context: explicit caller request
|
|
311
|
+
# - !preserve_context: agent configuration
|
|
312
|
+
# - concurrent: parallel fan-out to same delegate (always isolate)
|
|
313
|
+
# Clearing is done inside chat.ask's semaphore to avoid race conditions
|
|
314
|
+
should_clear = reset_context || !@preserve_context || concurrent
|
|
315
|
+
|
|
316
|
+
response = chat.ask(message, source: "delegation", clear_context: should_clear)
|
|
317
|
+
response.content
|
|
318
|
+
ensure
|
|
319
|
+
@active_count -= 1
|
|
279
320
|
end
|
|
280
321
|
|
|
281
322
|
# Resolve the delegate chat instance
|
|
@@ -294,48 +335,52 @@ module SwarmSDK
|
|
|
294
335
|
|
|
295
336
|
# Delegate to a registered swarm
|
|
296
337
|
#
|
|
338
|
+
# Sets Fiber-local delegation path so child Fibers (nested delegations)
|
|
339
|
+
# inherit the full chain for circular dependency detection.
|
|
340
|
+
# Tracks concurrent delegations the same way as delegate_to_agent.
|
|
341
|
+
#
|
|
297
342
|
# @param message [String] Message to send to the swarm
|
|
298
|
-
# @param call_stack [Array] Delegation call stack for circular dependency detection
|
|
299
343
|
# @param swarm_registry [SwarmRegistry] Registry for sub-swarms
|
|
300
344
|
# @param reset_context [Boolean] Whether to reset the swarm's conversation history before delegation
|
|
301
345
|
# @return [String] Result from swarm's lead agent
|
|
302
|
-
def delegate_to_swarm(message,
|
|
346
|
+
def delegate_to_swarm(message, swarm_registry, reset_context: false)
|
|
347
|
+
@active_count += 1
|
|
348
|
+
concurrent = @active_count > 1
|
|
349
|
+
|
|
350
|
+
# Set Fiber-local delegation path for this execution path
|
|
351
|
+
Fiber[:delegation_path] = (Fiber[:delegation_path] || []) + [@delegate_target]
|
|
352
|
+
|
|
303
353
|
# Load sub-swarm (lazy load + cache)
|
|
304
354
|
subswarm = swarm_registry.load_swarm(@delegate_target)
|
|
305
355
|
|
|
306
|
-
#
|
|
307
|
-
|
|
308
|
-
begin
|
|
309
|
-
# Reset swarm if reset_context is true
|
|
310
|
-
swarm_registry.reset(@delegate_target) if reset_context
|
|
356
|
+
# Reset swarm context if explicitly requested or concurrent fan-out
|
|
357
|
+
swarm_registry.reset(@delegate_target) if reset_context || concurrent
|
|
311
358
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
359
|
+
# Execute sub-swarm's lead agent (uses agent() to trigger lazy initialization)
|
|
360
|
+
lead_agent = subswarm.agent(subswarm.lead_agent)
|
|
361
|
+
response = lead_agent.ask(message, source: "delegation")
|
|
362
|
+
result = response.content
|
|
316
363
|
|
|
317
|
-
|
|
318
|
-
|
|
364
|
+
# Reset if keep_context: false (standard behavior)
|
|
365
|
+
swarm_registry.reset_if_needed(@delegate_target)
|
|
319
366
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
call_stack.pop
|
|
324
|
-
end
|
|
367
|
+
result
|
|
368
|
+
ensure
|
|
369
|
+
@active_count -= 1
|
|
325
370
|
end
|
|
326
371
|
|
|
327
372
|
# Emit circular dependency warning event
|
|
328
373
|
#
|
|
329
|
-
# @param
|
|
374
|
+
# @param delegation_path [Array<String>] Current Fiber-local delegation path
|
|
330
375
|
# @return [void]
|
|
331
|
-
def emit_circular_warning(
|
|
376
|
+
def emit_circular_warning(delegation_path)
|
|
332
377
|
LogStream.emit(
|
|
333
378
|
type: "delegation_circular_dependency",
|
|
334
379
|
agent: @agent_name,
|
|
335
380
|
swarm_id: @swarm.swarm_id,
|
|
336
381
|
parent_swarm_id: @swarm.parent_swarm_id,
|
|
337
382
|
target: @delegate_target,
|
|
338
|
-
|
|
383
|
+
delegation_path: delegation_path,
|
|
339
384
|
timestamp: Time.now.utc.iso8601,
|
|
340
385
|
)
|
|
341
386
|
end
|
data/lib/swarm_sdk/version.rb
CHANGED
data/lib/swarm_sdk.rb
CHANGED
|
@@ -91,6 +91,9 @@ module SwarmSDK
|
|
|
91
91
|
# Raised when agent turn exceeds turn_timeout
|
|
92
92
|
class TurnTimeoutError < TimeoutError; end
|
|
93
93
|
|
|
94
|
+
# Raised when swarm execution is interrupted via swarm.stop
|
|
95
|
+
class InterruptedError < Error; end
|
|
96
|
+
|
|
94
97
|
# Base class for MCP-related errors (provides context about server/tool)
|
|
95
98
|
class MCPError < Error; end
|
|
96
99
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: swarm_sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.7.
|
|
4
|
+
version: 2.7.14
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paulo Arruda
|
|
@@ -181,6 +181,7 @@ files:
|
|
|
181
181
|
- lib/swarm_sdk/ruby_llm_patches/connection_patch.rb
|
|
182
182
|
- lib/swarm_sdk/ruby_llm_patches/init.rb
|
|
183
183
|
- lib/swarm_sdk/ruby_llm_patches/io_endpoint_patch.rb
|
|
184
|
+
- lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb
|
|
184
185
|
- lib/swarm_sdk/ruby_llm_patches/message_management_patch.rb
|
|
185
186
|
- lib/swarm_sdk/ruby_llm_patches/responses_api_patch.rb
|
|
186
187
|
- lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb
|