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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +10 -0
  3. data/CLAUDE.md +346 -191
  4. data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
  5. data/docs/v2/CHANGELOG.swarm_cli.md +20 -0
  6. data/docs/v2/CHANGELOG.swarm_memory.md +146 -1
  7. data/docs/v2/CHANGELOG.swarm_sdk.md +433 -10
  8. data/docs/v2/README.md +20 -5
  9. data/docs/v2/guides/complete-tutorial.md +95 -9
  10. data/docs/v2/guides/getting-started.md +10 -8
  11. data/docs/v2/guides/memory-adapters.md +41 -0
  12. data/docs/v2/guides/migrating-to-2.x.md +746 -0
  13. data/docs/v2/guides/plugins.md +52 -5
  14. data/docs/v2/guides/rails-integration.md +6 -0
  15. data/docs/v2/guides/snapshots.md +14 -14
  16. data/docs/v2/guides/swarm-memory.md +2 -13
  17. data/docs/v2/reference/architecture-flow.md +3 -3
  18. data/docs/v2/reference/cli.md +0 -1
  19. data/docs/v2/reference/configuration_reference.md +300 -0
  20. data/docs/v2/reference/event_payload_structures.md +27 -5
  21. data/docs/v2/reference/ruby-dsl.md +614 -18
  22. data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
  23. data/docs/v2/reference/yaml.md +172 -54
  24. data/examples/snapshot_demo.rb +2 -2
  25. data/lib/claude_swarm/mcp_generator.rb +8 -21
  26. data/lib/claude_swarm/orchestrator.rb +8 -1
  27. data/lib/claude_swarm/version.rb +1 -1
  28. data/lib/swarm_cli/commands/run.rb +2 -2
  29. data/lib/swarm_cli/config_loader.rb +11 -11
  30. data/lib/swarm_cli/formatters/human_formatter.rb +0 -33
  31. data/lib/swarm_cli/interactive_repl.rb +2 -2
  32. data/lib/swarm_cli/ui/icons.rb +0 -23
  33. data/lib/swarm_cli/version.rb +1 -1
  34. data/lib/swarm_memory/adapters/filesystem_adapter.rb +11 -34
  35. data/lib/swarm_memory/core/semantic_index.rb +10 -2
  36. data/lib/swarm_memory/core/storage.rb +7 -2
  37. data/lib/swarm_memory/dsl/memory_config.rb +37 -0
  38. data/lib/swarm_memory/integration/sdk_plugin.rb +201 -28
  39. data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
  40. data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
  41. data/lib/swarm_memory/tools/load_skill.rb +0 -1
  42. data/lib/swarm_memory/tools/memory_edit.rb +2 -1
  43. data/lib/swarm_memory/tools/memory_read.rb +1 -1
  44. data/lib/swarm_memory/version.rb +1 -1
  45. data/lib/swarm_memory.rb +8 -6
  46. data/lib/swarm_sdk/agent/builder.rb +58 -0
  47. data/lib/swarm_sdk/agent/chat.rb +527 -1061
  48. data/lib/swarm_sdk/agent/{chat → chat_helpers}/context_tracker.rb +13 -88
  49. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +204 -0
  50. data/lib/swarm_sdk/agent/{chat → chat_helpers}/hook_integration.rb +108 -46
  51. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +78 -0
  52. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +267 -0
  53. data/lib/swarm_sdk/agent/{chat → chat_helpers}/logging_helpers.rb +3 -3
  54. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +83 -0
  55. data/lib/swarm_sdk/agent/{chat → chat_helpers}/system_reminder_injector.rb +11 -13
  56. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +79 -0
  57. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +146 -0
  58. data/lib/swarm_sdk/agent/context.rb +1 -2
  59. data/lib/swarm_sdk/agent/definition.rb +66 -154
  60. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +4 -2
  61. data/lib/swarm_sdk/agent/system_prompt_builder.rb +161 -0
  62. data/lib/swarm_sdk/agent_registry.rb +146 -0
  63. data/lib/swarm_sdk/builders/base_builder.rb +488 -0
  64. data/lib/swarm_sdk/concerns/cleanupable.rb +39 -0
  65. data/lib/swarm_sdk/concerns/snapshotable.rb +67 -0
  66. data/lib/swarm_sdk/concerns/validatable.rb +55 -0
  67. data/lib/swarm_sdk/config.rb +302 -0
  68. data/lib/swarm_sdk/configuration/parser.rb +373 -0
  69. data/lib/swarm_sdk/configuration/translator.rb +255 -0
  70. data/lib/swarm_sdk/configuration.rb +77 -546
  71. data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
  72. data/lib/swarm_sdk/context_compactor.rb +6 -11
  73. data/lib/swarm_sdk/context_management/builder.rb +128 -0
  74. data/lib/swarm_sdk/context_management/context.rb +328 -0
  75. data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
  76. data/lib/swarm_sdk/defaults.rb +196 -0
  77. data/lib/swarm_sdk/events_to_messages.rb +18 -0
  78. data/lib/swarm_sdk/hooks/adapter.rb +3 -3
  79. data/lib/swarm_sdk/hooks/shell_executor.rb +4 -2
  80. data/lib/swarm_sdk/log_collector.rb +179 -29
  81. data/lib/swarm_sdk/log_stream.rb +29 -0
  82. data/lib/swarm_sdk/models.json +4333 -1
  83. data/lib/swarm_sdk/models.rb +43 -2
  84. data/lib/swarm_sdk/node_context.rb +1 -1
  85. data/lib/swarm_sdk/observer/builder.rb +81 -0
  86. data/lib/swarm_sdk/observer/config.rb +45 -0
  87. data/lib/swarm_sdk/observer/manager.rb +236 -0
  88. data/lib/swarm_sdk/patterns/agent_observer.rb +160 -0
  89. data/lib/swarm_sdk/plugin.rb +95 -5
  90. data/lib/swarm_sdk/result.rb +52 -0
  91. data/lib/swarm_sdk/snapshot.rb +6 -6
  92. data/lib/swarm_sdk/snapshot_from_events.rb +13 -2
  93. data/lib/swarm_sdk/state_restorer.rb +136 -151
  94. data/lib/swarm_sdk/state_snapshot.rb +65 -100
  95. data/lib/swarm_sdk/swarm/agent_initializer.rb +181 -137
  96. data/lib/swarm_sdk/swarm/builder.rb +44 -578
  97. data/lib/swarm_sdk/swarm/executor.rb +213 -0
  98. data/lib/swarm_sdk/swarm/hook_triggers.rb +151 -0
  99. data/lib/swarm_sdk/swarm/logging_callbacks.rb +341 -0
  100. data/lib/swarm_sdk/swarm/mcp_configurator.rb +7 -4
  101. data/lib/swarm_sdk/swarm/tool_configurator.rb +58 -140
  102. data/lib/swarm_sdk/swarm.rb +203 -683
  103. data/lib/swarm_sdk/tools/bash.rb +14 -8
  104. data/lib/swarm_sdk/tools/delegate.rb +61 -43
  105. data/lib/swarm_sdk/tools/edit.rb +8 -13
  106. data/lib/swarm_sdk/tools/glob.rb +12 -4
  107. data/lib/swarm_sdk/tools/grep.rb +7 -0
  108. data/lib/swarm_sdk/tools/multi_edit.rb +15 -11
  109. data/lib/swarm_sdk/tools/path_resolver.rb +51 -2
  110. data/lib/swarm_sdk/tools/read.rb +16 -18
  111. data/lib/swarm_sdk/tools/registry.rb +122 -10
  112. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +9 -5
  113. data/lib/swarm_sdk/tools/stores/storage.rb +0 -6
  114. data/lib/swarm_sdk/tools/todo_write.rb +7 -0
  115. data/lib/swarm_sdk/tools/web_fetch.rb +20 -17
  116. data/lib/swarm_sdk/tools/write.rb +8 -13
  117. data/lib/swarm_sdk/version.rb +1 -1
  118. data/lib/swarm_sdk/{node → workflow}/agent_config.rb +1 -1
  119. data/lib/swarm_sdk/workflow/builder.rb +192 -0
  120. data/lib/swarm_sdk/workflow/executor.rb +497 -0
  121. data/lib/swarm_sdk/{node/builder.rb → workflow/node_builder.rb} +7 -5
  122. data/lib/swarm_sdk/{node → workflow}/transformer_executor.rb +5 -3
  123. data/lib/swarm_sdk/{node_orchestrator.rb → workflow.rb} +152 -456
  124. data/lib/swarm_sdk.rb +294 -108
  125. data/rubocop/cop/security/no_reflection_methods.rb +1 -1
  126. data/swarm_cli.gemspec +1 -1
  127. data/swarm_memory.gemspec +8 -3
  128. data/swarm_sdk.gemspec +6 -4
  129. data/team_full.yml +124 -320
  130. metadata +42 -14
  131. data/lib/swarm_memory/chat_extension.rb +0 -34
  132. data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
  133. data/lib/swarm_sdk/providers/openai_with_responses.rb +0 -589
  134. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -40,7 +40,7 @@ module SwarmSDK
