claude_swarm 1.0.9 → 1.0.11
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.
- checksums.yaml +4 -4
- data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +10 -0
- data/CLAUDE.md +346 -191
- data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +20 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +146 -1
- data/docs/v2/CHANGELOG.swarm_sdk.md +433 -10
- data/docs/v2/README.md +20 -5
- data/docs/v2/guides/complete-tutorial.md +95 -9
- data/docs/v2/guides/getting-started.md +10 -8
- data/docs/v2/guides/memory-adapters.md +41 -0
- data/docs/v2/guides/migrating-to-2.x.md +746 -0
- data/docs/v2/guides/plugins.md +52 -5
- data/docs/v2/guides/rails-integration.md +6 -0
- data/docs/v2/guides/snapshots.md +14 -14
- data/docs/v2/guides/swarm-memory.md +2 -13
- data/docs/v2/reference/architecture-flow.md +3 -3
- data/docs/v2/reference/cli.md +0 -1
- data/docs/v2/reference/configuration_reference.md +300 -0
- data/docs/v2/reference/event_payload_structures.md +27 -5
- data/docs/v2/reference/ruby-dsl.md +614 -18
- data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
- data/docs/v2/reference/yaml.md +172 -54
- data/examples/snapshot_demo.rb +2 -2
- data/lib/claude_swarm/mcp_generator.rb +8 -21
- data/lib/claude_swarm/orchestrator.rb +8 -1
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/swarm_cli/commands/run.rb +2 -2
- data/lib/swarm_cli/config_loader.rb +11 -11
- data/lib/swarm_cli/formatters/human_formatter.rb +0 -33
- data/lib/swarm_cli/interactive_repl.rb +2 -2
- data/lib/swarm_cli/ui/icons.rb +0 -23
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +11 -34
- data/lib/swarm_memory/core/semantic_index.rb +10 -2
- data/lib/swarm_memory/core/storage.rb +7 -2
- data/lib/swarm_memory/dsl/memory_config.rb +37 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +201 -28
- data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
- data/lib/swarm_memory/tools/load_skill.rb +0 -1
- data/lib/swarm_memory/tools/memory_edit.rb +2 -1
- data/lib/swarm_memory/tools/memory_read.rb +1 -1
- data/lib/swarm_memory/version.rb +1 -1
- data/lib/swarm_memory.rb +8 -6
- data/lib/swarm_sdk/agent/builder.rb +58 -0
- data/lib/swarm_sdk/agent/chat.rb +527 -1061
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/context_tracker.rb +13 -88
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +204 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/hook_integration.rb +108 -46
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +78 -0
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +267 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/logging_helpers.rb +3 -3
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +83 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/system_reminder_injector.rb +11 -13
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +79 -0
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +146 -0
- data/lib/swarm_sdk/agent/context.rb +1 -2
- data/lib/swarm_sdk/agent/definition.rb +66 -154
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +4 -2
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +161 -0
- data/lib/swarm_sdk/agent_registry.rb +146 -0
- data/lib/swarm_sdk/builders/base_builder.rb +488 -0
- data/lib/swarm_sdk/concerns/cleanupable.rb +39 -0
- data/lib/swarm_sdk/concerns/snapshotable.rb +67 -0
- data/lib/swarm_sdk/concerns/validatable.rb +55 -0
- data/lib/swarm_sdk/config.rb +302 -0
- data/lib/swarm_sdk/configuration/parser.rb +373 -0
- data/lib/swarm_sdk/configuration/translator.rb +255 -0
- data/lib/swarm_sdk/configuration.rb +77 -546
- data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
- data/lib/swarm_sdk/context_compactor.rb +6 -11
- data/lib/swarm_sdk/context_management/builder.rb +128 -0
- data/lib/swarm_sdk/context_management/context.rb +328 -0
- data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
- data/lib/swarm_sdk/defaults.rb +196 -0
- data/lib/swarm_sdk/events_to_messages.rb +18 -0
- data/lib/swarm_sdk/hooks/adapter.rb +3 -3
- data/lib/swarm_sdk/hooks/shell_executor.rb +4 -2
- data/lib/swarm_sdk/log_collector.rb +179 -29
- data/lib/swarm_sdk/log_stream.rb +29 -0
- data/lib/swarm_sdk/models.json +4333 -1
- data/lib/swarm_sdk/models.rb +43 -2
- data/lib/swarm_sdk/node_context.rb +1 -1
- data/lib/swarm_sdk/observer/builder.rb +81 -0
- data/lib/swarm_sdk/observer/config.rb +45 -0
- data/lib/swarm_sdk/observer/manager.rb +236 -0
- data/lib/swarm_sdk/patterns/agent_observer.rb +160 -0
- data/lib/swarm_sdk/plugin.rb +95 -5
- data/lib/swarm_sdk/result.rb +52 -0
- data/lib/swarm_sdk/snapshot.rb +6 -6
- data/lib/swarm_sdk/snapshot_from_events.rb +13 -2
- data/lib/swarm_sdk/state_restorer.rb +136 -151
- data/lib/swarm_sdk/state_snapshot.rb +65 -100
- data/lib/swarm_sdk/swarm/agent_initializer.rb +181 -137
- data/lib/swarm_sdk/swarm/builder.rb +44 -578
- data/lib/swarm_sdk/swarm/executor.rb +213 -0
- data/lib/swarm_sdk/swarm/hook_triggers.rb +151 -0
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +341 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +7 -4
- data/lib/swarm_sdk/swarm/tool_configurator.rb +58 -140
- data/lib/swarm_sdk/swarm.rb +203 -683
- data/lib/swarm_sdk/tools/bash.rb +14 -8
- data/lib/swarm_sdk/tools/delegate.rb +61 -43
- data/lib/swarm_sdk/tools/edit.rb +8 -13
- data/lib/swarm_sdk/tools/glob.rb +12 -4
- data/lib/swarm_sdk/tools/grep.rb +7 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +15 -11
- data/lib/swarm_sdk/tools/path_resolver.rb +51 -2
- data/lib/swarm_sdk/tools/read.rb +16 -18
- data/lib/swarm_sdk/tools/registry.rb +122 -10
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +9 -5
- data/lib/swarm_sdk/tools/stores/storage.rb +0 -6
- data/lib/swarm_sdk/tools/todo_write.rb +7 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +20 -17
- data/lib/swarm_sdk/tools/write.rb +8 -13
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk/{node → workflow}/agent_config.rb +1 -1
- data/lib/swarm_sdk/workflow/builder.rb +192 -0
- data/lib/swarm_sdk/workflow/executor.rb +497 -0
- data/lib/swarm_sdk/{node/builder.rb → workflow/node_builder.rb} +7 -5
- data/lib/swarm_sdk/{node → workflow}/transformer_executor.rb +5 -3
- data/lib/swarm_sdk/{node_orchestrator.rb → workflow.rb} +152 -456
- data/lib/swarm_sdk.rb +294 -108
- data/rubocop/cop/security/no_reflection_methods.rb +1 -1
- data/swarm_cli.gemspec +1 -1
- data/swarm_memory.gemspec +8 -3
- data/swarm_sdk.gemspec +6 -4
- data/team_full.yml +124 -320
- metadata +42 -14
- data/lib/swarm_memory/chat_extension.rb +0 -34
- data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
- data/lib/swarm_sdk/providers/openai_with_responses.rb +0 -589
- /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Configuration
|
|
5
|
+
# Translates parsed configuration to Swarm/Workflow using DSL builders
|
|
6
|
+
#
|
|
7
|
+
# This class is responsible for:
|
|
8
|
+
# - Creating the appropriate builder (Swarm::Builder or Workflow::Builder)
|
|
9
|
+
# - Translating parsed configuration into DSL method calls
|
|
10
|
+
# - Building the final Swarm or Workflow instance
|
|
11
|
+
#
|
|
12
|
+
# Receives a parsed Configuration::Parser and converts it to runtime objects.
|
|
13
|
+
class Translator
|
|
14
|
+
def initialize(parser)
|
|
15
|
+
@parser = parser
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_swarm(allow_filesystem_tools: nil)
|
|
19
|
+
builder = create_builder(allow_filesystem_tools)
|
|
20
|
+
|
|
21
|
+
translate_common_config(builder)
|
|
22
|
+
translate_type_specific_config(builder)
|
|
23
|
+
translate_agents(builder)
|
|
24
|
+
translate_hooks(builder)
|
|
25
|
+
|
|
26
|
+
builder.build_swarm
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def create_builder(allow_filesystem_tools)
|
|
32
|
+
if @parser.config_type == :swarm
|
|
33
|
+
Swarm::Builder.new(allow_filesystem_tools: allow_filesystem_tools)
|
|
34
|
+
else
|
|
35
|
+
Workflow::Builder.new(allow_filesystem_tools: allow_filesystem_tools)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def translate_common_config(builder)
|
|
40
|
+
builder.id(@parser.swarm_id) if @parser.swarm_id
|
|
41
|
+
builder.name(@parser.swarm_name)
|
|
42
|
+
builder.scratchpad(@parser.scratchpad_mode)
|
|
43
|
+
|
|
44
|
+
if @parser.external_swarms&.any?
|
|
45
|
+
external_swarms = @parser.external_swarms
|
|
46
|
+
builder.swarms do
|
|
47
|
+
external_swarms.each do |name, config|
|
|
48
|
+
source = config[:source]
|
|
49
|
+
case source[:type]
|
|
50
|
+
when :file
|
|
51
|
+
register(name, file: source[:value], keep_context: config[:keep_context])
|
|
52
|
+
when :yaml
|
|
53
|
+
register(name, yaml: source[:value], keep_context: config[:keep_context])
|
|
54
|
+
else
|
|
55
|
+
raise ConfigurationError, "Unknown source type: #{source[:type]}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
translate_all_agents(builder) if @parser.all_agents_config.any?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def translate_type_specific_config(builder)
|
|
65
|
+
if @parser.config_type == :swarm
|
|
66
|
+
builder.lead(@parser.lead_agent)
|
|
67
|
+
else
|
|
68
|
+
builder.start_node(@parser.start_node)
|
|
69
|
+
translate_nodes(builder)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def translate_hooks(builder)
|
|
74
|
+
return if @parser.swarm_hooks.none?
|
|
75
|
+
|
|
76
|
+
@parser.swarm_hooks.each do |event, hook_specs|
|
|
77
|
+
Array(hook_specs).each do |spec|
|
|
78
|
+
if spec[:type] == "command"
|
|
79
|
+
builder.hook(event, command: spec[:command], timeout: spec[:timeout])
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def translate_all_agents(builder)
|
|
86
|
+
all_agents_cfg = @parser.all_agents_config
|
|
87
|
+
all_agents_hks = @parser.all_agents_hooks
|
|
88
|
+
|
|
89
|
+
builder.all_agents do
|
|
90
|
+
tools(*all_agents_cfg[:tools]) if all_agents_cfg[:tools]&.any?
|
|
91
|
+
model(all_agents_cfg[:model]) if all_agents_cfg[:model]
|
|
92
|
+
provider(all_agents_cfg[:provider]) if all_agents_cfg[:provider]
|
|
93
|
+
base_url(all_agents_cfg[:base_url]) if all_agents_cfg[:base_url]
|
|
94
|
+
api_version(all_agents_cfg[:api_version]) if all_agents_cfg[:api_version]
|
|
95
|
+
timeout(all_agents_cfg[:timeout]) if all_agents_cfg[:timeout]
|
|
96
|
+
parameters(all_agents_cfg[:parameters]) if all_agents_cfg[:parameters]
|
|
97
|
+
headers(all_agents_cfg[:headers]) if all_agents_cfg[:headers]
|
|
98
|
+
coding_agent(all_agents_cfg[:coding_agent]) unless all_agents_cfg[:coding_agent].nil?
|
|
99
|
+
disable_default_tools(all_agents_cfg[:disable_default_tools]) unless all_agents_cfg[:disable_default_tools].nil?
|
|
100
|
+
|
|
101
|
+
if all_agents_hks.any?
|
|
102
|
+
all_agents_hks.each do |event, hook_specs|
|
|
103
|
+
Array(hook_specs).each do |spec|
|
|
104
|
+
matcher = spec[:matcher]
|
|
105
|
+
hook(event, matcher: matcher, command: spec[:command], timeout: spec[:timeout]) if spec[:type] == "command"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
self.permissions_hash = all_agents_cfg[:permissions] if all_agents_cfg[:permissions]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def translate_agents(builder)
|
|
115
|
+
@parser.agents.each do |name, agent_config|
|
|
116
|
+
translate_agent(builder, name, agent_config)
|
|
117
|
+
rescue ConfigurationError => e
|
|
118
|
+
raise ConfigurationError, "Error in #{@parser.config_type}.agents.#{name}: #{e.message}"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def translate_agent(builder, name, config)
|
|
123
|
+
if config[:agent_file]
|
|
124
|
+
agent_file_path = resolve_agent_file_path(config[:agent_file])
|
|
125
|
+
|
|
126
|
+
unless File.exist?(agent_file_path)
|
|
127
|
+
raise ConfigurationError, "Agent file not found: #{agent_file_path}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
content = File.read(agent_file_path)
|
|
131
|
+
overrides = config.except(:agent_file)
|
|
132
|
+
|
|
133
|
+
if overrides.any?
|
|
134
|
+
builder.agent(name, content, &create_agent_config_block(overrides))
|
|
135
|
+
else
|
|
136
|
+
builder.agent(name, content)
|
|
137
|
+
end
|
|
138
|
+
else
|
|
139
|
+
builder.agent(name, &create_agent_config_block(config))
|
|
140
|
+
end
|
|
141
|
+
rescue StandardError => e
|
|
142
|
+
raise ConfigurationError, "Error loading agent '#{name}': #{e.message}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def create_agent_config_block(config)
|
|
146
|
+
proc do
|
|
147
|
+
description(config[:description]) if config[:description]
|
|
148
|
+
model(config[:model]) if config[:model]
|
|
149
|
+
provider(config[:provider]) if config[:provider]
|
|
150
|
+
base_url(config[:base_url]) if config[:base_url]
|
|
151
|
+
api_version(config[:api_version]) if config[:api_version]
|
|
152
|
+
context_window(config[:context_window]) if config[:context_window]
|
|
153
|
+
system_prompt(config[:system_prompt]) if config[:system_prompt]
|
|
154
|
+
directory(config[:directory]) if config[:directory]
|
|
155
|
+
timeout(config[:timeout]) if config[:timeout]
|
|
156
|
+
parameters(config[:parameters]) if config[:parameters]
|
|
157
|
+
headers(config[:headers]) if config[:headers]
|
|
158
|
+
coding_agent(config[:coding_agent]) unless config[:coding_agent].nil?
|
|
159
|
+
bypass_permissions(config[:bypass_permissions]) if config[:bypass_permissions]
|
|
160
|
+
disable_default_tools(config[:disable_default_tools]) unless config[:disable_default_tools].nil?
|
|
161
|
+
shared_across_delegations(config[:shared_across_delegations]) unless config[:shared_across_delegations].nil?
|
|
162
|
+
|
|
163
|
+
if config[:tools]&.any?
|
|
164
|
+
tool_names = config[:tools].map { |t| t.is_a?(Hash) ? t[:name] : t }
|
|
165
|
+
tools(*tool_names)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
delegates_to(*config[:delegates_to]) if config[:delegates_to]&.any?
|
|
169
|
+
|
|
170
|
+
config[:mcp_servers]&.each do |server|
|
|
171
|
+
mcp_server(server[:name], **server.except(:name))
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
config[:hooks]&.each do |event, hook_specs|
|
|
175
|
+
Array(hook_specs).each do |spec|
|
|
176
|
+
matcher = spec[:matcher]
|
|
177
|
+
hook(event, matcher: matcher, command: spec[:command], timeout: spec[:timeout]) if spec[:type] == "command"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Translate context_management YAML config to DSL
|
|
182
|
+
if config[:context_management]
|
|
183
|
+
ctx_mgmt_config = config[:context_management]
|
|
184
|
+
context_management do
|
|
185
|
+
ctx_mgmt_config.each do |event_name, handler_cfg|
|
|
186
|
+
# Capture handler_cfg in closure for each threshold
|
|
187
|
+
captured_config = handler_cfg
|
|
188
|
+
on(event_name.to_sym) do |ctx|
|
|
189
|
+
action = captured_config[:action]&.to_s
|
|
190
|
+
|
|
191
|
+
case action
|
|
192
|
+
when "compress_tool_results"
|
|
193
|
+
ctx.compress_tool_results(
|
|
194
|
+
keep_recent: captured_config[:keep_recent] || 10,
|
|
195
|
+
truncate_to: captured_config[:truncate_to] || 200,
|
|
196
|
+
)
|
|
197
|
+
when "prune_old_messages"
|
|
198
|
+
ctx.prune_old_messages(keep_recent: captured_config[:keep_recent] || 20)
|
|
199
|
+
when "log_warning"
|
|
200
|
+
ctx.log_action("threshold_warning", threshold: ctx.threshold)
|
|
201
|
+
else
|
|
202
|
+
raise ConfigurationError, "Unknown context_management action: #{action}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Let plugins handle their YAML config translation
|
|
210
|
+
# This removes SDK knowledge of plugin-specific configuration
|
|
211
|
+
PluginRegistry.all.each do |plugin|
|
|
212
|
+
plugin.translate_yaml_config(self, config)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
self.permissions_hash = config[:permissions] if config[:permissions]
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def translate_nodes(builder)
|
|
220
|
+
@parser.nodes.each do |node_name, node_config|
|
|
221
|
+
builder.node(node_name) do
|
|
222
|
+
node_config[:agents]&.each do |agent_config|
|
|
223
|
+
agent_name = agent_config[:agent].to_sym
|
|
224
|
+
delegates = agent_config[:delegates_to] || []
|
|
225
|
+
reset_ctx = agent_config.key?(:reset_context) ? agent_config[:reset_context] : true
|
|
226
|
+
tools_override = agent_config[:tools]
|
|
227
|
+
|
|
228
|
+
agent_cfg = agent(agent_name, reset_context: reset_ctx)
|
|
229
|
+
agent_cfg = agent_cfg.delegates_to(*delegates) if delegates.any?
|
|
230
|
+
agent_cfg.tools(*tools_override) if tools_override
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
depends_on(*node_config[:dependencies]) if node_config[:dependencies]&.any?
|
|
234
|
+
|
|
235
|
+
lead(node_config[:lead].to_sym) if node_config[:lead]
|
|
236
|
+
|
|
237
|
+
if node_config[:input_command]
|
|
238
|
+
input_command(node_config[:input_command], timeout: node_config[:input_timeout] || 60)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
if node_config[:output_command]
|
|
242
|
+
output_command(node_config[:output_command], timeout: node_config[:output_timeout] || 60)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def resolve_agent_file_path(file_path)
|
|
249
|
+
return file_path if Pathname.new(file_path).absolute?
|
|
250
|
+
|
|
251
|
+
@parser.base_dir.join(file_path).to_s
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|