swarm_memory 2.1.4 → 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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. data/lib/swarm_memory.rb +7 -2
  4. metadata +6 -185
  5. data/lib/claude_swarm/base_executor.rb +0 -133
  6. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  7. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  8. data/lib/claude_swarm/cli.rb +0 -697
  9. data/lib/claude_swarm/commands/ps.rb +0 -215
  10. data/lib/claude_swarm/commands/show.rb +0 -139
  11. data/lib/claude_swarm/configuration.rb +0 -373
  12. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  13. data/lib/claude_swarm/json_handler.rb +0 -91
  14. data/lib/claude_swarm/mcp_generator.rb +0 -243
  15. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  16. data/lib/claude_swarm/openai/executor.rb +0 -256
  17. data/lib/claude_swarm/openai/responses.rb +0 -319
  18. data/lib/claude_swarm/orchestrator.rb +0 -878
  19. data/lib/claude_swarm/process_tracker.rb +0 -78
  20. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  21. data/lib/claude_swarm/session_path.rb +0 -42
  22. data/lib/claude_swarm/settings_generator.rb +0 -77
  23. data/lib/claude_swarm/system_utils.rb +0 -46
  24. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  25. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  27. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  28. data/lib/claude_swarm/version.rb +0 -5
  29. data/lib/claude_swarm/worktree_manager.rb +0 -475
  30. data/lib/claude_swarm/yaml_loader.rb +0 -22
  31. data/lib/claude_swarm.rb +0 -67
  32. data/lib/swarm_cli/cli.rb +0 -201
  33. data/lib/swarm_cli/command_registry.rb +0 -61
  34. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  35. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  36. data/lib/swarm_cli/commands/migrate.rb +0 -55
  37. data/lib/swarm_cli/commands/run.rb +0 -173
  38. data/lib/swarm_cli/config_loader.rb +0 -98
  39. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  40. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  41. data/lib/swarm_cli/interactive_repl.rb +0 -924
  42. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  43. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  44. data/lib/swarm_cli/migrate_options.rb +0 -54
  45. data/lib/swarm_cli/migrator.rb +0 -132
  46. data/lib/swarm_cli/options.rb +0 -151
  47. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  48. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  49. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  50. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  51. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  52. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  53. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  54. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  55. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  56. data/lib/swarm_cli/ui/icons.rb +0 -36
  57. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  58. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  59. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  60. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  61. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  62. data/lib/swarm_cli/version.rb +0 -5
  63. data/lib/swarm_cli.rb +0 -46
  64. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  65. data/lib/swarm_sdk/agent/builder.rb +0 -552
  66. data/lib/swarm_sdk/agent/chat.rb +0 -774
  67. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  68. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  69. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  70. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  71. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  72. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  73. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  75. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  76. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  77. data/lib/swarm_sdk/agent/context.rb +0 -116
  78. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  79. data/lib/swarm_sdk/agent/definition.rb +0 -477
  80. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  81. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  82. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  83. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  84. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  85. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  86. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  87. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  88. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  89. data/lib/swarm_sdk/configuration.rb +0 -135
  90. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  91. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  92. data/lib/swarm_sdk/context_compactor.rb +0 -335
  93. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  94. data/lib/swarm_sdk/context_management/context.rb +0 -328
  95. data/lib/swarm_sdk/defaults.rb +0 -196
  96. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  97. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  98. data/lib/swarm_sdk/hooks/context.rb +0 -197
  99. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  100. data/lib/swarm_sdk/hooks/error.rb +0 -29
  101. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  102. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  103. data/lib/swarm_sdk/hooks/result.rb +0 -150
  104. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  105. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  106. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  107. data/lib/swarm_sdk/log_collector.rb +0 -227
  108. data/lib/swarm_sdk/log_stream.rb +0 -127
  109. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  110. data/lib/swarm_sdk/model_aliases.json +0 -8
  111. data/lib/swarm_sdk/models.json +0 -1
  112. data/lib/swarm_sdk/models.rb +0 -120
  113. data/lib/swarm_sdk/node_context.rb +0 -245
  114. data/lib/swarm_sdk/observer/builder.rb +0 -81
  115. data/lib/swarm_sdk/observer/config.rb +0 -45
  116. data/lib/swarm_sdk/observer/manager.rb +0 -236
  117. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  118. data/lib/swarm_sdk/permissions/config.rb +0 -239
  119. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  120. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  121. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  122. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  123. data/lib/swarm_sdk/plugin.rb +0 -309
  124. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  125. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  126. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  127. data/lib/swarm_sdk/restore_result.rb +0 -65
  128. data/lib/swarm_sdk/result.rb +0 -123
  129. data/lib/swarm_sdk/snapshot.rb +0 -156
  130. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  131. data/lib/swarm_sdk/state_restorer.rb +0 -476
  132. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  133. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  134. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  135. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  136. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  137. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  138. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  139. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  140. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  141. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  142. data/lib/swarm_sdk/swarm.rb +0 -717
  143. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  144. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  145. data/lib/swarm_sdk/tools/bash.rb +0 -282
  146. data/lib/swarm_sdk/tools/clock.rb +0 -44
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  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 -163
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  160. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  161. data/lib/swarm_sdk/tools/read.rb +0 -261
  162. data/lib/swarm_sdk/tools/registry.rb +0 -205
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  166. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  167. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  168. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  169. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  170. data/lib/swarm_sdk/tools/think.rb +0 -98
  171. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  172. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  173. data/lib/swarm_sdk/tools/write.rb +0 -112
  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 -79
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  182. data/lib/swarm_sdk/workflow.rb +0 -554
  183. data/lib/swarm_sdk.rb +0 -524
  184. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -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