40
40
  # Fetch tools from MCP server and register with chat
41
41
  # Tools are already in RubyLLM::Tool format
42
42
  tools = client.tools
43
- tools.each { |tool| chat.with_tool(tool) }
43
+ tools.each { |tool| chat.add_tool(tool) }
44
44
 
45
45
  RubyLLM.logger.debug("SwarmSDK: Registered #{tools.size} tools from MCP server '#{server_config[:name]}' for agent #{agent_name}")
46
46
  rescue StandardError => e
@@ -138,13 +138,16 @@ module SwarmSDK
138
138
  # @param config [Hash] MCP server configuration
139
139
  # @return [Hash] Streamable configuration
140
140
  def build_streamable_config(config)
141
- {
141
+ streamable_config = {
142
142
  url: config[:url],
143
143
  headers: config[:headers] || {},
144
144
  version: config[:version]&.to_sym || :http2,
145
- oauth: config[:oauth],
146
- rate_limit: config[:rate_limit],
147
145
  }
146
+
147
+ # Only include rate_limit if present
148
+ streamable_config[:rate_limit] = config[:rate_limit] if config[:rate_limit]
149
+
150
+ streamable_config
148
151
  end
149
152
  end
150
153
  end
@@ -57,6 +57,14 @@ module SwarmSDK
57
57
 
58
58
  # Create a tool instance by name
59
59
  #
60
+ # Uses the Registry factory pattern to instantiate tools based on their
61
+ # declared requirements. This eliminates the need for a giant case statement.
62
+ #
63
+ # Tool lookup order:
64
+ # 1. Plugin tools (registered via SwarmSDK::PluginRegistry)
65
+ # 2. Custom tools (registered via SwarmSDK.register_tool)
66
+ # 3. Built-in tools (SwarmSDK::Tools::Registry)
67
+ #
60
68
  # File tools and TodoWrite require agent context for tracking state.
61
69
  # Scratchpad tools require shared scratchpad instance.
62
70
  # Plugin tools are delegated to their respective plugins.
@@ -77,47 +85,23 @@ module SwarmSDK
77
85
  return create_plugin_tool(tool_name_sym, agent_name, directory, chat, agent_definition)
78
86
  end
79
87
 
80
- case tool_name_sym
81
- when :Read
82
- Tools::Read.new(agent_name: agent_name, directory: directory)
83
- when :Write
84
- Tools::Write.new(agent_name: agent_name, directory: directory)
85
- when :Edit
86
- Tools::Edit.new(agent_name: agent_name, directory: directory)
87
- when :MultiEdit
88
- Tools::MultiEdit.new(agent_name: agent_name, directory: directory)
89
- when :Bash
90
- Tools::Bash.new(directory: directory)
91
- when :Glob
92
- Tools::Glob.new(directory: directory)
93
- when :Grep
94
- Tools::Grep.new(directory: directory)
95
- when :TodoWrite
96
- Tools::TodoWrite.new(agent_name: agent_name) # TodoWrite doesn't need directory
97
- when :ScratchpadWrite
98
- Tools::Scratchpad::ScratchpadWrite.create_for_scratchpad(@scratchpad_storage)
99
- when :ScratchpadRead
100
- Tools::Scratchpad::ScratchpadRead.create_for_scratchpad(@scratchpad_storage)
101
- when :ScratchpadList
102
- Tools::Scratchpad::ScratchpadList.create_for_scratchpad(@scratchpad_storage)
103
- when :Think
104
- Tools::Think.new
105
- when :Clock
106
- Tools::Clock.new
107
- else
108
- # Regular tools - get class from registry and instantiate
109
- tool_class = Tools::Registry.get(tool_name_sym)
110
- raise ConfigurationError, "Unknown tool: #{tool_name}" unless tool_class
88
+ # Check if tool is a custom registered tool
89
+ if CustomToolRegistry.registered?(tool_name_sym)
90
+ context = {
91
+ agent_name: agent_name,
92
+ directory: directory,
93
+ }
94
+ return CustomToolRegistry.create(tool_name_sym, context)
95
+ end
111
96
 
112
- # Check if tool is marked as :special but not handled in case statement
113
- if tool_class == :special
114
- raise ConfigurationError,
115
- "Tool '#{tool_name}' requires special initialization but is not handled in create_tool_instance. " \
116
- "This is a bug - #{tool_name} should be added to the case statement above."
117
- end
97
+ # Use Registry factory pattern for built-in tools
98
+ context = {
99
+ agent_name: agent_name,
100
+ directory: directory,
101
+ scratchpad_storage: @scratchpad_storage,
102
+ }
118
103
 
119
- tool_class.new
120
- end
104
+ Tools::Registry.create(tool_name_sym, context)
121
105
  end
122
106
 
123
107
  # Wrap a tool instance with permissions validator if configured
@@ -157,10 +141,10 @@ module SwarmSDK
157
141
  forbidden = tool_configs.map { |tc| tc[:name].to_sym }.select { |name| FILESYSTEM_TOOLS.include?(name) }
158
142
  unless forbidden.empty?
159
143
  raise ConfigurationError,
160
- "Filesystem tools are globally disabled (SwarmSDK.settings.allow_filesystem_tools = false) " \
144
+ "Filesystem tools are globally disabled (SwarmSDK.config.allow_filesystem_tools = false) " \
161
145
  "but agent '#{agent_name}' attempts to use: #{forbidden.join(", ")}.\n\n" \
162
146
  "This is a system-wide security setting that cannot be overridden by swarm configuration.\n" \
163
- "To use filesystem tools, set SwarmSDK.settings.allow_filesystem_tools = true before loading the swarm."
147
+ "To use filesystem tools, set SwarmSDK.config.allow_filesystem_tools = true before loading the swarm."
164
148
  end
165
149
  end
166
150
 
@@ -171,14 +155,8 @@ module SwarmSDK
171
155
  # Create tool instance
172
156
  tool_instance = create_tool_instance(tool_name, agent_name, agent_definition.directory)
173
157
 
174
- # Wrap with permissions validator if configured
175
- tool_instance = wrap_tool_with_permissions(
176
- tool_instance,
177
- permissions_config,
178
- agent_definition,
179
- )
180
-
181
- chat.with_tool(tool_instance)
158
+ # Wrap with permissions and add to chat
159
+ wrap_and_add_tool(chat, tool_instance, permissions_config, agent_definition)
182
160
  end
183
161
  end
184
162
 
@@ -225,19 +203,36 @@ module SwarmSDK
225
203
  return if tool_disabled?(tool_name, agent_definition.disable_default_tools)
226
204
 
227
205
  tool_instance = create_tool_instance(tool_name, agent_name, agent_definition.directory)
206
+ permissions_config = resolve_default_permissions(tool_name, agent_definition)
228
207
 
229
- # Resolve permissions for default tool
230
- permissions_config = agent_definition.agent_permissions[tool_name] ||
231
- agent_definition.default_permissions[tool_name]
208
+ wrap_and_add_tool(chat, tool_instance, permissions_config, agent_definition)
209
+ end
232
210
 
233
- # Wrap with permissions validator if configured
234
- tool_instance = wrap_tool_with_permissions(
235
- tool_instance,
236
- permissions_config,
237
- agent_definition,
238
- )
211
+ # Wrap tool with permissions and add to chat
212
+ #
213
+ # This is the common pattern for registering tools:
214
+ # 1. Wrap with permissions validator (if configured)
215
+ # 2. Add to chat
216
+ #
217
+ # @param chat [Agent::Chat] The chat instance
218
+ # @param tool_instance [RubyLLM::Tool] Tool instance
219
+ # @param permissions_config [Hash, nil] Permissions configuration
220
+ # @param agent_definition [Agent::Definition] Agent definition
221
+ # @return [void]
222
+ def wrap_and_add_tool(chat, tool_instance, permissions_config, agent_definition)
223
+ tool_instance = wrap_tool_with_permissions(tool_instance, permissions_config, agent_definition)
224
+ chat.add_tool(tool_instance)
225
+ end
239
226
 
240
- chat.with_tool(tool_instance)
227
+ # Resolve permissions for a default/plugin tool
228
+ #
229
+ # Looks up permissions in agent-specific config first, falls back to global defaults.
230
+ #
231
+ # @param tool_name [Symbol] Tool name
232
+ # @param agent_definition [Agent::Definition] Agent definition
233
+ # @return [Hash, nil] Permissions configuration or nil
234
+ def resolve_default_permissions(tool_name, agent_definition)
235
+ agent_definition.agent_permissions[tool_name] || agent_definition.default_permissions[tool_name]
241
236
  end
242
237
 
243
238
  # Create a tool instance via plugin
@@ -286,11 +281,7 @@ module SwarmSDK
286
281
  def register_plugin_tools(chat, agent_name, agent_definition, explicit_tool_names)
287
282
  PluginRegistry.all.each do |plugin|
288
283
  # Check if plugin has storage enabled for this agent
289
- next unless plugin.storage_enabled?(agent_definition)
290
-
291
- # Get plugin storage for this agent
292
- plugin_storages = @plugin_storages[plugin.name] || {}
293
- plugin_storages[agent_name]
284
+ next unless plugin.memory_configured?(agent_definition)
294
285
 
295
286
  # Register each tool provided by the plugin
296
287
  plugin.tools.each do |tool_name|
@@ -308,18 +299,9 @@ module SwarmSDK
308
299
  agent_definition: agent_definition,
309
300
  )
