swarm_sdk 2.7.14 → 3.0.0.alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Global registry for reusable agent definitions
5
- #
6
- # AgentRegistry allows declaring agents in separate files that can be
7
- # referenced by name in swarm definitions. This promotes code reuse and
8
- # separation of concerns - agent definitions can live in dedicated files
9
- # while swarm configurations compose them together.
10
- #
11
- # ## Usage
12
- #
13
- # Register agents globally (typically in separate files):
14
- #
15
- # # agents/backend.rb
16
- # SwarmSDK.agent :backend do
17
- # model "claude-sonnet-4"
18
- # description "Backend API developer"
19
- # system_prompt "You build REST APIs"
20
- # tools :Read, :Edit, :Bash
21
- # end
22
- #
23
- # Reference registered agents in swarm definitions:
24
- #
25
- # # swarm.rb
26
- # SwarmSDK.build do
27
- # name "Dev Team"
28
- # lead :backend
29
- #
30
- # agent :backend # Pulls from registry
31
- # end
32
- #
33
- # ## Override Support
34
- #
35
- # Registered agents can be extended with additional configuration:
36
- #
37
- # SwarmSDK.build do
38
- # name "Dev Team"
39
- # lead :backend
40
- #
41
- # agent :backend do
42
- # # Registry config is applied first, then this block
43
- # tools :CustomTool # Adds to tools from registry
44
- # delegates_to :database
45
- # end
46
- # end
47
- #
48
- # @note This registry is not thread-safe. In multi-threaded environments,
49
- # register all agents before spawning threads, or synchronize access
50
- # externally. For typical fiber-based async usage (the default in SwarmSDK),
51
- # this is not a concern.
52
- #
53
- class AgentRegistry
54
- @agents = {}
55
-
56
- class << self
57
- # Register an agent definition block
58
- #
59
- # Stores a configuration block that will be executed when the agent
60
- # is referenced in a swarm definition. The block receives an
61
- # Agent::Builder context and can use all builder DSL methods.
62
- #
63
- # @param name [Symbol, String] Agent name (will be symbolized)
64
- # @yield Agent configuration block using Agent::Builder DSL
65
- # @return [void]
66
- # @raise [ArgumentError] If no block is provided
67
- # @raise [ArgumentError] If agent with same name is already registered
68
- #
69
- # @example Register a backend agent
70
- # SwarmSDK::AgentRegistry.register(:backend) do
71
- # model "claude-sonnet-4"
72
- # description "Backend developer"
73
- # tools :Read, :Edit, :Bash
74
- # end
75
- #
76
- # @example Register with MCP servers
77
- # SwarmSDK::AgentRegistry.register(:filesystem_agent) do
78
- # model "gpt-4"
79
- # description "File manager"
80
- # mcp_server :fs, type: :stdio, command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem"]
81
- # end
82
- def register(name, &block)
83
- raise ArgumentError, "Block required for agent registration" unless block_given?
84
-
85
- sym_name = name.to_sym
86
- if @agents.key?(sym_name)
87
- raise ArgumentError,
88
- "Agent '#{sym_name}' is already registered. " \
89
- "Use SwarmSDK.clear_agent_registry! to reset, or choose a different name."
90
- end
91
-
92
- @agents[sym_name] = block
93
- end
94
-
95
- # Retrieve a registered agent block
96
- #
97
- # @param name [Symbol, String] Agent name
98
- # @return [Proc, nil] The registration block or nil if not found
99
- #
100
- # @example
101
- # block = SwarmSDK::AgentRegistry.get(:backend)
102
- # builder.instance_eval(&block) if block
103
- def get(name)
104
- @agents[name.to_sym]
105
- end
106
-
107
- # Check if an agent is registered
108
- #
109
- # @param name [Symbol, String] Agent name
110
- # @return [Boolean] true if agent is registered
111
- #
112
- # @example
113
- # if SwarmSDK::AgentRegistry.registered?(:backend)
114
- # puts "Backend agent is available"
115
- # end
116
- def registered?(name)
117
- @agents.key?(name.to_sym)
118
- end
119
-
120
- # List all registered agent names
121
- #
122
- # @return [Array<Symbol>] Names of all registered agents
123
- #
124
- # @example
125
- # SwarmSDK::AgentRegistry.names
126
- # # => [:backend, :frontend, :database]
127
- def names
128
- @agents.keys
129
- end
130
-
131
- # Clear all registrations
132
- #
133
- # Primarily useful for testing to ensure clean state between tests.
134
- #
135
- # @return [void]
136
- #
137
- # @example In test setup/teardown
138
- # def teardown
139
- # SwarmSDK::AgentRegistry.clear
140
- # end
141
- def clear
142
- @agents.clear
143
- end
144
- end
145
- end
146
- end
@@ -1,558 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Builders
5
- # Base builder with shared DSL methods for Swarm and Workflow builders
6
- #
7
- # Provides common functionality:
8
- # - Basic configuration (name, id, scratchpad)
9
- # - Agent definition (inline DSL, markdown files, with overrides)
10
- # - All agents configuration
11
- # - External swarms registry
12
- # - Validation helpers
13
- # - Merging logic
14
- #
15
- # Subclasses must implement:
16
- # - build_swarm - Build and return the appropriate instance
17
- # - Type-specific DSL methods (lead for Swarm, node/start_node for Workflow)
18
- #
19
- class BaseBuilder
20
- def initialize(allow_filesystem_tools: nil)
21
- @swarm_id = nil
22
- @swarm_name = nil
23
- @agents = {}
24
- @all_agents_config = nil
25
- @swarm_registry_config = []
26
- @scratchpad = :disabled
27
- @allow_filesystem_tools = allow_filesystem_tools
28
- end
29
-
30
- # Set swarm ID
31
- #
32
- # @param swarm_id [String] Unique identifier for this swarm/workflow
33
- def id(swarm_id)
34
- @swarm_id = swarm_id
35
- end
36
-
37
- # Set swarm/workflow name
38
- def name(swarm_name)
39
- @swarm_name = swarm_name
40
- end
41
-
42
- # Configure scratchpad mode
43
- #
44
- # For Workflow: :enabled (shared across nodes), :per_node (isolated), or :disabled
45
- # For Swarm: :enabled or :disabled
46
- #
47
- # @param mode [Symbol, Boolean] Scratchpad mode
48
- def scratchpad(mode)
49
- @scratchpad = mode
50
- end
51
-
52
- # Register external swarms for composable swarms
53
- #
54
- # @example
55
- # swarms do
56
- # register "code_review", file: "./swarms/code_review.rb"
57
- # register "testing", file: "./swarms/testing.yml", keep_context: false
58
- # end
59
- #
60
- # @yield Block containing register() calls
61
- def swarms(&block)
62
- builder = Swarm::SwarmRegistryBuilder.new
63
- builder.instance_eval(&block)
64
- @swarm_registry_config = builder.registrations
65
- end
66
-
67
- # Define an agent with fluent API, load from markdown, or reference registry
68
- #
69
- # Supports multiple forms:
70
- # 1. Registry lookup: agent :name (pulls from global registry)
71
- # 2. Registry + overrides: agent :name do ... end (when registered)
72
- # 3. Inline DSL: agent :name do ... end (when not registered)
73
- # 4. Markdown content: agent :name, <<~MD ... MD
74
- # 5. Markdown + overrides: agent :name, <<~MD do ... end
75
- #
76
- # @example Inline DSL
77
- # agent :backend do
78
- # model "gpt-5"
79
- # system_prompt "You build APIs"
80
- # tools :Read, :Write
81
- # end
82
- #
83
- # @example Registry lookup (agent must be registered with SwarmSDK.agent)
84
- # agent :backend # Pulls configuration from registry
85
- #
86
- # @example Registry + overrides
87
- # agent :backend do
88
- # # Base config from registry, then apply overrides
89
- # tools :CustomTool # Adds to registry-defined tools
90
- # end
91
- #
92
- # @example Markdown content
93
- # agent :backend, <<~MD
94
- # ---
95
- # description: "Backend developer"
96
- # model: "gpt-4"
97
- # ---
98
- #
99
- # You build APIs.
100
- # MD
101
- def agent(name, content = nil, &block)
102
- name = name.to_sym
103
-
104
- # Case 1: agent :name, <<~MD do ... end (markdown + overrides)
105
- if content.is_a?(String) && block_given? && markdown_content?(content)
106
- load_agent_from_markdown_with_overrides(content, name, &block)
107
-
108
- # Case 2: agent :name, <<~MD (markdown only)
109
- elsif content.is_a?(String) && !block_given? && markdown_content?(content)
110
- load_agent_from_markdown(content, name)
111
-
112
- # Case 3: agent :name (registry lookup only - no content, no block)
113
- elsif content.nil? && !block_given?
114
- load_agent_from_registry(name)
115
-
116
- # Case 4: agent :name do ... end (with registered agent - registry + overrides)
117
- elsif content.nil? && block_given? && AgentRegistry.registered?(name)
118
- load_agent_from_registry_with_overrides(name, &block)
119
-
120
- # Case 5: agent :name do ... end (inline DSL - not registered)
121
- elsif block_given?
122
- builder = Agent::Builder.new(name)
123
- builder.instance_eval(&block)
124
- @agents[name] = builder
125
-
126
- else
127
- raise ArgumentError,
128
- "Invalid agent definition for '#{name}'. Use:\n " \
129
- "agent :#{name} { ... } # Inline DSL\n " \
130
- "agent :#{name} # Registry lookup\n " \
131
- "agent :#{name} { ... } # Registry + overrides (if registered)\n " \
132
- "agent :#{name}, <<~MD ... MD # Markdown\n " \
133
- "agent :#{name}, <<~MD do ... end # Markdown + overrides"
134
- end
135
- end
136
-
137
- # Configure all agents with a block
138
- #
139
- # @example
140
- # all_agents do
141
- # tools :Read, :Write
142
- #
143
- # hook :pre_tool_use, matcher: "Write" do |ctx|
144
- # # Validation for all agents
145
- # end
146
- # end
147
- def all_agents(&block)
148
- builder = Swarm::AllAgentsBuilder.new
149
- builder.instance_eval(&block)
150
- @all_agents_config = builder
151
- end
152
-
153
- # Build the actual Swarm or Workflow instance
154
- #
155
- # Subclasses must implement this method.
156
- #
157
- # @return [Swarm, Workflow] Configured instance
158
- def build_swarm
159
- raise NotImplementedError, "#{self.class} must implement #build_swarm"
160
- end
161
-
162
- protected
163
-
164
- # Check if a string is markdown content (has frontmatter)
165
- #
166
- # @param str [String] String to check
167
- # @return [Boolean] true if string contains markdown frontmatter
168
- def markdown_content?(str)
169
- str.start_with?("---") || str.include?("\n---\n")
170
- end
171
-
172
- # Load an agent from the global registry
173
- #
174
- # Retrieves the registered agent block and executes it in the context
175
- # of a new Agent::Builder.
176
- #
177
- # @param name [Symbol] Agent name
178
- # @return [void]
179
- # @raise [ConfigurationError] If agent is not registered
180
- #
181
- # @example
182
- # load_agent_from_registry(:backend)
183
- def load_agent_from_registry(name)
184
- registered_proc = AgentRegistry.get(name)
185
- unless registered_proc
186
- raise ConfigurationError,
187
- "Agent '#{name}' not found in registry. " \
188
- "Either define inline with `agent :#{name} do ... end` or " \
189
- "register globally with `SwarmSDK.agent :#{name} do ... end`"
190
- end
191
-
192
- builder = Agent::Builder.new(name)
193
- builder.instance_eval(&registered_proc)
194
- @agents[name] = builder
195
- end
196
-
197
- # Load an agent from the registry with additional overrides
198
- #
199
- # Applies the registered configuration first, then executes the
200
- # override block to customize the agent.
201
- #
202
- # @param name [Symbol] Agent name
203
- # @yield Override block with additional configuration
204
- # @return [void]
205
- #
206
- # @example
207
- # load_agent_from_registry_with_overrides(:backend) do
208
- # tools :CustomTool # Adds to registry-defined tools
209
- # end
210
- def load_agent_from_registry_with_overrides(name, &override_block)
211
- registered_proc = AgentRegistry.get(name)
212
- # Guaranteed to exist since we checked in the condition
213
-
214
- builder = Agent::Builder.new(name)
215
- builder.instance_eval(&registered_proc) # Base config from registry
216
- builder.instance_eval(&override_block) # Apply overrides
217
- @agents[name] = builder
218
- end
219
-
220
- # Load an agent from markdown content
221
- #
222
- # Returns a hash of the agent config (not a Definition yet) so that
223
- # all_agents config can be applied later in the build process.
224
- #
225
- # @param content [String] Markdown content with frontmatter
226
- # @param name_override [Symbol, nil] Optional name to override frontmatter name
227
- # @return [void]
228
- def load_agent_from_markdown(content, name_override = nil)
229
- definition = MarkdownParser.parse(content, name_override)
230
- @agents[definition.name] = { __file_config__: definition.to_h }
231
- end
232
-
233
- # Load an agent from markdown content with DSL overrides
234
- #
235
- # @param content [String] Markdown content with frontmatter
236
- # @param name_override [Symbol, nil] Optional name to override frontmatter name
237
- # @yield Block with DSL overrides
238
- # @return [void]
239
- def load_agent_from_markdown_with_overrides(content, name_override = nil, &block)
240
- definition = MarkdownParser.parse(content, name_override)
241
-
242
- builder = Agent::Builder.new(definition.name)
243
- apply_definition_to_builder(builder, definition.to_h)
244
- builder.instance_eval(&block)
245
-
246
- @agents[definition.name] = builder
247
- end
248
-
249
- # Apply agent definition hash to a builder
250
- #
251
- # @param builder [Agent::Builder] Builder to configure
252
- # @param config [Hash] Configuration hash from definition
253
- # @return [void]
254
- def apply_definition_to_builder(builder, config)
255
- builder.description(config[:description]) if config[:description]
256
- builder.model(config[:model]) if config[:model]
257
- builder.provider(config[:provider]) if config[:provider]
258
- builder.base_url(config[:base_url]) if config[:base_url]
259
- builder.api_version(config[:api_version]) if config[:api_version]
260
- builder.context_window(config[:context_window]) if config[:context_window]
261
- builder.system_prompt(config[:system_prompt]) if config[:system_prompt]
262
- builder.directory(config[:directory]) if config[:directory]
263
- builder.request_timeout(config[:request_timeout]) if config[:request_timeout]
264
- builder.turn_timeout(config[:turn_timeout]) if config[:turn_timeout]
265
- builder.parameters(config[:parameters]) if config[:parameters]
266
- builder.headers(config[:headers]) if config[:headers]
267
- builder.coding_agent(config[:coding_agent]) unless config[:coding_agent].nil?
268
- builder.disable_environment_info(config[:disable_environment_info]) unless config[:disable_environment_info].nil?
269
- builder.bypass_permissions(config[:bypass_permissions]) if config[:bypass_permissions]
270
- builder.disable_default_tools(config[:disable_default_tools]) unless config[:disable_default_tools].nil?
271
-
272
- # Add tools from markdown
273
- if config[:tools]&.any?
274
- tool_names = config[:tools].map do |tool|
275
- tool.is_a?(Hash) ? tool[:name] : tool
276
- end
277
- builder.tools(*tool_names)
278
- end
279
-
280
- # Add delegates_to (handle both array and hash formats)
281
- if config[:delegates_to]&.any?
282
- delegation_config = config[:delegates_to]
283
- if delegation_config.is_a?(Hash)
284
- # Hash format: pass as single argument
285
- builder.delegates_to(delegation_config)
286
- elsif delegation_config.is_a?(Array)
287
- # Array format: splat the array
288
- builder.delegates_to(*delegation_config)
289
- end
290
- end
291
-
292
- # Add MCP servers
293
- config[:mcp_servers]&.each do |server|
294
- builder.mcp_server(server[:name], **server.except(:name))
295
- end
296
- end
297
-
298
- # Merge all_agents configuration into each agent
299
- #
300
- # All_agents values are used as defaults - agent-specific values override.
301
- # This applies to both inline DSL agents (Builder) and file-loaded agents (config hash).
302
- #
303
- # @return [void]
304
- def merge_all_agents_config_into_agents
305
- return unless @all_agents_config
306
-
307
- all_agents_hash = @all_agents_config.to_h
308
-
309
- @agents.each_value do |agent_builder_or_config|
310
- if agent_builder_or_config.is_a?(Hash) && agent_builder_or_config.key?(:__file_config__)
311
- # File-loaded agent - merge into the config hash
312
- file_config = agent_builder_or_config[:__file_config__]
313
- merged_config = merge_all_agents_into_config(all_agents_hash, file_config)
314
- agent_builder_or_config[:__file_config__] = merged_config
315
- else
316
- # Builder object (inline DSL agent)
317
- agent_builder = agent_builder_or_config
318
-
319
- apply_all_agents_defaults(agent_builder, all_agents_hash)
320
-
321
- # Merge tools (prepend all_agents tools)
322
- all_agents_tools = @all_agents_config.tools_list
323
- agent_builder.prepend_tools(*all_agents_tools) if all_agents_tools.any?
324
-
325
- # Pass all_agents permissions as default_permissions
326
- if @all_agents_config.permissions_config.any?
327
- agent_builder.default_permissions = @all_agents_config.permissions_config
328
- end
329
- end
330
- end
331
- end
332
-
333
- # Merge all_agents config into file-loaded agent config
334
- #
335
- # @param all_agents_hash [Hash] All_agents configuration
336
- # @param file_config [Hash] File-loaded agent configuration
337
- # @return [Hash] Merged configuration
338
- def merge_all_agents_into_config(all_agents_hash, file_config)
339
- merged = all_agents_hash.dup
340
-
341
- file_config.each do |key, value|
342
- case key
343
- when :tools
344
- merged[:tools] = Array(merged[:tools]) + Array(value)
345
- when :delegates_to
346
- # Handle merging delegation configs (can be array or hash)
347
- existing = merged[:delegates_to] || []
348
- new_value = value || []
349
-
350
- # Convert both to array of delegation configs for merging
351
- existing_array = normalize_delegation_array(existing)
352
- new_array = normalize_delegation_array(new_value)
353
-
354
- merged[:delegates_to] = existing_array + new_array
355
- when :parameters
356
- merged[:parameters] = (merged[:parameters] || {}).merge(value || {})
357
- when :headers
358
- merged[:headers] = (merged[:headers] || {}).merge(value || {})
359
- when :turn_timeout
360
- # Agent-specific turn_timeout overrides all_agents
361
- merged[key] = value
362
- when :request_timeout
363
- # Agent-specific request_timeout overrides all_agents
364
- merged[key] = value
365
- else
366
- merged[key] = value
367
- end
368
- end
369
-
370
- # Pass all_agents permissions as default_permissions
371
- if all_agents_hash[:permissions] && !merged[:default_permissions]
372
- merged[:default_permissions] = all_agents_hash[:permissions]
373
- end
374
-
375
- merged
376
- end
377
-
378
- # Normalize delegation config to array of hashes format
379
- #
380
- # Converts various delegation formats to normalized array for merging:
381
- # - Array of symbols: [:frontend, :backend] → [{agent: :frontend, tool_name: nil}, ...]
382
- # - Hash: {frontend: "Custom"} → [{agent: :frontend, tool_name: "Custom"}, ...]
383
- # - Array of hashes: [{agent: :frontend, tool_name: "Custom"}] → unchanged
384
- #
385
- # @param delegation_config [Array, Hash] Delegation configuration
386
- # @return [Array<Hash>] Normalized array of {agent:, tool_name:} hashes
387
- def normalize_delegation_array(delegation_config)
388
- return [] if delegation_config.nil? || (delegation_config.respond_to?(:empty?) && delegation_config.empty?)
389
-
390
- case delegation_config
391
- when Array
392
- delegation_config.map do |item|
393
- case item
394
- when Symbol, String
395
- { agent: item.to_sym, tool_name: nil }
396
- when Hash
397
- item.key?(:agent) ? item : item.map { |agent, tool_name| { agent: agent.to_sym, tool_name: tool_name } }
398
- end
399
- end.flatten
400
- when Hash
401
- delegation_config.map { |agent, tool_name| { agent: agent.to_sym, tool_name: tool_name } }
402
- else
403
- []
404
- end
405
- end
406
-
407
- # Apply all_agents defaults to an agent builder
408
- #
409
- # @param agent_builder [Agent::Builder] The agent builder to configure
410
- # @param all_agents_hash [Hash] All_agents configuration
411
- # @return [void]
412
- def apply_all_agents_defaults(agent_builder, all_agents_hash)
413
- if all_agents_hash[:model] && !agent_builder.model_set?
414
- agent_builder.model(all_agents_hash[:model])
415
- end
416
-
417
- if all_agents_hash[:provider] && !agent_builder.provider_set?
418
- agent_builder.provider(all_agents_hash[:provider])
419
- end
420
-
421
- if all_agents_hash[:base_url] && !agent_builder.base_url_set?
422
- agent_builder.base_url(all_agents_hash[:base_url])
423
- end
424
-
425
- if all_agents_hash[:api_version] && !agent_builder.api_version_set?
426
- agent_builder.api_version(all_agents_hash[:api_version])
427
- end
428
-
429
- if all_agents_hash[:request_timeout] && !agent_builder.request_timeout_set?
430
- agent_builder.request_timeout(all_agents_hash[:request_timeout])
431
- end
432
-
433
- if all_agents_hash[:turn_timeout] && !agent_builder.turn_timeout_set?
434
- agent_builder.turn_timeout(all_agents_hash[:turn_timeout])
435
- end
436
-
437
- if all_agents_hash[:parameters]
438
- merged_params = all_agents_hash[:parameters].merge(agent_builder.parameters)
439
- agent_builder.parameters(merged_params)
440
- end
441
-
442
- if all_agents_hash[:headers]
443
- merged_headers = all_agents_hash[:headers].merge(agent_builder.headers)
444
- agent_builder.headers(merged_headers)
445
- end
446
-
447
- if !all_agents_hash[:coding_agent].nil? && !agent_builder.coding_agent_set?
448
- agent_builder.coding_agent(all_agents_hash[:coding_agent])
449
- end
450
-
451
- if !all_agents_hash[:streaming].nil? && !agent_builder.streaming_set?
452
- agent_builder.streaming(all_agents_hash[:streaming])
453
- end
454
-
455
- if !all_agents_hash[:disable_environment_info].nil? && !agent_builder.disable_environment_info_set?
456
- agent_builder.disable_environment_info(all_agents_hash[:disable_environment_info])
457
- end
458
-
459
- if all_agents_hash[:thinking] && !agent_builder.thinking_set?
460
- agent_builder.thinking(**all_agents_hash[:thinking])
461
- end
462
- end
463
-
464
- # Validate all_agents filesystem tools
465
- #
466
- # @raise [ConfigurationError] If filesystem tools are disabled and all_agents has them
467
- # @return [void]
468
- def validate_all_agents_filesystem_tools
469
- resolved_setting = if @allow_filesystem_tools.nil?
470
- SwarmSDK.config.allow_filesystem_tools
471
- else
472
- @allow_filesystem_tools
473
- end
474
-
475
- return if resolved_setting
476
- return unless @all_agents_config&.tools_list&.any?
477
-
478
- forbidden = @all_agents_config.tools_list.select do |tool|
479
- SwarmSDK::Swarm::ToolConfigurator::FILESYSTEM_TOOLS.include?(tool.to_sym)
480
- end
481
-
482
- return if forbidden.empty?
483
-
484
- raise ConfigurationError,
485
- "Filesystem tools are globally disabled (SwarmSDK.config.allow_filesystem_tools = false) " \
486
- "but all_agents configuration includes: #{forbidden.join(", ")}.\n\n" \
487
- "This is a system-wide security setting that cannot be overridden by swarm configuration.\n" \
488
- "To use filesystem tools, set SwarmSDK.config.allow_filesystem_tools = true before loading the swarm."
489
- end
490
-
491
- # Validate individual agent filesystem tools
492
- #
493
- # @raise [ConfigurationError] If filesystem tools are disabled and any agent has them
494
- # @return [void]
495
- def validate_agent_filesystem_tools
496
- resolved_setting = if @allow_filesystem_tools.nil?
497
- SwarmSDK.config.allow_filesystem_tools
498
- else
499
- @allow_filesystem_tools
500
- end
501
-
502
- return if resolved_setting
503
-
504
- @agents.each do |agent_name, agent_builder_or_config|
505
- tools_list = if agent_builder_or_config.is_a?(Hash) && agent_builder_or_config.key?(:__file_config__)
506
- agent_builder_or_config[:__file_config__][:tools] || []
507
- elsif agent_builder_or_config.is_a?(Agent::Builder)
508
- agent_builder_or_config.tools_list
509
- else
510
- []
511
- end
512
-
513
- tool_names = tools_list.map do |tool|
514
- name = tool.is_a?(Hash) ? tool[:name] : tool
515
- name.to_sym
516
- end
517
-
518
- forbidden = tool_names.select do |tool|
519
- SwarmSDK::Swarm::ToolConfigurator::FILESYSTEM_TOOLS.include?(tool)
520
- end
521
-
522
- next if forbidden.empty?
523
-
524
- raise ConfigurationError,
525
- "Filesystem tools are globally disabled (SwarmSDK.config.allow_filesystem_tools = false) " \
526
- "but agent '#{agent_name}' attempts to use: #{forbidden.join(", ")}.\n\n" \
527
- "This is a system-wide security setting that cannot be overridden by swarm configuration.\n" \
528
- "To use filesystem tools, set SwarmSDK.config.allow_filesystem_tools = true before loading the swarm."
529
- end
530
- end
531
-
532
- # Build agent definitions from builders or file configs
533
- #
534
- # Handles both Agent::Builder (inline DSL) and file configs (from files).
535
- # Merges all_agents config before building.
536
- #
537
- # @return [Hash<Symbol, Agent::Definition>] Agent definitions
538
- def build_agent_definitions
539
- # Merge all_agents config first
540
- merge_all_agents_config_into_agents if @all_agents_config
541
-
542
- # Build definitions
543
- agent_definitions = {}
544
- @agents.each do |agent_name, agent_builder_or_config|
545
- agent_definitions[agent_name] = if agent_builder_or_config.is_a?(Hash) && agent_builder_or_config.key?(:__file_config__)
546
- # File-loaded agent config (with all_agents merged)
547
- Agent::Definition.new(agent_name, agent_builder_or_config[:__file_config__])
548
- else
549
- # Builder object (from inline DSL) - convert to definition
550
- agent_builder_or_config.to_definition
551
- end
552
- end
553
-
554
- agent_definitions
555
- end
556
- end
557
- end
558
- end