swarm_memory 2.1.5 → 2.1.6

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. metadata +5 -184
  4. data/lib/claude_swarm/base_executor.rb +0 -133
  5. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  6. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  7. data/lib/claude_swarm/cli.rb +0 -697
  8. data/lib/claude_swarm/commands/ps.rb +0 -215
  9. data/lib/claude_swarm/commands/show.rb +0 -139
  10. data/lib/claude_swarm/configuration.rb +0 -373
  11. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  12. data/lib/claude_swarm/json_handler.rb +0 -91
  13. data/lib/claude_swarm/mcp_generator.rb +0 -230
  14. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  15. data/lib/claude_swarm/openai/executor.rb +0 -256
  16. data/lib/claude_swarm/openai/responses.rb +0 -319
  17. data/lib/claude_swarm/orchestrator.rb +0 -878
  18. data/lib/claude_swarm/process_tracker.rb +0 -78
  19. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  20. data/lib/claude_swarm/session_path.rb +0 -42
  21. data/lib/claude_swarm/settings_generator.rb +0 -77
  22. data/lib/claude_swarm/system_utils.rb +0 -46
  23. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  24. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  25. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  27. data/lib/claude_swarm/version.rb +0 -5
  28. data/lib/claude_swarm/worktree_manager.rb +0 -475
  29. data/lib/claude_swarm/yaml_loader.rb +0 -22
  30. data/lib/claude_swarm.rb +0 -67
  31. data/lib/swarm_cli/cli.rb +0 -201
  32. data/lib/swarm_cli/command_registry.rb +0 -61
  33. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  34. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  35. data/lib/swarm_cli/commands/migrate.rb +0 -55
  36. data/lib/swarm_cli/commands/run.rb +0 -173
  37. data/lib/swarm_cli/config_loader.rb +0 -98
  38. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  39. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  40. data/lib/swarm_cli/interactive_repl.rb +0 -924
  41. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  42. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  43. data/lib/swarm_cli/migrate_options.rb +0 -54
  44. data/lib/swarm_cli/migrator.rb +0 -132
  45. data/lib/swarm_cli/options.rb +0 -151
  46. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  47. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  48. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  49. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  50. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  51. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  52. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  53. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  54. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  55. data/lib/swarm_cli/ui/icons.rb +0 -36
  56. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  57. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  58. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  59. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  60. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  61. data/lib/swarm_cli/version.rb +0 -5
  62. data/lib/swarm_cli.rb +0 -46
  63. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  64. data/lib/swarm_sdk/agent/builder.rb +0 -552
  65. data/lib/swarm_sdk/agent/chat.rb +0 -774
  66. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  67. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  68. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  69. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  70. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  71. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  72. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  73. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  75. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  76. data/lib/swarm_sdk/agent/context.rb +0 -116
  77. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  78. data/lib/swarm_sdk/agent/definition.rb +0 -477
  79. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  80. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  81. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  82. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  83. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  84. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  85. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  86. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  87. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  88. data/lib/swarm_sdk/configuration.rb +0 -135
  89. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  90. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  91. data/lib/swarm_sdk/context_compactor.rb +0 -335
  92. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  93. data/lib/swarm_sdk/context_management/context.rb +0 -328
  94. data/lib/swarm_sdk/defaults.rb +0 -196
  95. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  96. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  97. data/lib/swarm_sdk/hooks/context.rb +0 -197
  98. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  99. data/lib/swarm_sdk/hooks/error.rb +0 -29
  100. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  101. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  102. data/lib/swarm_sdk/hooks/result.rb +0 -150
  103. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  104. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  105. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  106. data/lib/swarm_sdk/log_collector.rb +0 -227
  107. data/lib/swarm_sdk/log_stream.rb +0 -127
  108. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  109. data/lib/swarm_sdk/model_aliases.json +0 -8
  110. data/lib/swarm_sdk/models.json +0 -1
  111. data/lib/swarm_sdk/models.rb +0 -120
  112. data/lib/swarm_sdk/node_context.rb +0 -245
  113. data/lib/swarm_sdk/observer/builder.rb +0 -81
  114. data/lib/swarm_sdk/observer/config.rb +0 -45
  115. data/lib/swarm_sdk/observer/manager.rb +0 -236
  116. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  117. data/lib/swarm_sdk/permissions/config.rb +0 -239
  118. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  119. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  120. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  121. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  122. data/lib/swarm_sdk/plugin.rb +0 -309
  123. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  124. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  125. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  126. data/lib/swarm_sdk/restore_result.rb +0 -65
  127. data/lib/swarm_sdk/result.rb +0 -123
  128. data/lib/swarm_sdk/snapshot.rb +0 -156
  129. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  130. data/lib/swarm_sdk/state_restorer.rb +0 -476
  131. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  132. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  133. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  134. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  135. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  136. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  141. data/lib/swarm_sdk/swarm.rb +0 -717
  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/bash.rb +0 -282
  145. data/lib/swarm_sdk/tools/clock.rb +0 -44
  146. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  147. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  148. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  149. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  150. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  151. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  152. data/lib/swarm_sdk/tools/edit.rb +0 -145
  153. data/lib/swarm_sdk/tools/glob.rb +0 -166
  154. data/lib/swarm_sdk/tools/grep.rb +0 -235
  155. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  156. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
  157. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  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 -272
  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 -98
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/utils.rb +0 -68
  174. data/lib/swarm_sdk/validation_result.rb +0 -33
  175. data/lib/swarm_sdk/version.rb +0 -5
  176. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  177. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  178. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  179. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  180. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  181. data/lib/swarm_sdk/workflow.rb +0 -554
  182. data/lib/swarm_sdk.rb +0 -524
