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,705 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Agent
5
- # Builder provides fluent API for configuring agents
6
- #
7
- # This class offers a Ruby DSL for defining agents with a clean, readable syntax.
8
- # It collects configuration and then adds the agent to the swarm.
9
- #
10
- # @example
11
- # agent :backend do
12
- # model "gpt-5"
13
- # prompt "You build APIs"
14
- # tools :Read, :Write, :Bash
15
- #
16
- # hook :pre_tool_use, matcher: "Bash" do |ctx|
17
- # SwarmSDK::Hooks::Result.halt("Blocked!") if dangerous?(ctx)
18
- # end
19
- # end
20
- class Builder
21
- # Expose default_permissions for Swarm::Builder to set from all_agents
22
- attr_writer :default_permissions
23
-
24
- # Expose mcp_servers for tests
25
- attr_reader :mcp_servers
26
-
27
- # Get tools list as array for validation
28
- #
29
- # @return [Array<Symbol>] List of tools
30
- def tools_list
31
- @tools.to_a
32
- end
33
-
34
- def initialize(name)
35
- @name = name
36
- @description = nil
37
- @model = "gpt-5"
38
- @provider = nil
39
- @base_url = nil
40
- @api_version = nil
41
- @context_window = nil
42
- @system_prompt = nil
43
- # Use Set for tools to automatically handle duplicates when tools() is called multiple times.
44
- # This ensures that if someone does: tools :Read; tools :Write; tools :Read
45
- # the final set contains only [:Read, :Write] without duplicates.
46
- # We convert to Array in to_definition for compatibility with Agent::Definition.
47
- @tools = Set.new
48
- @delegates_to = []
49
- @directory = "."
50
- @parameters = {}
51
- @headers = {}
52
- @request_timeout = nil
53
- @turn_timeout = nil
54
- @mcp_servers = []
55
- @disable_default_tools = nil # nil = include all default tools
56
- @bypass_permissions = false
57
- @coding_agent = nil # nil = not set (will default to false in Definition)
58
- @assume_model_exists = nil
59
- @hooks = []
60
- @permissions_config = {}
61
- @default_permissions = {} # Set by SwarmBuilder from all_agents
62
- @memory_config = nil
63
- @shared_across_delegations = nil # nil = not set (will default to false in Definition)
64
- @streaming = nil # nil = not set (will use global config default)
65
- @thinking = nil # nil = not set (extended thinking disabled)
66
- @disable_environment_info = nil # nil = not set (will default to false in Definition)
67
- @context_management_config = nil # Context management DSL hooks
68
- end
69
-
70
- # Set/get agent model
71
- def model(model_name = :__not_provided__)
72
- return @model if model_name == :__not_provided__
73
-
74
- @model = model_name
75
- end
76
-
77
- # Set/get provider
78
- def provider(provider_name = :__not_provided__)
79
- return @provider if provider_name == :__not_provided__
80
-
81
- @provider = provider_name
82
- end
83
-
84
- # Set/get base URL
85
- def base_url(url = :__not_provided__)
86
- return @base_url if url == :__not_provided__
87
-
88
- @base_url = url
89
- end
90
-
91
- # Set/get API version (OpenAI-compatible providers only)
92
- def api_version(version = :__not_provided__)
93
- return @api_version if version == :__not_provided__
94
-
95
- @api_version = version
96
- end
97
-
98
- # Set/get explicit context window override
99
- def context_window(tokens = :__not_provided__)
100
- return @context_window if tokens == :__not_provided__
101
-
102
- @context_window = tokens
103
- end
104
-
105
- # Set/get LLM parameters
106
- def parameters(params = :__not_provided__)
107
- return @parameters if params == :__not_provided__
108
-
109
- @parameters = params
110
- end
111
-
112
- # Set/get custom HTTP headers
113
- def headers(header_hash = :__not_provided__)
114
- return @headers if header_hash == :__not_provided__
115
-
116
- @headers = header_hash
117
- end
118
-
119
- # Set/get request timeout
120
- def request_timeout(seconds = :__not_provided__)
121
- return @request_timeout if seconds == :__not_provided__
122
-
123
- @request_timeout = seconds
124
- end
125
-
126
- # Set/get turn timeout
127
- def turn_timeout(seconds = :__not_provided__)
128
- return @turn_timeout if seconds == :__not_provided__
129
-
130
- @turn_timeout = seconds
131
- end
132
-
133
- # Add an MCP server configuration
134
- #
135
- # @param name [Symbol] Server name
136
- # @param type [Symbol] Transport type (:stdio, :sse, :http)
137
- # @param tools [Array<Symbol>, nil] Tool names to expose (nil = discover all tools)
138
- # @param options [Hash] Transport-specific options
139
- #
140
- # @example stdio transport with discovery
141
- # mcp_server :filesystem, type: :stdio, command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem"]
142
- #
143
- # @example stdio transport with filtered tools (faster boot)
144
- # mcp_server :codebase, type: :stdio, command: "mcp-server-codebase", tools: [:search_code, :list_files]
145
- #
146
- # @example SSE transport
147
- # mcp_server :web, type: :sse, url: "https://example.com/mcp", headers: { authorization: "Bearer token" }
148
- #
149
- # @example HTTP/streamable transport
150
- # mcp_server :api, type: :http, url: "https://api.example.com/mcp", timeout: 60
151
- def mcp_server(name, **options)
152
- server_config = { name: name }.merge(options)
153
- @mcp_servers << server_config
154
- end
155
-
156
- # Disable default tools
157
- #
158
- # @param value [Boolean, Array<Symbol>]
159
- # - true: Disable ALL default tools
160
- # - Array of symbols: Disable specific tools (e.g., [:Think, :TodoWrite])
161
- #
162
- # @example Disable all default tools
163
- # disable_default_tools true
164
- #
165
- # @example Disable specific tools (array)
166
- # disable_default_tools [:Think, :TodoWrite]
167
- #
168
- # @example Disable specific tools (separate arguments)
169
- # disable_default_tools :Think, :TodoWrite
170
- def disable_default_tools(*tools)
171
- # Handle different argument forms
172
- @disable_default_tools = case tools.size
173
- when 0
174
- nil
175
- when 1
176
- # Single argument: could be true/false/array
177
- tools.first
178
- else
179
- # Multiple arguments: treat as array of tool names
180
- tools.map(&:to_sym)
181
- end
182
- end
183
-
184
- # Set bypass_permissions flag
185
- def bypass_permissions(enabled)
186
- @bypass_permissions = enabled
187
- end
188
-
189
- # Set coding_agent flag
190
- #
191
- # When true, includes the base system prompt for coding tasks.
192
- # When false (default), uses only the custom system prompt.
193
- #
194
- # @param enabled [Boolean] Whether to include base coding prompt
195
- # @return [void]
196
- #
197
- # @example
198
- # coding_agent true # Include base prompt for coding tasks
199
- def coding_agent(enabled)
200
- @coding_agent = enabled
201
- end
202
-
203
- # Set assume_model_exists flag
204
- def assume_model_exists(enabled)
205
- @assume_model_exists = enabled
206
- end
207
-
208
- # Set system prompt (matches YAML key)
209
- def system_prompt(text)
210
- @system_prompt = text
211
- end
212
-
213
- # Set description
214
- def description(text)
215
- @description = text
216
- end
217
-
218
- # Set or add tools
219
- #
220
- # Uses Set internally to automatically deduplicate tool names across multiple calls.
221
- # This allows calling tools() multiple times without worrying about duplicates.
222
- #
223
- # @param tool_names [Array<Symbol>] Tool names to add
224
- # @param include_default [Boolean] Whether to include default tools (Read, Grep, etc.)
225
- # @param replace [Boolean] If true, replaces existing tools instead of merging (default: false)
226
- #
227
- # @example Basic usage with defaults
228
- # tools :Grep, :Read # include_default: true is implicit
229
- #
230
- # @example Explicit tools only, no defaults
231
- # tools :Grep, :Read, include_default: false
232
- #
233
- # @example Multiple calls (cumulative, automatic deduplication)
234
- # tools :Read
235
- # tools :Write, :Edit # @tools now contains Set[:Read, :Write, :Edit]
236
- # tools :Read # Still Set[:Read, :Write, :Edit] - no duplicate
237
- #
238
- # @example Replace tools (for markdown overrides)
239
- # tools :Read, :Write, replace: true # Replaces all existing tools
240
- def tools(*tool_names, include_default: true, replace: false)
241
- @tools = Set.new if replace
242
- @tools.merge(tool_names.map(&:to_sym))
243
- # When include_default is false, disable all default tools
244
- @disable_default_tools = true unless include_default
245
- end
246
-
247
- # Add tools from all_agents configuration
248
- #
249
- # Used by Swarm::Builder to add all_agents tools.
250
- # Since we use Set, order doesn't matter and duplicates are handled automatically.
251
- #
252
- # @param tool_names [Array] Tool names to add
253
- # @return [void]
254
- def prepend_tools(*tool_names)
255
- @tools.merge(tool_names.map(&:to_sym))
256
- end
257
-
258
- # Set directory
259
- def directory(dir)
260
- @directory = dir
261
- end
262
-
263
- # Set delegation targets
264
- #
265
- # Supports multiple formats for flexibility:
266
- #
267
- # @example Simple array (backwards compatible)
268
- # delegates_to :frontend, :backend, :qa
269
- #
270
- # @example Hash with custom tool names
271
- # delegates_to frontend: "AskFrontend",
272
- # backend: "GetBackendHelp",
273
- # qa: "RequestReview"
274
- #
275
- # @example Mixed - some auto, some custom
276
- # delegates_to :frontend,
277
- # backend: "GetBackendHelp",
278
- # :qa
279
- #
280
- # @example With delegation options (preserve_context controls context persistence)
281
- # delegates_to :frontend,
282
- # { agent: :backend, tool_name: "AskBackend", preserve_context: false }
283
- #
284
- # @param agent_names_and_options [Array<Symbol, Hash>] Agent names and/or hash with custom tool names
285
- # @return [void]
286
- def delegates_to(*agent_names_and_options)
287
- agent_names_and_options.each do |item|
288
- case item
289
- when Symbol, String
290
- # Simple format: :frontend
291
- @delegates_to << { agent: item.to_sym, tool_name: nil, preserve_context: true }
292
- when Hash
293
- if item.key?(:agent)
294
- # Full config format: { agent: :backend, tool_name: "Custom", preserve_context: false }
295
- @delegates_to << {
296
- agent: item[:agent].to_sym,
297
- tool_name: item[:tool_name],
298
- preserve_context: item.fetch(:preserve_context, true),
299
- }
300
- else
301
- # Hash format: { frontend: "AskFrontend", backend: nil }
302
- item.each do |agent, tool_name|
303
- @delegates_to << { agent: agent.to_sym, tool_name: tool_name, preserve_context: true }
304
- end
305
- end
306
- else
307
- raise ConfigurationError, "delegates_to accepts Symbols or Hashes, got #{item.class}"
308
- end
309
- end
310
- end
311
-
312
- # Add a hook (Ruby block OR shell command)
313
- #
314
- # @example Ruby block
315
- # hook :pre_tool_use, matcher: "Bash" do |ctx|
316
- # HookResult.halt("Blocked") if dangerous?(ctx)
317
- # end
318
- #
319
- # @example Shell command
320
- # hook :pre_tool_use, matcher: "Bash", command: "validate.sh"
321
- def hook(event, matcher: nil, command: nil, timeout: nil, &block)
322
- @hooks << {
323
- event: event,
324
- matcher: matcher,
325
- command: command,
326
- timeout: timeout,
327
- block: block,
328
- }
329
- end
330
-
331
- # Configure permissions for this agent
332
- #
333
- # @example
334
- # permissions do
335
- # Write.allow_paths "backend/**/*"
336
- # Write.deny_paths "backend/secrets/**"
337
- # end
338
- def permissions(&block)
339
- @permissions_config = PermissionsBuilder.build(&block)
340
- end
341
-
342
- # Configure delegation isolation mode
343
- #
344
- # @param enabled [Boolean] If true, allows sharing instances across delegations (old behavior)
345
- # If false (default), creates isolated instances per delegation
346
- # @return [self] Returns self for method chaining
347
- #
348
- # @example
349
- # shared_across_delegations true # Allow sharing (old behavior)
350
- def shared_across_delegations(enabled)
351
- @shared_across_delegations = enabled
352
- self
353
- end
354
-
355
- # Enable or disable streaming for LLM API responses
356
- #
357
- # @param value [Boolean] If true (default), enables streaming; if false, disables it
358
- # @return [self] Returns self for method chaining
359
- #
360
- # @example Enable streaming (default)
361
- # streaming true
362
- #
363
- # @example Disable streaming
364
- # streaming false
365
- def streaming(value = true)
366
- @streaming = value
367
- self
368
- end
369
-
370
- # Check if streaming has been explicitly set
371
- #
372
- # @return [Boolean] true if streaming was explicitly set, false otherwise
373
- def streaming_set?
374
- !@streaming.nil?
375
- end
376
-
377
- # Configure extended thinking for this agent
378
- #
379
- # Extended thinking allows models to reason through complex problems before responding.
380
- # For Anthropic models, specify a budget (token count). For OpenAI models, specify effort.
381
- # Both can be specified for cross-provider compatibility.
382
- #
383
- # @param effort [Symbol, String, nil] Reasoning effort level (:low, :medium, :high) — used by OpenAI
384
- # @param budget [Integer, nil] Token budget for thinking — used by Anthropic
385
- # @return [self] Returns self for method chaining
386
- #
387
- # @example Anthropic thinking with budget
388
- # thinking budget: 10_000
389
- #
390
- # @example OpenAI reasoning effort
391
- # thinking effort: :high
392
- #
393
- # @example Cross-provider (both)
394
- # thinking effort: :high, budget: 10_000
395
- def thinking(effort: nil, budget: nil)
396
- raise ArgumentError, "thinking requires :effort or :budget" if effort.nil? && budget.nil?
397
-
398
- @thinking = { effort: effort, budget: budget }.compact
399
- self
400
- end
401
-
402
- # Check if thinking has been explicitly set
403
- #
404
- # @return [Boolean] true if thinking was explicitly configured
405
- def thinking_set?
406
- !@thinking.nil?
407
- end
408
-
409
- # Configure context management handlers
410
- #
411
- # Define custom handlers for context warning thresholds (60%, 80%, 90%).
412
- # Handlers receive a rich context object with message manipulation methods.
413
- # When a custom handler is registered, automatic compression is disabled
414
- # for that threshold, giving full control to the handler.
415
- #
416
- # @yield Context management DSL block
417
- # @return [void]
418
- #
419
- # @example Basic compression at 60%
420
- # context_management do
421
- # on :warning_60 do |ctx|
422
- # ctx.compress_tool_results(keep_recent: 10)
423
- # end
424
- # end
425
- #
426
- # @example Multiple thresholds with different strategies
427
- # context_management do
428
- # on :warning_60 do |ctx|
429
- # ctx.compress_tool_results(keep_recent: 15, truncate_to: 500)
430
- # end
431
- #
432
- # on :warning_80 do |ctx|
433
- # ctx.prune_old_messages(keep_recent: 30)
434
- # ctx.compress_tool_results(keep_recent: 5, truncate_to: 200)
435
- # end
436
- #
437
- # on :warning_90 do |ctx|
438
- # ctx.log_action("emergency_pruning", remaining: ctx.tokens_remaining)
439
- # ctx.prune_old_messages(keep_recent: 15)
440
- # end
441
- # end
442
- #
443
- # @example Conditional logic based on metrics
444
- # context_management do
445
- # on :warning_80 do |ctx|
446
- # if ctx.usage_percentage > 85
447
- # ctx.prune_old_messages(keep_recent: 10)
448
- # else
449
- # ctx.compress_tool_results(keep_recent: 5)
450
- # end
451
- # end
452
- # end
453
- def context_management(&block)
454
- builder = ContextManagement::Builder.new
455
- builder.instance_eval(&block)
456
- @context_management_config = builder.build
457
- end
458
-
459
- # Set permissions directly from hash (for YAML translation)
460
- #
461
- # This is intentionally separate from permissions() to keep the DSL clean.
462
- # Called by Configuration when translating YAML permissions.
463
- #
464
- # @param hash [Hash] Permissions configuration hash
465
- # @return [void]
466
- def permissions_hash=(hash)
467
- @permissions_config = hash || {}
468
- end
469
-
470
- # Check if model has been explicitly set (not default)
471
- #
472
- # Used by Swarm::Builder to determine if all_agents model should apply.
473
- #
474
- # @return [Boolean] true if model was explicitly set
475
- def model_set?
476
- @model != "gpt-5"
477
- end
478
-
479
- # Check if provider has been explicitly set
480
- #
481
- # Used by Swarm::Builder to determine if all_agents provider should apply.
482
- #
483
- # @return [Boolean] true if provider was explicitly set
484
- def provider_set?
485
- !@provider.nil?
486
- end
487
-
488
- # Check if base_url has been explicitly set
489
- #
490
- # Used by Swarm::Builder to determine if all_agents base_url should apply.
491
- #
492
- # @return [Boolean] true if base_url was explicitly set
493
- def base_url_set?
494
- !@base_url.nil?
495
- end
496
-
497
- # Check if api_version has been explicitly set
498
- #
499
- # Used by Swarm::Builder to determine if all_agents api_version should apply.
500
- #
501
- # @return [Boolean] true if api_version was explicitly set
502
- def api_version_set?
503
- !@api_version.nil?
504
- end
505
-
506
- # Check if request_timeout has been explicitly set
507
- #
508
- # Used by Swarm::Builder to determine if all_agents request_timeout should apply.
509
- #
510
- # @return [Boolean] true if request_timeout was explicitly set
511
- def request_timeout_set?
512
- !@request_timeout.nil?
513
- end
514
-
515
- # Check if turn_timeout has been explicitly set
516
- #
517
- # Used by Swarm::Builder to determine if all_agents turn_timeout should apply.
518
- #
519
- # @return [Boolean] true if turn_timeout was explicitly set
520
- def turn_timeout_set?
521
- !@turn_timeout.nil?
522
- end
523
-
524
- # Check if coding_agent has been explicitly set
525
- #
526
- # Used by Swarm::Builder to determine if all_agents coding_agent should apply.
527
- #
528
- # @return [Boolean] true if coding_agent was explicitly set
529
- def coding_agent_set?
530
- !@coding_agent.nil?
531
- end
532
-
533
- # Disable environment info (date, platform, OS, working directory) in system prompt
534
- #
535
- # When true, omits the environment information section from the agent's system prompt.
536
- # Defaults to false if not set.
537
- #
538
- # @param enabled [Boolean] Whether to disable environment info
539
- # @return [void]
540
- #
541
- # @example
542
- # disable_environment_info true # Omit environment info from prompt
543
- def disable_environment_info(enabled)
544
- @disable_environment_info = enabled
545
- end
546
-
547
- # Check if disable_environment_info has been explicitly set
548
- #
549
- # Used by Swarm::Builder to determine if all_agents disable_environment_info should apply.
550
- #
551
- # @return [Boolean] true if disable_environment_info was explicitly set
552
- def disable_environment_info_set?
553
- !@disable_environment_info.nil?
554
- end
555
-
556
- # Check if parameters have been set
557
- #
558
- # Used by Swarm::Builder for merging all_agents parameters.
559
- #
560
- # @return [Boolean] true if parameters were set
561
- def parameters_set?
562
- @parameters.any?
563
- end
564
-
565
- # Check if headers have been set
566
- #
567
- # Used by Swarm::Builder for merging all_agents headers.
568
- #
569
- # @return [Boolean] true if headers were set
570
- def headers_set?
571
- @headers.any?
572
- end
573
-
574
- # Build and return an Agent::Definition
575
- #
576
- # This method converts the builder's configuration into a validated
577
- # Agent::Definition object. The caller is responsible for adding it to a swarm.
578
- #
579
- # Converts @tools Set to Array here because Agent::Definition expects an array.
580
- # The Set was only used during building to handle duplicates efficiently.
581
- #
582
- # @return [Agent::Definition] Fully configured and validated agent definition
583
- def to_definition
584
- agent_config = {
585
- description: @description || "Agent #{@name}",
586
- model: @model,
587
- system_prompt: @system_prompt,
588
- tools: @tools.to_a, # Convert Set to Array for Agent::Definition compatibility
589
- delegates_to: @delegates_to,
590
- directory: @directory,
591
- }
592
-
593
- # Add optional fields
594
- agent_config[:provider] = @provider if @provider
595
- agent_config[:base_url] = @base_url if @base_url
596
- agent_config[:api_version] = @api_version if @api_version
597
- agent_config[:context_window] = @context_window if @context_window
598
- agent_config[:parameters] = @parameters if @parameters.any?
599
- agent_config[:headers] = @headers if @headers.any?
600
- agent_config[:request_timeout] = @request_timeout if @request_timeout
601
- agent_config[:turn_timeout] = @turn_timeout if @turn_timeout
602
- agent_config[:mcp_servers] = @mcp_servers if @mcp_servers.any?
603
- agent_config[:disable_default_tools] = @disable_default_tools unless @disable_default_tools.nil?
604
- agent_config[:bypass_permissions] = @bypass_permissions
605
- agent_config[:coding_agent] = @coding_agent
606
- agent_config[:assume_model_exists] = @assume_model_exists unless @assume_model_exists.nil?
607
- agent_config[:permissions] = @permissions_config if @permissions_config.any?
608
- agent_config[:default_permissions] = @default_permissions if @default_permissions.any?
609
- agent_config[:memory] = @memory_config if @memory_config
610
- agent_config[:shared_across_delegations] = @shared_across_delegations unless @shared_across_delegations.nil?
611
- agent_config[:streaming] = @streaming unless @streaming.nil?
612
- agent_config[:thinking] = @thinking if @thinking
613
- agent_config[:disable_environment_info] = @disable_environment_info unless @disable_environment_info.nil?
614
-
615
- # Convert DSL hooks to HookDefinition format
616
- agent_config[:hooks] = convert_hooks_to_definitions if @hooks.any?
617
-
618
- # Merge context management hooks into agent hooks
619
- if @context_management_config
620
- agent_config[:hooks] ||= {}
621
- agent_config[:hooks][:context_warning] ||= []
622
- agent_config[:hooks][:context_warning].concat(@context_management_config)
623
- end
624
-
625
- Agent::Definition.new(@name, agent_config)
626
- end
627
-
628
- private
629
-
630
- # Convert DSL hooks to HookDefinition objects for Agent::Definition
631
- #
632
- # This converts the builder's hook configuration (Ruby blocks and shell commands)
633
- # into HookDefinition objects that will be applied during agent initialization.
634
- #
635
- # @return [Hash] Hooks grouped by event type { event: [HookDefinition, ...] }
636
- def convert_hooks_to_definitions
637
- result = Hash.new { |h, k| h[k] = [] }
638
-
639
- @hooks.each do |hook_config|
640
- event = hook_config[:event]
641
-
642
- # Create HookDefinition with proc or command
643
- if hook_config[:block]
644
- # Ruby block hook
645
- hook_def = Hooks::Definition.new(
646
- event: event,
647
- matcher: hook_config[:matcher],
648
- priority: 0,
649
- proc: hook_config[:block],
650
- )
651
- elsif hook_config[:command]
652
- # Shell command hook - wrap in a block that calls ShellExecutor
653
- hook_def = Hooks::Definition.new(
654
- event: event,
655
- matcher: hook_config[:matcher],
656
- priority: 0,
657
- proc: create_shell_hook_proc(hook_config),
658
- )
659
- else
660
- raise ConfigurationError, "Hook must have either :block or :command"
661
- end
662
-
663
- result[event] << hook_def
664
- end
665
-
666
- result
667
- end
668
-
669
- # Create a proc that executes a shell command hook
670
- def create_shell_hook_proc(config)
671
- command = config[:command]
672
- timeout = config[:timeout] || 60
673
- agent_name = @name
674
-
675
- proc do |context|
676
- input_json = build_hook_input(context, config[:event])
677
- Hooks::ShellExecutor.execute(
678
- command: command,
679
- input_json: input_json,
680
- timeout: timeout,
681
- agent_name: agent_name,
682
- swarm_name: context.swarm&.name,
683
- event: config[:event],
684
- )
685
- end
686
- end
687
-
688
- # Build hook input JSON for shell command hooks
689
- def build_hook_input(context, event)
690
- base = { event: event.to_s, agent: @name.to_s }
691
-
692
- case event
693
- when :pre_tool_use
694
- base.merge(tool: context.tool_call.name, parameters: context.tool_call.parameters)
695
- when :post_tool_use
696
- base.merge(result: context.tool_result.content, success: context.tool_result.success?)
697
- when :user_prompt
698
- base.merge(prompt: context.metadata[:prompt])
699
- else
700
- base
701
- end
702
- end
703
- end
704
- end
705
- end