claude_swarm 1.0.1 → 1.0.4
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/.claude/commands/release.md +1 -1
- data/.claude/hooks/lint-code-files.rb +65 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +14 -1
- data/CLAUDE.md +1 -1
- data/CONTRIBUTING.md +69 -0
- data/README.md +27 -2
- data/Rakefile +71 -3
- data/analyze_coverage.rb +94 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
- data/docs/v2/README.md +308 -0
- data/docs/v2/guides/claude-code-agents.md +262 -0
- data/docs/v2/guides/complete-tutorial.md +3088 -0
- data/docs/v2/guides/getting-started.md +1456 -0
- data/docs/v2/guides/memory-adapters.md +998 -0
- data/docs/v2/guides/plugins.md +816 -0
- data/docs/v2/guides/quick-start-cli.md +1745 -0
- data/docs/v2/guides/rails-integration.md +1902 -0
- data/docs/v2/guides/swarm-memory.md +599 -0
- data/docs/v2/reference/cli.md +729 -0
- data/docs/v2/reference/ruby-dsl.md +2154 -0
- data/docs/v2/reference/yaml.md +1835 -0
- data/docs-team-swarm.yml +2222 -0
- data/examples/learning-assistant/assistant.md +7 -0
- data/examples/learning-assistant/example-memories/concept-example.md +90 -0
- data/examples/learning-assistant/example-memories/experience-example.md +66 -0
- data/examples/learning-assistant/example-memories/fact-example.md +76 -0
- data/examples/learning-assistant/example-memories/memory-index.md +78 -0
- data/examples/learning-assistant/example-memories/skill-example.md +168 -0
- data/examples/learning-assistant/learning_assistant.rb +34 -0
- data/examples/learning-assistant/learning_assistant.yml +20 -0
- data/examples/v2/dsl/01_basic.rb +44 -0
- data/examples/v2/dsl/02_core_parameters.rb +59 -0
- data/examples/v2/dsl/03_capabilities.rb +71 -0
- data/examples/v2/dsl/04_llm_parameters.rb +56 -0
- data/examples/v2/dsl/05_advanced_flags.rb +73 -0
- data/examples/v2/dsl/06_permissions.rb +80 -0
- data/examples/v2/dsl/07_mcp_server.rb +62 -0
- data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
- data/examples/v2/dsl/09_agent_hooks.rb +67 -0
- data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
- data/examples/v2/dsl/11_delegation.rb +60 -0
- data/examples/v2/dsl/12_complete_integration.rb +137 -0
- data/examples/v2/file_tools_swarm.yml +102 -0
- data/examples/v2/hooks/01_basic_hooks.rb +133 -0
- data/examples/v2/hooks/02_usage_tracking.rb +201 -0
- data/examples/v2/hooks/03_production_monitoring.rb +429 -0
- data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
- data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
- data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
- data/examples/v2/hooks/swarm_summary.sh +44 -0
- data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
- data/examples/v2/hooks/validate_bash.rb +59 -0
- data/examples/v2/multi_directory_permissions.yml +221 -0
- data/examples/v2/node_context_demo.rb +127 -0
- data/examples/v2/node_workflow.rb +173 -0
- data/examples/v2/path_resolution_demo.rb +216 -0
- data/examples/v2/simple-swarm-v2.rb +90 -0
- data/examples/v2/simple-swarm-v2.yml +62 -0
- data/examples/v2/swarm.yml +71 -0
- data/examples/v2/swarm_with_hooks.yml +61 -0
- data/examples/v2/swarm_with_hooks_simple.yml +25 -0
- data/examples/v2/think_tool_demo.rb +62 -0
- data/exe/swarm +6 -0
- data/lib/claude_swarm/claude_mcp_server.rb +0 -6
- data/lib/claude_swarm/cli.rb +10 -3
- data/lib/claude_swarm/commands/ps.rb +19 -20
- data/lib/claude_swarm/commands/show.rb +1 -1
- data/lib/claude_swarm/configuration.rb +10 -12
- data/lib/claude_swarm/mcp_generator.rb +10 -1
- data/lib/claude_swarm/orchestrator.rb +73 -49
- data/lib/claude_swarm/system_utils.rb +37 -11
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +1 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +7 -2
- data/lib/swarm_cli/cli.rb +201 -0
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
- data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
- data/lib/swarm_cli/commands/migrate.rb +55 -0
- data/lib/swarm_cli/commands/run.rb +173 -0
- data/lib/swarm_cli/config_loader.rb +97 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
- data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
- data/lib/swarm_cli/interactive_repl.rb +918 -0
- data/lib/swarm_cli/mcp_serve_options.rb +44 -0
- data/lib/swarm_cli/mcp_tools_options.rb +59 -0
- data/lib/swarm_cli/migrate_options.rb +54 -0
- data/lib/swarm_cli/migrator.rb +132 -0
- data/lib/swarm_cli/options.rb +151 -0
- data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
- data/lib/swarm_cli/ui/components/content_block.rb +120 -0
- data/lib/swarm_cli/ui/components/divider.rb +57 -0
- data/lib/swarm_cli/ui/components/panel.rb +62 -0
- data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
- data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
- data/lib/swarm_cli/ui/formatters/number.rb +58 -0
- data/lib/swarm_cli/ui/formatters/text.rb +77 -0
- data/lib/swarm_cli/ui/formatters/time.rb +73 -0
- data/lib/swarm_cli/ui/icons.rb +59 -0
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
- data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
- data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
- data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
- data/lib/swarm_cli/version.rb +5 -0
- data/lib/swarm_cli.rb +44 -0
- data/lib/swarm_memory/adapters/base.rb +141 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
- data/lib/swarm_memory/chat_extension.rb +34 -0
- data/lib/swarm_memory/cli/commands.rb +306 -0
- data/lib/swarm_memory/core/entry.rb +37 -0
- data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
- data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
- data/lib/swarm_memory/core/path_normalizer.rb +75 -0
- data/lib/swarm_memory/core/semantic_index.rb +244 -0
- data/lib/swarm_memory/core/storage.rb +288 -0
- data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
- data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
- data/lib/swarm_memory/dsl/memory_config.rb +113 -0
- data/lib/swarm_memory/embeddings/embedder.rb +36 -0
- data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
- data/lib/swarm_memory/errors.rb +21 -0
- data/lib/swarm_memory/integration/cli_registration.rb +30 -0
- data/lib/swarm_memory/integration/configuration.rb +43 -0
- data/lib/swarm_memory/integration/registration.rb +31 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
- data/lib/swarm_memory/optimization/analyzer.rb +244 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
- data/lib/swarm_memory/prompts/memory.md.erb +109 -0
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +42 -0
- data/lib/swarm_memory/search/text_similarity.rb +80 -0
- data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
- data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
- data/lib/swarm_memory/tools/load_skill.rb +313 -0
- data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
- data/lib/swarm_memory/tools/memory_delete.rb +99 -0
- data/lib/swarm_memory/tools/memory_edit.rb +185 -0
- data/lib/swarm_memory/tools/memory_glob.rb +160 -0
- data/lib/swarm_memory/tools/memory_grep.rb +247 -0
- data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
- data/lib/swarm_memory/tools/memory_read.rb +123 -0
- data/lib/swarm_memory/tools/memory_write.rb +231 -0
- data/lib/swarm_memory/utils.rb +50 -0
- data/lib/swarm_memory/version.rb +5 -0
- data/lib/swarm_memory.rb +166 -0
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
- data/lib/swarm_sdk/agent/builder.rb +461 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
- data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
- data/lib/swarm_sdk/agent/chat.rb +1159 -0
- data/lib/swarm_sdk/agent/context.rb +112 -0
- data/lib/swarm_sdk/agent/context_manager.rb +309 -0
- data/lib/swarm_sdk/agent/definition.rb +556 -0
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
- data/lib/swarm_sdk/configuration.rb +296 -0
- data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
- data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
- data/lib/swarm_sdk/context_compactor.rb +340 -0
- data/lib/swarm_sdk/hooks/adapter.rb +359 -0
- data/lib/swarm_sdk/hooks/context.rb +197 -0
- data/lib/swarm_sdk/hooks/definition.rb +80 -0
- data/lib/swarm_sdk/hooks/error.rb +29 -0
- data/lib/swarm_sdk/hooks/executor.rb +146 -0
- data/lib/swarm_sdk/hooks/registry.rb +147 -0
- data/lib/swarm_sdk/hooks/result.rb +150 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
- data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
- data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
- data/lib/swarm_sdk/log_collector.rb +51 -0
- data/lib/swarm_sdk/log_stream.rb +69 -0
- data/lib/swarm_sdk/markdown_parser.rb +75 -0
- data/lib/swarm_sdk/model_aliases.json +5 -0
- data/lib/swarm_sdk/models.json +1 -0
- data/lib/swarm_sdk/models.rb +120 -0
- data/lib/swarm_sdk/node/agent_config.rb +49 -0
- data/lib/swarm_sdk/node/builder.rb +439 -0
- data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
- data/lib/swarm_sdk/node_context.rb +170 -0
- data/lib/swarm_sdk/node_orchestrator.rb +384 -0
- data/lib/swarm_sdk/permissions/config.rb +239 -0
- data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
- data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
- data/lib/swarm_sdk/permissions/validator.rb +173 -0
- data/lib/swarm_sdk/permissions_builder.rb +122 -0
- data/lib/swarm_sdk/plugin.rb +147 -0
- data/lib/swarm_sdk/plugin_registry.rb +101 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
- data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
- data/lib/swarm_sdk/result.rb +97 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
- data/lib/swarm_sdk/swarm/builder.rb +586 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
- data/lib/swarm_sdk/swarm.rb +982 -0
- data/lib/swarm_sdk/tools/bash.rb +274 -0
- data/lib/swarm_sdk/tools/clock.rb +44 -0
- data/lib/swarm_sdk/tools/delegate.rb +164 -0
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
- data/lib/swarm_sdk/tools/edit.rb +150 -0
- data/lib/swarm_sdk/tools/glob.rb +158 -0
- data/lib/swarm_sdk/tools/grep.rb +228 -0
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
- data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
- data/lib/swarm_sdk/tools/read.rb +251 -0
- data/lib/swarm_sdk/tools/registry.rb +93 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
- data/lib/swarm_sdk/tools/think.rb +95 -0
- data/lib/swarm_sdk/tools/todo_write.rb +216 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
- data/lib/swarm_sdk/tools/write.rb +117 -0
- data/lib/swarm_sdk/utils.rb +50 -0
- data/lib/swarm_sdk/version.rb +5 -0
- data/lib/swarm_sdk.rb +157 -0
- data/llm.v2.txt +13407 -0
- data/rubocop/cop/security/no_reflection_methods.rb +47 -0
- data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
- data/swarm_cli.gemspec +57 -0
- data/swarm_memory.gemspec +28 -0
- data/swarm_sdk.gemspec +41 -0
- data/team.yml +1 -1
- data/team_full.yml +1875 -0
- data/{team_v2.yml → team_sdk.yml} +121 -52
- metadata +249 -6
- data/EXAMPLES.md +0 -164
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Swarm
|
|
5
|
+
# Handles the complex 5-pass agent initialization process
|
|
6
|
+
#
|
|
7
|
+
# Responsibilities:
|
|
8
|
+
# - Create all agent chat instances (pass 1)
|
|
9
|
+
# - Register delegation tools (pass 2)
|
|
10
|
+
# - Setup agent contexts (pass 3)
|
|
11
|
+
# - Configure hook system (pass 4)
|
|
12
|
+
# - Apply YAML hooks if present (pass 5)
|
|
13
|
+
#
|
|
14
|
+
# This encapsulates the complex initialization logic that was previously
|
|
15
|
+
# embedded in Swarm#initialize_agents.
|
|
16
|
+
class AgentInitializer
|
|
17
|
+
# rubocop:disable Metrics/ParameterLists
|
|
18
|
+
def initialize(swarm, agent_definitions, global_semaphore, hook_registry, scratchpad_storage, plugin_storages, config_for_hooks: nil)
|
|
19
|
+
# rubocop:enable Metrics/ParameterLists
|
|
20
|
+
@swarm = swarm
|
|
21
|
+
@agent_definitions = agent_definitions
|
|
22
|
+
@global_semaphore = global_semaphore
|
|
23
|
+
@hook_registry = hook_registry
|
|
24
|
+
@scratchpad_storage = scratchpad_storage
|
|
25
|
+
@plugin_storages = plugin_storages
|
|
26
|
+
@config_for_hooks = config_for_hooks
|
|
27
|
+
@agents = {}
|
|
28
|
+
@agent_contexts = {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Initialize all agents with their chat instances and tools
|
|
32
|
+
#
|
|
33
|
+
# This implements a 5-pass algorithm:
|
|
34
|
+
# 1. Create all Agent::Chat instances
|
|
35
|
+
# 2. Register delegation tools (agents can call each other)
|
|
36
|
+
# 3. Setup agent contexts for tracking
|
|
37
|
+
# 4. Configure hook system
|
|
38
|
+
# 5. Apply YAML hooks (if loaded from YAML)
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash] agents hash { agent_name => Agent::Chat }
|
|
41
|
+
def initialize_all
|
|
42
|
+
pass_1_create_agents
|
|
43
|
+
pass_2_register_delegation_tools
|
|
44
|
+
pass_3_setup_contexts
|
|
45
|
+
pass_4_configure_hooks
|
|
46
|
+
pass_5_apply_yaml_hooks
|
|
47
|
+
|
|
48
|
+
@agents
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Provide access to agent contexts for Swarm
|
|
52
|
+
attr_reader :agent_contexts
|
|
53
|
+
|
|
54
|
+
# Create a tool that delegates work to another agent
|
|
55
|
+
#
|
|
56
|
+
# This method is public for testing delegation from Swarm.
|
|
57
|
+
#
|
|
58
|
+
# @param name [String] Delegate agent name
|
|
59
|
+
# @param description [String] Delegate agent description
|
|
60
|
+
# @param delegate_chat [Agent::Chat] The delegate's chat instance
|
|
61
|
+
# @param agent_name [Symbol] Name of the delegating agent
|
|
62
|
+
# @param delegating_chat [Agent::Chat, nil] The chat instance of the agent doing the delegating
|
|
63
|
+
# @return [Tools::Delegate] Delegation tool
|
|
64
|
+
def create_delegation_tool(name:, description:, delegate_chat:, agent_name:, delegating_chat: nil)
|
|
65
|
+
Tools::Delegate.new(
|
|
66
|
+
delegate_name: name,
|
|
67
|
+
delegate_description: description,
|
|
68
|
+
delegate_chat: delegate_chat,
|
|
69
|
+
agent_name: agent_name,
|
|
70
|
+
swarm: @swarm,
|
|
71
|
+
hook_registry: @hook_registry,
|
|
72
|
+
delegating_chat: delegating_chat,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# Pass 1: Create all agent chat instances
|
|
79
|
+
#
|
|
80
|
+
# This creates the Agent::Chat instances but doesn't wire them together yet.
|
|
81
|
+
# Each agent gets its own chat instance with configured tools.
|
|
82
|
+
def pass_1_create_agents
|
|
83
|
+
# Create plugin storages for agents
|
|
84
|
+
create_plugin_storages
|
|
85
|
+
|
|
86
|
+
tool_configurator = ToolConfigurator.new(@swarm, @scratchpad_storage, @plugin_storages)
|
|
87
|
+
|
|
88
|
+
@agent_definitions.each do |name, agent_definition|
|
|
89
|
+
chat = create_agent_chat(name, agent_definition, tool_configurator)
|
|
90
|
+
@agents[name] = chat
|
|
91
|
+
|
|
92
|
+
# Notify plugins that agent was initialized
|
|
93
|
+
notify_plugins_agent_initialized(name, chat, agent_definition, tool_configurator)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Pass 2: Register agent delegation tools
|
|
98
|
+
#
|
|
99
|
+
# Now that all agents exist, we can create delegation tools
|
|
100
|
+
# that allow agents to call each other.
|
|
101
|
+
def pass_2_register_delegation_tools
|
|
102
|
+
@agent_definitions.each do |name, agent_definition|
|
|
103
|
+
register_delegation_tools(@agents[name], agent_definition.delegates_to, agent_name: name)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Pass 3: Setup agent contexts
|
|
108
|
+
#
|
|
109
|
+
# Create Agent::Context for each agent to track delegations and metadata.
|
|
110
|
+
# This is needed regardless of whether logging is enabled.
|
|
111
|
+
def pass_3_setup_contexts
|
|
112
|
+
@agents.each do |agent_name, chat|
|
|
113
|
+
agent_definition = @agent_definitions[agent_name]
|
|
114
|
+
delegate_tool_names = agent_definition.delegates_to.map do |delegate_name|
|
|
115
|
+
"DelegateTaskTo#{delegate_name.to_s.capitalize}"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Create agent context
|
|
119
|
+
context = Agent::Context.new(
|
|
120
|
+
name: agent_name,
|
|
121
|
+
delegation_tools: delegate_tool_names,
|
|
122
|
+
metadata: {},
|
|
123
|
+
)
|
|
124
|
+
@agent_contexts[agent_name] = context
|
|
125
|
+
|
|
126
|
+
# Always set agent context (needed for delegation tracking)
|
|
127
|
+
chat.setup_context(context) if chat.respond_to?(:setup_context)
|
|
128
|
+
|
|
129
|
+
# Configure logging callbacks if logging is enabled
|
|
130
|
+
next unless LogStream.emitter
|
|
131
|
+
|
|
132
|
+
chat.setup_logging if chat.respond_to?(:setup_logging)
|
|
133
|
+
|
|
134
|
+
# Emit validation warnings for this agent
|
|
135
|
+
emit_validation_warnings(agent_name, agent_definition)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Emit validation warnings as log events
|
|
140
|
+
#
|
|
141
|
+
# This validates the agent definition and emits any warnings as log events
|
|
142
|
+
# through LogStream (so formatters can handle them).
|
|
143
|
+
#
|
|
144
|
+
# @param agent_name [Symbol] Agent name
|
|
145
|
+
# @param agent_definition [Agent::Definition] Agent definition to validate
|
|
146
|
+
# @return [void]
|
|
147
|
+
def emit_validation_warnings(agent_name, agent_definition)
|
|
148
|
+
warnings = agent_definition.validate
|
|
149
|
+
|
|
150
|
+
warnings.each do |warning|
|
|
151
|
+
case warning[:type]
|
|
152
|
+
when :model_not_found
|
|
153
|
+
LogStream.emit(
|
|
154
|
+
type: "model_lookup_warning",
|
|
155
|
+
agent: agent_name,
|
|
156
|
+
model: warning[:model],
|
|
157
|
+
error_message: warning[:error_message],
|
|
158
|
+
suggestions: warning[:suggestions],
|
|
159
|
+
timestamp: Time.now.utc.iso8601,
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Pass 4: Configure hook system
|
|
166
|
+
#
|
|
167
|
+
# Setup the callback system for each agent, integrating with RubyLLM callbacks.
|
|
168
|
+
def pass_4_configure_hooks
|
|
169
|
+
@agents.each do |agent_name, chat|
|
|
170
|
+
agent_definition = @agent_definitions[agent_name]
|
|
171
|
+
|
|
172
|
+
# Configure callback system (integrates with RubyLLM callbacks)
|
|
173
|
+
chat.setup_hooks(
|
|
174
|
+
registry: @hook_registry,
|
|
175
|
+
agent_definition: agent_definition,
|
|
176
|
+
swarm: @swarm,
|
|
177
|
+
) if chat.respond_to?(:setup_hooks)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Pass 5: Apply YAML hooks
|
|
182
|
+
#
|
|
183
|
+
# If the swarm was loaded from YAML with agent-specific hooks,
|
|
184
|
+
# apply them now via HooksAdapter.
|
|
185
|
+
def pass_5_apply_yaml_hooks
|
|
186
|
+
return unless @config_for_hooks
|
|
187
|
+
|
|
188
|
+
@agents.each do |agent_name, chat|
|
|
189
|
+
agent_def = @config_for_hooks.agents[agent_name]
|
|
190
|
+
next unless agent_def&.hooks
|
|
191
|
+
|
|
192
|
+
# Apply agent-specific hooks via Hooks::Adapter
|
|
193
|
+
Hooks::Adapter.apply_agent_hooks(chat, agent_name, agent_def.hooks, @swarm.name)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Create Agent::Chat instance with rate limiting
|
|
198
|
+
#
|
|
199
|
+
# @param agent_name [Symbol] Agent name
|
|
200
|
+
# @param agent_definition [Agent::Definition] Agent definition object
|
|
201
|
+
# @param tool_configurator [ToolConfigurator] Tool configuration helper
|
|
202
|
+
# @return [Agent::Chat] Configured agent chat instance
|
|
203
|
+
def create_agent_chat(agent_name, agent_definition, tool_configurator)
|
|
204
|
+
chat = Agent::Chat.new(
|
|
205
|
+
definition: agent_definition.to_h,
|
|
206
|
+
agent_name: agent_name,
|
|
207
|
+
global_semaphore: @global_semaphore,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Set agent name on provider for logging (if provider supports it)
|
|
211
|
+
chat.provider.agent_name = agent_name if chat.provider.respond_to?(:agent_name=)
|
|
212
|
+
|
|
213
|
+
# Register tools using ToolConfigurator
|
|
214
|
+
tool_configurator.register_all_tools(
|
|
215
|
+
chat: chat,
|
|
216
|
+
agent_name: agent_name,
|
|
217
|
+
agent_definition: agent_definition,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Register MCP servers using McpConfigurator
|
|
221
|
+
if agent_definition.mcp_servers.any?
|
|
222
|
+
mcp_configurator = McpConfigurator.new(@swarm)
|
|
223
|
+
mcp_configurator.register_mcp_servers(chat, agent_definition.mcp_servers, agent_name: agent_name)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
chat
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Register agent delegation tools
|
|
230
|
+
#
|
|
231
|
+
# Creates delegation tools that allow one agent to call another.
|
|
232
|
+
#
|
|
233
|
+
# @param chat [Agent::Chat] The chat instance
|
|
234
|
+
# @param delegate_names [Array<Symbol>] Names of agents to delegate to
|
|
235
|
+
# @param agent_name [Symbol] Name of the agent doing the delegating
|
|
236
|
+
def register_delegation_tools(chat, delegate_names, agent_name:)
|
|
237
|
+
return if delegate_names.empty?
|
|
238
|
+
|
|
239
|
+
delegate_names.each do |delegate_name|
|
|
240
|
+
delegate_name = delegate_name.to_sym
|
|
241
|
+
|
|
242
|
+
unless @agents.key?(delegate_name)
|
|
243
|
+
raise ConfigurationError, "Agent delegates to unknown agent '#{delegate_name}'"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Create a tool that delegates to the specified agent
|
|
247
|
+
delegate_agent = @agents[delegate_name]
|
|
248
|
+
delegate_definition = @agent_definitions[delegate_name]
|
|
249
|
+
|
|
250
|
+
tool = create_delegation_tool(
|
|
251
|
+
name: delegate_name.to_s,
|
|
252
|
+
description: delegate_definition.description,
|
|
253
|
+
delegate_chat: delegate_agent,
|
|
254
|
+
agent_name: agent_name,
|
|
255
|
+
delegating_chat: chat,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
chat.with_tool(tool)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Create plugin storages for all agents
|
|
263
|
+
#
|
|
264
|
+
# Iterates through all registered plugins and asks each to create
|
|
265
|
+
# storage for agents that need it.
|
|
266
|
+
#
|
|
267
|
+
# @return [void]
|
|
268
|
+
def create_plugin_storages
|
|
269
|
+
PluginRegistry.all.each do |plugin|
|
|
270
|
+
@agent_definitions.each do |agent_name, agent_definition|
|
|
271
|
+
# Check if this plugin needs storage for this agent
|
|
272
|
+
next unless plugin.storage_enabled?(agent_definition)
|
|
273
|
+
|
|
274
|
+
# Get plugin config for this agent
|
|
275
|
+
config = get_plugin_config(agent_definition, plugin.name)
|
|
276
|
+
next unless config
|
|
277
|
+
|
|
278
|
+
# Parse config through plugin
|
|
279
|
+
parsed_config = plugin.parse_config(config)
|
|
280
|
+
|
|
281
|
+
# Create plugin storage
|
|
282
|
+
storage = plugin.create_storage(agent_name: agent_name, config: parsed_config)
|
|
283
|
+
|
|
284
|
+
# Store in plugin_storages: { plugin_name => { agent_name => storage } }
|
|
285
|
+
@plugin_storages[plugin.name] ||= {}
|
|
286
|
+
@plugin_storages[plugin.name][agent_name] = storage
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Get plugin-specific config from agent definition
|
|
292
|
+
#
|
|
293
|
+
# Plugins can store their config in agent definition under their plugin name.
|
|
294
|
+
# E.g., memory plugin looks for `agent_definition.memory`
|
|
295
|
+
#
|
|
296
|
+
# @param agent_definition [Agent::Definition] Agent definition
|
|
297
|
+
# @param plugin_name [Symbol] Plugin name
|
|
298
|
+
# @return [Object, nil] Plugin config or nil
|
|
299
|
+
def get_plugin_config(agent_definition, plugin_name)
|
|
300
|
+
# Try to call method named after plugin (e.g., .memory for :memory plugin)
|
|
301
|
+
if agent_definition.respond_to?(plugin_name)
|
|
302
|
+
agent_definition.public_send(plugin_name)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Notify all plugins that an agent was initialized
|
|
307
|
+
#
|
|
308
|
+
# Plugins can register additional tools, mark tools immutable, etc.
|
|
309
|
+
#
|
|
310
|
+
# @param agent_name [Symbol] Agent name
|
|
311
|
+
# @param chat [Agent::Chat] Chat instance
|
|
312
|
+
# @param agent_definition [Agent::Definition] Agent definition
|
|
313
|
+
# @param tool_configurator [ToolConfigurator] Tool configurator
|
|
314
|
+
# @return [void]
|
|
315
|
+
def notify_plugins_agent_initialized(agent_name, chat, agent_definition, tool_configurator)
|
|
316
|
+
PluginRegistry.all.each do |plugin|
|
|
317
|
+
# Get plugin storage for this agent (if any)
|
|
318
|
+
plugin_storages = @plugin_storages[plugin.name] || {}
|
|
319
|
+
storage = plugin_storages[agent_name]
|
|
320
|
+
|
|
321
|
+
# Build context for plugin
|
|
322
|
+
context = {
|
|
323
|
+
storage: storage,
|
|
324
|
+
agent_definition: agent_definition,
|
|
325
|
+
tool_configurator: tool_configurator,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Notify plugin
|
|
329
|
+
plugin.on_agent_initialized(agent_name: agent_name, agent: chat, context: context)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Swarm
|
|
5
|
+
# AllAgentsBuilder for configuring settings that apply to all agents
|
|
6
|
+
#
|
|
7
|
+
# Settings configured here are applied to ALL agents, but can be overridden
|
|
8
|
+
# at the agent level. This is useful for shared configuration like:
|
|
9
|
+
# - Common provider/base_url (all agents use same proxy)
|
|
10
|
+
# - Shared timeout settings
|
|
11
|
+
# - Global permissions
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# all_agents do
|
|
15
|
+
# provider :openai
|
|
16
|
+
# base_url "http://proxy.com/v1"
|
|
17
|
+
# timeout 180
|
|
18
|
+
# tools :Read, :Write
|
|
19
|
+
# coding_agent false
|
|
20
|
+
# end
|
|
21
|
+
class AllAgentsBuilder
|
|
22
|
+
attr_reader :hooks, :permissions_config, :tools_list
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@tools_list = []
|
|
26
|
+
@hooks = []
|
|
27
|
+
@permissions_config = {}
|
|
28
|
+
@model = nil
|
|
29
|
+
@provider = nil
|
|
30
|
+
@base_url = nil
|
|
31
|
+
@api_version = nil
|
|
32
|
+
@timeout = nil
|
|
33
|
+
@parameters = nil
|
|
34
|
+
@headers = nil
|
|
35
|
+
@coding_agent = nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Set model for all agents
|
|
39
|
+
def model(model_name)
|
|
40
|
+
@model = model_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Set provider for all agents
|
|
44
|
+
def provider(provider_name)
|
|
45
|
+
@provider = provider_name
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Set base URL for all agents
|
|
49
|
+
def base_url(url)
|
|
50
|
+
@base_url = url
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Set API version for all agents
|
|
54
|
+
def api_version(version)
|
|
55
|
+
@api_version = version
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Set timeout for all agents
|
|
59
|
+
def timeout(seconds)
|
|
60
|
+
@timeout = seconds
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Set parameters for all agents
|
|
64
|
+
def parameters(params)
|
|
65
|
+
@parameters = params
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Set headers for all agents
|
|
69
|
+
def headers(header_hash)
|
|
70
|
+
@headers = header_hash
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Set coding_agent flag for all agents
|
|
74
|
+
def coding_agent(enabled)
|
|
75
|
+
@coding_agent = enabled
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Add tools that all agents will have
|
|
79
|
+
def tools(*tool_names)
|
|
80
|
+
@tools_list.concat(tool_names)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Add hook for all agents (agent-level events only)
|
|
84
|
+
#
|
|
85
|
+
# @example
|
|
86
|
+
# hook :pre_tool_use, matcher: "Write" do |ctx|
|
|
87
|
+
# # Applies to all agents
|
|
88
|
+
# end
|
|
89
|
+
def hook(event, matcher: nil, command: nil, timeout: nil, &block)
|
|
90
|
+
# Validate agent-level events
|
|
91
|
+
agent_events = [
|
|
92
|
+
:pre_tool_use,
|
|
93
|
+
:post_tool_use,
|
|
94
|
+
:user_prompt,
|
|
95
|
+
:agent_stop,
|
|
96
|
+
:first_message,
|
|
97
|
+
:pre_delegation,
|
|
98
|
+
:post_delegation,
|
|
99
|
+
:context_warning,
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
unless agent_events.include?(event)
|
|
103
|
+
raise ArgumentError, "Invalid all_agents hook: #{event}. Swarm-level events (:swarm_start, :swarm_stop) cannot be used in all_agents block."
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@hooks << { event: event, matcher: matcher, command: command, timeout: timeout, block: block }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Configure permissions for all agents
|
|
110
|
+
#
|
|
111
|
+
# @example
|
|
112
|
+
# permissions do
|
|
113
|
+
# Write.allow_paths "tmp/**/*"
|
|
114
|
+
# Write.deny_paths "tmp/secrets/**"
|
|
115
|
+
# Bash.allow_commands "^git status$"
|
|
116
|
+
# end
|
|
117
|
+
def permissions(&block)
|
|
118
|
+
@permissions_config = PermissionsBuilder.build(&block)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Convert to hash for merging with agent configs
|
|
122
|
+
#
|
|
123
|
+
# @return [Hash] Configuration hash
|
|
124
|
+
def to_h
|
|
125
|
+
{
|
|
126
|
+
model: @model,
|
|
127
|
+
provider: @provider,
|
|
128
|
+
base_url: @base_url,
|
|
129
|
+
api_version: @api_version,
|
|
130
|
+
timeout: @timeout,
|
|
131
|
+
parameters: @parameters,
|
|
132
|
+
headers: @headers,
|
|
133
|
+
coding_agent: @coding_agent,
|
|
134
|
+
tools: @tools_list,
|
|
135
|
+
permissions: @permissions_config,
|
|
136
|
+
}.compact
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|