@@ -1,167 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # AllAgentsBuilder for configuring settings that apply to all agents
6
- #
7
- # Settings configured here are applied to ALL agents, but can be overridden
8
- # at the agent level. This is useful for shared configuration like:
9
- # - Common provider/base_url (all agents use same proxy)
10
- # - Shared timeout settings
11
- # - Global permissions
12
- #
13
- # @example
14
- # all_agents do
15
- # provider :openai
16
- # base_url "http://proxy.com/v1"
17
- # timeout 180
18
- # tools :Read, :Write
19
- # coding_agent false
20
- # end
21
- class AllAgentsBuilder
22
- attr_reader :hooks, :permissions_config, :tools_list
23
-
24
- def initialize
25
- @tools_list = []
26
- @hooks = []
27
- @permissions_config = {}
28
- @model = nil
29
- @provider = nil
30
- @base_url = nil
31
- @api_version = nil
32
- @timeout = nil
33
- @parameters = nil
34
- @headers = nil
35
- @coding_agent = nil
36
- @disable_default_tools = nil
37
- end
38
-
39
- # Set model for all agents
40
- def model(model_name)
41
- @model = model_name
42
- end
43
-
44
- # Set provider for all agents
45
- def provider(provider_name)
46
- @provider = provider_name
47
- end
48
-
49
- # Set base URL for all agents
50
- def base_url(url)
51
- @base_url = url
52
- end
53
-
54
- # Set API version for all agents
55
- def api_version(version)
56
- @api_version = version
57
- end
58
-
59
- # Set timeout for all agents
60
- def timeout(seconds)
61
- @timeout = seconds
62
- end
63
-
64
- # Set parameters for all agents
65
- def parameters(params)
66
- @parameters = params
67
- end
68
-
69
- # Set headers for all agents
70
- def headers(header_hash)
71
- @headers = header_hash
72
- end
73
-
74
- # Set coding_agent flag for all agents
75
- def coding_agent(enabled)
76
- @coding_agent = enabled
77
- end
78
-
79
- # Disable default tools for all agents
80
- #
81
- # @param value [Boolean, Array<Symbol>]
82
- # - true: Disable ALL default tools
83
- # - Array of symbols: Disable specific tools (e.g., [:Think, :TodoWrite])
84
- def disable_default_tools(value)
85
- @disable_default_tools = value
86
- end
87
-
88
- # Add tools that all agents will have
89
- def tools(*tool_names)
90
- @tools_list.concat(tool_names)
91
- end
92
-
93
- # Add hook for all agents (agent-level events only)
94
- #
95
- # @example
96
- # hook :pre_tool_use, matcher: "Write" do |ctx|
97
- # # Applies to all agents
98
- # end
99
- def hook(event, matcher: nil, command: nil, timeout: nil, &block)
100
- # Validate agent-level events
101
- agent_events = [
102
- :pre_tool_use,
103
- :post_tool_use,
104
- :user_prompt,
105
- :agent_step,
106
- :agent_stop,
107
- :first_message,
108
- :pre_delegation,
109
- :post_delegation,
110
- :context_warning,
111
- ]
112
-
113
- unless agent_events.include?(event)
114
- raise ArgumentError, "Invalid all_agents hook: #{event}. Swarm-level events (:swarm_start, :swarm_stop) cannot be used in all_agents block."
115
- end
116
-
117
- @hooks << { event: event, matcher: matcher, command: command, timeout: timeout, block: block }
118
- end
119
-
120
- # Configure permissions for all agents
121
- #
122
- # Supports two forms:
123
- # 1. Block form (DSL): permissions do ... end
124
- # 2. Direct hash (internal/YAML): set_permissions_hash(hash)
125
- #
126
- # @example Block form
127
- # permissions do
128
- # Write.allow_paths "tmp/**/*"
129
- # Write.deny_paths "tmp/secrets/**"
130
- # Bash.allow_commands "^git status$"
131
- # end
132
- def permissions(&block)
133
- @permissions_config = PermissionsBuilder.build(&block)
134
- end
135
-
136
- # Set permissions directly from hash (for YAML translation)
137
- #
138
- # This is intentionally separate from permissions() to keep the DSL clean.
139
- # Called by Configuration when translating YAML permissions.
140
- #
141
- # @param hash [Hash] Permissions configuration hash
142
- # @return [void]
143
- def permissions_hash=(hash)
144
- @permissions_config = hash || {}
145
- end
146
-
147
- # Convert to hash for merging with agent configs
148
- #
149
- # @return [Hash] Configuration hash
150
- def to_h
151
- {
152
- model: @model,
153
- provider: @provider,
154
- base_url: @base_url,
155
- api_version: @api_version,
156
- timeout: @timeout,
157
- parameters: @parameters,
158
- headers: @headers,
159
- coding_agent: @coding_agent,
160
- disable_default_tools: @disable_default_tools,
161
- tools: @tools_list,
162
- permissions: @permissions_config,
163
- }.compact
164
- end
165
- end
166
- end
167
- end
@@ -1,249 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- class Swarm
5
- # Builder provides a beautiful Ruby DSL for building swarms
6
- #
7
- # The DSL combines YAML simplicity with Ruby power, enabling:
8
- # - Fluent, chainable configuration
9
- # - Hooks as Ruby blocks OR shell commands
10
- # - Full Ruby language features (variables, conditionals, loops)
11
- # - Type-safe, IDE-friendly API
12
- #
13
- # @example Basic usage
14
- # swarm = SwarmSDK.build do
15
- # name "Dev Team"
16
- # lead :backend
17
- #
18
- # agent :backend do
19
- # model "gpt-5"
20
- # prompt "You build APIs"
21
- # tools :Read, :Write, :Bash
22
- #
23
- # # Hook as Ruby block - inline logic!
24
- # hook :pre_tool_use, matcher: "Bash" do |ctx|
25
- # SwarmSDK::Hooks::Result.halt("Blocked!") if ctx.tool_call.parameters[:command].include?("rm -rf")
26
- # end
27
- # end
28
- # end
29
- #
30
- # swarm.execute("Build auth API")
31
- class Builder < Builders::BaseBuilder
32
- # Main entry point for DSL
33
- #
34
- # @example
35
- # swarm = SwarmSDK.build do
36
- # name "Team"
37
- # agent :backend { ... }
38
- # end
39
- class << self
40
- def build(allow_filesystem_tools: nil, &block)
41
- builder = new(allow_filesystem_tools: allow_filesystem_tools)
42
- builder.instance_eval(&block)
43
- builder.build_swarm
44
- end
45
- end
46
-
47
- def initialize(allow_filesystem_tools: nil)
48
- super
49
- @lead_agent = nil
50
- @swarm_hooks = []
51
- @observer_configs = []
52
- end
53
-
54
- # Set lead agent
55
- def lead(agent_name)
56
- @lead_agent = agent_name
57
- end
58
-
59
- # Define observer agent behavior
60
- #
61
- # Configures an agent to run in parallel with main execution,
62
- # triggered by specific events. The block defines event handlers.
63
- #
64
- # @param agent_name [Symbol] Name of agent to use as observer (must be defined)
65
- # @param options [Hash] Optional observer settings (timeout, max_concurrent, etc.)
66
- # @yield Observer configuration block
67
- #
68
- # @example Basic observer
69
- # observer :profiler do
70
- # on :swarm_start do |event|
71
- # "Analyze this prompt: #{event[:prompt]}"
72
- # end
73
- # end
74
- #
75
- # @example Observer with options
76
- # observer :monitor, timeout: 120 do
77
- # on :tool_call do |event|
78
- # next unless event[:tool_name] == "Bash"
79
- # "Check command: #{event[:arguments][:command]}"
80
- # end
81
- # end
82
- def observer(agent_name, **options, &block)
83
- unless @agents.key?(agent_name)
84
- raise ConfigurationError,
85
- "Observer agent '#{agent_name}' not defined. " \
86
- "Define the agent first with `agent :#{agent_name} do ... end`"
87
- end
88
-
89
- config = Observer::Config.new(agent_name)
90
- config.options.merge!(options) if options.any?
91
- builder = Observer::Builder.new(agent_name, config)
92
- builder.instance_eval(&block)
93
-
94
- @observer_configs << config
95
- end
96
-
97
- # Add swarm-level hook (swarm_start, swarm_stop only)
98
- #
99
- # @example Shell command
100
- # hook :swarm_start, command: "echo 'Starting' >> log.txt"
101
- #
102
- # @example Ruby block
103
- # hook :swarm_start do |ctx|
104
- # puts "Swarm starting: #{ctx.metadata[:prompt]}"
105
- # end
106
- def hook(event, command: nil, timeout: nil, &block)
107
- # Validate swarm-level events
108
- unless [:swarm_start, :swarm_stop].include?(event)
109
- raise ArgumentError, "Invalid swarm-level hook: #{event}. Only :swarm_start and :swarm_stop allowed at swarm level. Use all_agents { hook ... } or agent { hook ... } for other events."
110
- end
111
-
112
- @swarm_hooks << { event: event, command: command, timeout: timeout, block: block }
113
- end
114
-
115
- # Build the actual Swarm instance
116
- def build_swarm
117
- raise ConfigurationError, "Swarm name not set. Use: name 'My Swarm'" unless @swarm_name
118
- raise ConfigurationError, "No agents defined. Use: agent :name { ... }" if @agents.empty?
119
- raise ConfigurationError, "Lead agent not set. Use: lead :agent_name" unless @lead_agent
120
-
121
- # Validate filesystem tools BEFORE building
122
- validate_all_agents_filesystem_tools if @all_agents_config
123
- validate_agent_filesystem_tools
124
-
125
- build_single_swarm
126
- end
127
-
128
- private
129
-
130
- # Build a traditional single-swarm execution
131
- #
132
- # @return [Swarm] Configured swarm instance
133
- def build_single_swarm
134
- # Validate swarm_id is set if external swarms are registered (required for composable swarms)
135
- if @swarm_registry_config.any? && @swarm_id.nil?
136
- raise ConfigurationError, "Swarm id must be set using id(...) when using composable swarms"
137
- end
138
-
139
- # Create swarm using SDK (swarm_id auto-generates if nil)
140
- swarm = Swarm.new(
141
- name: @swarm_name,
142
- swarm_id: @swarm_id,
143
- scratchpad_mode: @scratchpad,
144
- allow_filesystem_tools: @allow_filesystem_tools,
145
- )
146
-
147
- # Setup swarm registry if external swarms are registered
148
- if @swarm_registry_config.any?
149
- registry = SwarmRegistry.new(parent_swarm_id: @swarm_id)
150
- @swarm_registry_config.each do |reg|
151
- registry.register(reg[:name], source: reg[:source], keep_context: reg[:keep_context])
152
- end
153
- swarm.swarm_registry = registry
154
- end
155
-
156
- # Build agent definitions and add to swarm
157
- agent_definitions = build_agent_definitions
158
- agent_definitions.each_value do |definition|
159
- swarm.add_agent(definition)
160
- end
161
-
162
- # Set lead
163
- swarm.lead = @lead_agent
164
-
165
- # Apply swarm hooks (Ruby blocks)
166
- @swarm_hooks.each do |hook_config|
167
- apply_swarm_hook(swarm, hook_config)
168
- end
169
-
170
- # Apply all_agents hooks (Ruby blocks)
171
- @all_agents_config&.hooks&.each do |hook_config|
172
- apply_all_agents_hook(swarm, hook_config)
173
- end
174
-
175
- # Add observer configurations to swarm
176
- @observer_configs.each { |c| swarm.add_observer_config(c) }
177
-
178
- swarm
179
- end
180
-
181
- def apply_swarm_hook(swarm, config)
182
- event = config[:event]
183
-
184
- if config[:block]
185
- # Ruby block hook - register directly
186
- swarm.add_default_callback(event, &config[:block])
187
- elsif config[:command]
188
- # Shell command hook - use ShellExecutor
189
- swarm.add_default_callback(event) do |context|
190
- input_json = build_hook_input(context, event)
191
- Hooks::ShellExecutor.execute(
192
- command: config[:command],
193
- input_json: input_json,
194
- timeout: config[:timeout] || 60,
195
- swarm_name: swarm.name,
196
- event: event,
197
- )
198
- end
199
- end
200
- end
201
-
202
- def apply_all_agents_hook(swarm, config)
203
- event = config[:event]
204
- matcher = config[:matcher]
205
-
206
- if config[:block]
207
- # Ruby block hook
208
- swarm.add_default_callback(event, matcher: matcher, &config[:block])
209
- elsif config[:command]
210
- # Shell command hook
211
- swarm.add_default_callback(event, matcher: matcher) do |context|
212
- input_json = build_hook_input(context, event)
213
- Hooks::ShellExecutor.execute(
214
- command: config[:command],
215
- input_json: input_json,
216
- timeout: config[:timeout] || 60,
217
- agent_name: context.agent_name,
218
- swarm_name: swarm.name,
219
- event: event,
220
- )
221
- end
222
- end
223
- end
224
-
225
- def build_hook_input(context, event)
226
- # Build JSON input for shell hooks
227
- base = { event: event.to_s }
228
-
229
- case event
230
- when :pre_tool_use
231
- base.merge(tool: context.tool_call.name, parameters: context.tool_call.parameters)
232
- when :post_tool_use
233
- base.merge(result: context.tool_result.content, success: context.tool_result.success?)
234
- when :user_prompt
235
- base.merge(prompt: context.metadata[:prompt])
236
- when :swarm_start
237
- base.merge(prompt: context.metadata[:prompt])
238
- when :swarm_stop
239
- base.merge(success: context.metadata[:success], duration: context.metadata[:duration])
240
- else
241
- base
242
- end
243
- end
244
- end
245
-
246
- # Helper class for swarms block in DSL (kept in this file for reference)
247
- # Actual implementation is in swarm_registry_builder.rb for Zeitwerk
248
- end
249
- end
@@ -1,213 +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
- Sync do |task|
36
- execute_in_task(prompt, logs: logs, has_logging: has_logging) do |lead, current_prompt|
37
- task.async(finished: false) { lead.ask(current_prompt) }.wait
38
- end
39
- ensure
40
- # Always wait for observer tasks, even if main execution raises
41
- # This is INSIDE Sync block, so async tasks can still complete
42
- @swarm.wait_for_observers
43
- end
44
- ensure
45
- # Restore original fiber storage (preserves parent context for nested swarms)
46
- restore_fiber_storage
47
- end
48
-
49
- # Non-blocking execution using parent async task
50
- def run_async(prompt, logs:, has_logging:)
51
- parent = Async::Task.current
52
- raise ConfigurationError, "wait: false requires an async context. Use Sync { swarm.execute(..., wait: false) }" unless parent
53
-
54
- parent.async(finished: false) do
55
- execute_in_task(prompt, logs: logs, has_logging: has_logging) do |lead, current_prompt|
56
- Async(finished: false) { lead.ask(current_prompt) }.wait
57
- end
58
- end
59
- end
60
-
61
- # Core execution logic (unified, no duplication)
62
- #
63
- # @param prompt [String] Initial prompt
64
- # @param logs [Array] Log collection
65
- # @param has_logging [Boolean] Whether logging is enabled
66
- # @yield [lead, current_prompt] Block to execute LLM call
67
- # @return [Result] Execution result
68
- def execute_in_task(prompt, logs:, has_logging:, &block)
69
- start_time = Time.now
70
- result = nil
71
- swarm_stop_triggered = false
72
- current_prompt = prompt
73
-
74
- begin
75
- # Notify plugins that swarm is starting
76
- PluginRegistry.emit_event(:on_swarm_started, swarm: @swarm)
77
-
78
- result = execution_loop(current_prompt, logs, start_time, &block)
79
- swarm_stop_triggered = true
80
- rescue ConfigurationError, AgentNotFoundError
81
- # Re-raise configuration errors - these should be fixed, not caught
82
- raise
83
- rescue TypeError => e
84
- result = handle_type_error(e, logs, start_time)
85
- rescue StandardError => e
86
- result = handle_standard_error(e, logs, start_time)
87
- ensure
88
- # Notify plugins that swarm is stopping (called even on error)
89
- PluginRegistry.emit_event(:on_swarm_stopped, swarm: @swarm)
90
-
91
- cleanup_after_execution(result, start_time, logs, swarm_stop_triggered, has_logging)
92
- end
93
-
94
- result
95
- end
96
-
97
- # Main execution loop with reprompting support
98
- def execution_loop(initial_prompt, logs, start_time)
99
- current_prompt = initial_prompt
100
-
101
- loop do
102
- lead = @swarm.agents[@swarm.lead_agent]
103
- response = yield(lead, current_prompt)
104
-
105
- # Check if swarm was finished by a hook (finish_swarm)
106
- if response.is_a?(Hash) && response[:__finish_swarm__]
107
- result = build_result(response[:message], logs, start_time)
108
- @swarm.trigger_swarm_stop(result)
109
- return result
110
- end
111
-
112
- result = build_result(response.content, logs, start_time)
113
-
114
- # Trigger swarm_stop hooks (for reprompt check and event emission)
115
- hook_result = @swarm.trigger_swarm_stop(result)
116
-
117
- # Check if hook requests reprompting
118
- if hook_result&.reprompt?
119
- current_prompt = hook_result.value
120
- # Continue loop with new prompt
121
- else
122
- # Exit loop - execution complete
123
- return result
124
- end
125
- end
126
- end
127
-
128
- # Build a Result object
129
- def build_result(content, logs, start_time)
130
- Result.new(
131
- content: content,
132
- agent: @swarm.lead_agent.to_s,
133
- logs: logs,
134
- duration: Time.now - start_time,
135
- )
136
- end
137
-
138
- # Handle TypeError (e.g., "String does not have #dig method")
139
- def handle_type_error(error, logs, start_time)
140
- if error.message.include?("does not have #dig method")
141
- agent_definition = @swarm.agent_definitions[@swarm.lead_agent]
142
- error_msg = if agent_definition.base_url
143
- "LLM API request failed: The proxy/server at '#{agent_definition.base_url}' returned an invalid response. " \
144
- "This usually means the proxy is unreachable, requires authentication, or returned an error in non-JSON format. " \
145
- "Original error: #{error.message}"
146
- else
147
- "LLM API request failed with unexpected response format. Original error: #{error.message}"
148
- end
149
-
150
- Result.new(
151
- content: nil,
152
- agent: @swarm.lead_agent.to_s,
153
- error: LLMError.new(error_msg),
154
- logs: logs,
155
- duration: Time.now - start_time,
156
- )
157
- else
158
- Result.new(
159
- content: nil,
160
- agent: @swarm.lead_agent.to_s,
161
- error: error,
162
- logs: logs,
163
- duration: Time.now - start_time,
164
- )
165
- end
166
- end
167
-
168
- # Handle StandardError
169
- def handle_standard_error(error, logs, start_time)
170
- Result.new(
171
- content: nil,
172
- agent: @swarm.lead_agent&.to_s || "unknown",
173
- error: error,
174
- logs: logs,
175
- duration: Time.now - start_time,
176
- )
177
- end
178
-
179
- # Cleanup after execution (ensure block logic)
180
- def cleanup_after_execution(result, start_time, logs, swarm_stop_triggered, has_logging)
181
- # Trigger swarm_stop if not already triggered (handles error cases)
182
- unless swarm_stop_triggered
183
- @swarm.trigger_swarm_stop_final(result, start_time, logs)
184
- end
185
-
186
- # Cleanup MCP clients after execution
187
- @swarm.cleanup
188
-
189
- # Cleanup observer subscriptions (matches MCP cleanup pattern)
190
- @swarm.cleanup_observers
191
-
192
- # Restore original Fiber storage (preserves parent context for nested swarms)
193
- restore_fiber_storage
194
-
195
- # Reset logging state for next execution if we set it up
196
- reset_logging if has_logging
197
- end
198
-
199
- # Restore Fiber-local storage to original values (preserves parent context)
200
- def restore_fiber_storage
201
- Fiber[:execution_id] = @original_fiber_storage[:execution_id]
202
- Fiber[:swarm_id] = @original_fiber_storage[:swarm_id]
203
- Fiber[:parent_swarm_id] = @original_fiber_storage[:parent_swarm_id]
204
- end
205
-
206
- # Reset logging state
207
- def reset_logging
208
- LogCollector.reset!
209
- LogStream.reset!
210
- end
211
- end
212
- end
213
- end