310
301
 
311
- # Resolve permissions for plugin tool
312
- permissions_config = agent_definition.agent_permissions[tool_name] ||
313
- agent_definition.default_permissions[tool_name]
314
-
315
- # Wrap with permissions validator if configured
316
- tool_instance = wrap_tool_with_permissions(
317
- tool_instance,
318
- permissions_config,
319
- agent_definition,
320
- )
302
+ permissions_config = resolve_default_permissions(tool_name, agent_definition)
321
303
 
322
- chat.with_tool(tool_instance)
304
+ wrap_and_add_tool(chat, tool_instance, permissions_config, agent_definition)
323
305
  end
324
306
  end
325
307
  end
@@ -379,76 +361,12 @@ module SwarmSDK
379
361
  delegate_chat: delegate_agent,
380
362
  agent_name: agent_name,
381
363
  swarm: @swarm,
382
- hook_registry: @hook_registry,
383
364
  delegating_chat: chat,
384
365
  )
385
366
 
386
- chat.with_tool(tool)
367
+ chat.add_tool(tool)
387
368
  end
388
369
  end
389
-
390
- # Pass 4: Configure hook system
391
- #
392
- # Setup the callback system for each agent.
393
- def pass_4_configure_hooks
394
- @agents.each do |agent_name, chat|
395
- agent_definition = @agent_definitions[agent_name]
396
-
397
- chat.setup_hooks(
398
- registry: @hook_registry,
399
- agent_definition: agent_definition,
400
- swarm: @swarm,
401
- ) if chat.respond_to?(:setup_hooks)
402
- end
403
- end
404
-
405
- # Pass 5: Apply YAML hooks if present
406
- #
407
- # If loaded from YAML, apply agent-specific hooks.
408
- def pass_5_apply_yaml_hooks
409
- return unless @config_for_hooks
410
-
411
- @agents.each do |agent_name, chat|
412
- agent_def = @config_for_hooks.agents[agent_name]
413
- next unless agent_def&.hooks
414
-
415
- HooksAdapter.apply_agent_hooks(chat, agent_name, agent_def.hooks, @swarm.name)
416
- end
417
- end
418
-
419
- # Create an AgentChat instance
420
- #
421
- # NOTE: This is dead code, left over from refactoring. AgentInitializer
422
- # now handles agent creation. This should be removed in a cleanup pass.
423
- #
424
- # @param agent_name [Symbol] Agent name
425
- # @param agent_definition [AgentDefinition] Agent definition
426
- # @param tool_configurator [ToolConfigurator] Tool configurator
427
- # @return [AgentChat] Configured chat instance
428
- def create_agent_chat(agent_name, agent_definition, tool_configurator)
429
- chat = AgentChat.new(
430
- definition: agent_definition.to_h,
431
- global_semaphore: @global_semaphore,
432
- )
433
-
434
- # Set agent name on provider for logging (if provider supports it)
435
- chat.provider.agent_name = agent_name if chat.provider.respond_to?(:agent_name=)
436
-
437
- # Register tools
438
- tool_configurator.register_all_tools(
439
- chat: chat,
440
- agent_name: agent_name,
441
- agent_definition: agent_definition,
442
- )
443
-
444
- # Register MCP servers if any
445
- if agent_definition.mcp_servers.any?
446
- mcp_configurator = McpConfigurator.new(@swarm)
447
- mcp_configurator.register_mcp_servers(chat, agent_definition.mcp_servers, agent_name: agent_name)
448
- end
449
-
450
- chat
451
- end
452
370
  end
453
371
  end
454
372
  end