swarm_sdk 2.7.14 → 3.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) 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/edit.rb +111 -0
  42. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  43. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  44. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  45. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  46. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  47. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  48. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  49. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  50. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  51. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  52. data/lib/swarm_sdk/v3.rb +145 -0
  53. metadata +83 -148
  54. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  55. data/lib/swarm_sdk/agent/builder.rb +0 -705
  56. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  57. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  58. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  59. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  60. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  61. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  62. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  63. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  64. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  65. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  66. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  67. data/lib/swarm_sdk/agent/context.rb +0 -115
  68. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  69. data/lib/swarm_sdk/agent/definition.rb +0 -588
  70. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  71. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  72. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  73. data/lib/swarm_sdk/agent_registry.rb +0 -146
  74. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  75. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  76. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  77. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  78. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  79. data/lib/swarm_sdk/config.rb +0 -368
  80. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  81. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  82. data/lib/swarm_sdk/configuration.rb +0 -165
  83. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  84. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  85. data/lib/swarm_sdk/context_compactor.rb +0 -335
  86. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  87. data/lib/swarm_sdk/context_management/context.rb +0 -328
  88. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  89. data/lib/swarm_sdk/defaults.rb +0 -251
  90. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  91. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  92. data/lib/swarm_sdk/hooks/context.rb +0 -197
  93. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  94. data/lib/swarm_sdk/hooks/error.rb +0 -29
  95. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  96. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  97. data/lib/swarm_sdk/hooks/result.rb +0 -150
  98. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  99. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  100. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  101. data/lib/swarm_sdk/log_collector.rb +0 -227
  102. data/lib/swarm_sdk/log_stream.rb +0 -127
  103. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  104. data/lib/swarm_sdk/model_aliases.json +0 -8
  105. data/lib/swarm_sdk/models.json +0 -44002
  106. data/lib/swarm_sdk/models.rb +0 -161
  107. data/lib/swarm_sdk/node_context.rb +0 -245
  108. data/lib/swarm_sdk/observer/builder.rb +0 -81
  109. data/lib/swarm_sdk/observer/config.rb +0 -45
  110. data/lib/swarm_sdk/observer/manager.rb +0 -248
  111. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  112. data/lib/swarm_sdk/permissions/config.rb +0 -239
  113. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  114. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  115. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  116. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  117. data/lib/swarm_sdk/plugin.rb +0 -309
  118. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  119. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  120. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  121. data/lib/swarm_sdk/restore_result.rb +0 -65
  122. data/lib/swarm_sdk/result.rb +0 -241
  123. data/lib/swarm_sdk/snapshot.rb +0 -156
  124. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  125. data/lib/swarm_sdk/state_restorer.rb +0 -476
  126. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  127. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  128. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  129. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  130. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  131. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  132. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  133. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  134. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  135. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  136. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  137. data/lib/swarm_sdk/swarm.rb +0 -973
  138. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  139. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  140. data/lib/swarm_sdk/tools/base.rb +0 -63
  141. data/lib/swarm_sdk/tools/bash.rb +0 -280
  142. data/lib/swarm_sdk/tools/clock.rb +0 -46
  143. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  144. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  145. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  146. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  147. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  148. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  149. data/lib/swarm_sdk/tools/edit.rb +0 -145
  150. data/lib/swarm_sdk/tools/glob.rb +0 -166
  151. data/lib/swarm_sdk/tools/grep.rb +0 -235
  152. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  153. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  154. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  155. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  156. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  157. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  158. data/lib/swarm_sdk/tools/read.rb +0 -261
  159. data/lib/swarm_sdk/tools/registry.rb +0 -205
  160. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  161. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  163. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  164. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  165. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  166. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  167. data/lib/swarm_sdk/tools/think.rb +0 -100
  168. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  169. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  170. data/lib/swarm_sdk/tools/write.rb +0 -112
  171. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  172. data/lib/swarm_sdk/utils.rb +0 -68
  173. data/lib/swarm_sdk/validation_result.rb +0 -33
  174. data/lib/swarm_sdk/version.rb +0 -5
  175. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  176. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  177. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  178. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  179. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  180. data/lib/swarm_sdk/workflow.rb +0 -589
  181. 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