swarm_sdk 2.2.0 → 2.3.0
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/lib/swarm_sdk/agent/builder.rb +58 -0
- data/lib/swarm_sdk/agent/chat.rb +527 -1059
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/context_tracker.rb +9 -88
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +204 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/hook_integration.rb +111 -44
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +78 -0
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +233 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/logging_helpers.rb +1 -1
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +83 -0
- data/lib/swarm_sdk/agent/{chat → chat_helpers}/system_reminder_injector.rb +12 -12
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +79 -0
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +98 -0
- data/lib/swarm_sdk/agent/context.rb +2 -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/builders/base_builder.rb +409 -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/configuration/parser.rb +353 -0
- data/lib/swarm_sdk/configuration/translator.rb +255 -0
- data/lib/swarm_sdk/configuration.rb +65 -543
- data/lib/swarm_sdk/context_compactor/token_counter.rb +3 -3
- 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/defaults.rb +196 -0
- data/lib/swarm_sdk/events_to_messages.rb +18 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +2 -1
- data/lib/swarm_sdk/log_collector.rb +179 -29
- data/lib/swarm_sdk/log_stream.rb +29 -0
- 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 +93 -3
- 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 +180 -136
- 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 +150 -0
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +340 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +7 -4
- data/lib/swarm_sdk/swarm/tool_configurator.rb +42 -138
- data/lib/swarm_sdk/swarm.rb +137 -679
- data/lib/swarm_sdk/tools/bash.rb +11 -3
- 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 +9 -1
- 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 +11 -13
- data/lib/swarm_sdk/tools/registry.rb +122 -10
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +8 -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 +3 -2
- 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 +143 -0
- data/lib/swarm_sdk/workflow/executor.rb +497 -0
- data/lib/swarm_sdk/{node/builder.rb → workflow/node_builder.rb} +3 -3
- data/lib/swarm_sdk/{node → workflow}/transformer_executor.rb +3 -2
- data/lib/swarm_sdk/{node_orchestrator.rb → workflow.rb} +152 -456
- data/lib/swarm_sdk.rb +33 -3
- metadata +67 -15
- data/lib/swarm_sdk/providers/openai_with_responses.rb +0 -589
|
@@ -15,10 +15,13 @@ module SwarmSDK
|
|
|
15
15
|
# Use for temporary, cross-agent communication within a single session.
|
|
16
16
|
class ScratchpadStorage < Storage
|
|
17
17
|
# Initialize scratchpad storage (always volatile)
|
|
18
|
-
|
|
18
|
+
#
|
|
19
|
+
# @param total_size_limit [Integer, nil] Maximum total size in bytes (defaults to Defaults::Storage::TOTAL_SIZE_BYTES)
|
|
20
|
+
def initialize(total_size_limit: nil)
|
|
19
21
|
super() # Initialize parent Storage class
|
|
20
22
|
@entries = {}
|
|
21
23
|
@total_size = 0
|
|
24
|
+
@total_size_limit = total_size_limit || Defaults::Storage::TOTAL_SIZE_BYTES
|
|
22
25
|
@mutex = Mutex.new
|
|
23
26
|
end
|
|
24
27
|
|
|
@@ -38,8 +41,8 @@ module SwarmSDK
|
|
|
38
41
|
content_size = content.bytesize
|
|
39
42
|
|
|
40
43
|
# Check entry size limit
|
|
41
|
-
if content_size >
|
|
42
|
-
raise ArgumentError, "Content exceeds maximum size (#{format_bytes(
|
|
44
|
+
if content_size > Defaults::Storage::ENTRY_SIZE_BYTES
|
|
45
|
+
raise ArgumentError, "Content exceeds maximum size (#{format_bytes(Defaults::Storage::ENTRY_SIZE_BYTES)}). " \
|
|
43
46
|
"Current: #{format_bytes(content_size)}"
|
|
44
47
|
end
|
|
45
48
|
|
|
@@ -49,8 +52,8 @@ module SwarmSDK
|
|
|
49
52
|
new_total_size = @total_size - existing_size + content_size
|
|
50
53
|
|
|
51
54
|
# Check total size limit
|
|
52
|
-
if new_total_size >
|
|
53
|
-
raise ArgumentError, "Scratchpad full (#{format_bytes(
|
|
55
|
+
if new_total_size > @total_size_limit
|
|
56
|
+
raise ArgumentError, "Scratchpad full (#{format_bytes(@total_size_limit)} limit). " \
|
|
54
57
|
"Current: #{format_bytes(@total_size)}, " \
|
|
55
58
|
"Would be: #{format_bytes(new_total_size)}. " \
|
|
56
59
|
"Clear old entries or use smaller content."
|
|
@@ -14,12 +14,6 @@ module SwarmSDK
|
|
|
14
14
|
# - Search capabilities: Glob patterns and grep-style content search
|
|
15
15
|
# - Thread-safe: Mutex-protected operations
|
|
16
16
|
class Storage
|
|
17
|
-
# Maximum size per entry (3MB)
|
|
18
|
-
MAX_ENTRY_SIZE = 3_000_000
|
|
19
|
-
|
|
20
|
-
# Maximum total storage size (100GB)
|
|
21
|
-
MAX_TOTAL_SIZE = 100_000_000_000
|
|
22
|
-
|
|
23
17
|
# Represents a single storage entry with metadata
|
|
24
18
|
Entry = Struct.new(:content, :title, :updated_at, :size, keyword_init: true)
|
|
25
19
|
|
|
@@ -7,6 +7,13 @@ module SwarmSDK
|
|
|
7
7
|
# This tool helps agents track progress on complex multi-step tasks.
|
|
8
8
|
# Each agent maintains its own independent todo list.
|
|
9
9
|
class TodoWrite < RubyLLM::Tool
|
|
10
|
+
# Factory pattern: declare what parameters this tool needs for instantiation
|
|
11
|
+
class << self
|
|
12
|
+
def creation_requirements
|
|
13
|
+
[:agent_name]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
10
17
|
description <<~DESC
|
|
11
18
|
Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
12
19
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
@@ -51,9 +51,10 @@ module SwarmSDK
|
|
|
51
51
|
desc: "The prompt to run on the fetched content. Required when SwarmSDK is configured with webfetch_provider and webfetch_model. Optional otherwise (ignored if LLM processing not configured).",
|
|
52
52
|
required: false
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
# Backward compatibility aliases - use Defaults module for new code
|
|
55
|
+
MAX_CONTENT_LENGTH = Defaults::Limits::WEB_FETCH_CHARACTERS
|
|
55
56
|
USER_AGENT = "SwarmSDK WebFetch Tool (https://github.com/parruda/claude-swarm)"
|
|
56
|
-
TIMEOUT =
|
|
57
|
+
TIMEOUT = Defaults::Timeouts::WEB_FETCH_SECONDS
|
|
57
58
|
|
|
58
59
|
def execute(url:, prompt: nil)
|
|
59
60
|
# Validate inputs
|
|
@@ -10,6 +10,13 @@ module SwarmSDK
|
|
|
10
10
|
class Write < RubyLLM::Tool
|
|
11
11
|
include PathResolver
|
|
12
12
|
|
|
13
|
+
# Factory pattern: declare what parameters this tool needs for instantiation
|
|
14
|
+
class << self
|
|
15
|
+
def creation_requirements
|
|
16
|
+
[:agent_name, :directory]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
13
20
|
description <<~DESC
|
|
14
21
|
Writes a file to the local filesystem.
|
|
15
22
|
This tool will overwrite the existing file if there is one at the provided path.
|
|
@@ -42,8 +49,7 @@ module SwarmSDK
|
|
|
42
49
|
# @param directory [String] Agent's working directory
|
|
43
50
|
def initialize(agent_name:, directory:)
|
|
44
51
|
super()
|
|
45
|
-
|
|
46
|
-
@directory = File.expand_path(directory)
|
|
52
|
+
initialize_agent_context(agent_name: agent_name, directory: directory)
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
# Override name to return simple "Write" instead of full class path
|
|
@@ -101,17 +107,6 @@ module SwarmSDK
|
|
|
101
107
|
rescue StandardError => e
|
|
102
108
|
error("Unexpected error writing file: #{e.class.name} - #{e.message}")
|
|
103
109
|
end
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
# Helper methods
|
|
108
|
-
def validation_error(message)
|
|
109
|
-
"<tool_use_error>InputValidationError: #{message}</tool_use_error>"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def error(message)
|
|
113
|
-
"Error: #{message}"
|
|
114
|
-
end
|
|
115
110
|
end
|
|
116
111
|
end
|
|
117
112
|
end
|
data/lib/swarm_sdk/version.rb
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
class Workflow
|
|
5
|
+
# Builder provides DSL for building multi-node workflows
|
|
6
|
+
# This is the top-level builder accessed via SwarmSDK.workflow
|
|
7
|
+
#
|
|
8
|
+
# The DSL enables:
|
|
9
|
+
# - Node-based workflow configuration
|
|
10
|
+
# - Agent delegation per node
|
|
11
|
+
# - Input/output transformers for data flow
|
|
12
|
+
# - Context preservation across nodes
|
|
13
|
+
#
|
|
14
|
+
# @example Multi-stage workflow
|
|
15
|
+
# workflow = SwarmSDK.workflow do
|
|
16
|
+
# name "Build Pipeline"
|
|
17
|
+
# start_node :planning
|
|
18
|
+
#
|
|
19
|
+
# agent :architect do
|
|
20
|
+
# model "gpt-5"
|
|
21
|
+
# prompt "You design systems"
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# agent :coder do
|
|
25
|
+
# model "gpt-4"
|
|
26
|
+
# prompt "You implement code"
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# node :planning do
|
|
30
|
+
# agent(:architect)
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# node :implementation do
|
|
34
|
+
# agent(:coder)
|
|
35
|
+
# depends_on :planning
|
|
36
|
+
# end
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# workflow.execute("Build auth system")
|
|
40
|
+
class Builder < Builders::BaseBuilder
|
|
41
|
+
# Main entry point for DSL
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# workflow = SwarmSDK.workflow do
|
|
45
|
+
# name "Pipeline"
|
|
46
|
+
# start_node :planning
|
|
47
|
+
# node(:planning) { agent(:architect) }
|
|
48
|
+
# end
|
|
49
|
+
class << self
|
|
50
|
+
def build(allow_filesystem_tools: nil, &block)
|
|
51
|
+
builder = new(allow_filesystem_tools: allow_filesystem_tools)
|
|
52
|
+
builder.instance_eval(&block)
|
|
53
|
+
builder.build_swarm
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize(allow_filesystem_tools: nil)
|
|
58
|
+
super
|
|
59
|
+
@nodes = {}
|
|
60
|
+
@start_node = nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Define a node (mini-swarm execution stage)
|
|
64
|
+
#
|
|
65
|
+
# Nodes enable multi-stage workflows where different agent teams
|
|
66
|
+
# collaborate in sequence. Each node is an independent swarm execution.
|
|
67
|
+
#
|
|
68
|
+
# @param name [Symbol] Node name
|
|
69
|
+
# @yield Block for node configuration
|
|
70
|
+
# @return [void]
|
|
71
|
+
#
|
|
72
|
+
# @example Solo agent node
|
|
73
|
+
# node :planning do
|
|
74
|
+
# agent(:architect)
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# @example Multi-agent node with delegation
|
|
78
|
+
# node :implementation do
|
|
79
|
+
# agent(:backend).delegates_to(:tester, :database)
|
|
80
|
+
# agent(:tester).delegates_to(:database)
|
|
81
|
+
# agent(:database)
|
|
82
|
+
# depends_on :planning
|
|
83
|
+
# end
|
|
84
|
+
def node(name, &block)
|
|
85
|
+
builder = Workflow::NodeBuilder.new(name)
|
|
86
|
+
builder.instance_eval(&block)
|
|
87
|
+
@nodes[name] = builder
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Set the starting node for workflow execution
|
|
91
|
+
#
|
|
92
|
+
# Required when nodes are defined. Specifies which node to execute first.
|
|
93
|
+
#
|
|
94
|
+
# @param name [Symbol] Name of starting node
|
|
95
|
+
# @return [void]
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# start_node :planning
|
|
99
|
+
def start_node(name)
|
|
100
|
+
@start_node = name.to_sym
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Build the actual Workflow instance
|
|
104
|
+
def build_swarm # Returns Workflow despite method name
|
|
105
|
+
raise ConfigurationError, "Workflow name not set. Use: name 'My Workflow'" unless @swarm_name
|
|
106
|
+
raise ConfigurationError, "No nodes defined. Use: node :name { ... }" if @nodes.empty?
|
|
107
|
+
raise ConfigurationError, "start_node not set. Use: start_node :name" unless @start_node
|
|
108
|
+
|
|
109
|
+
# Validate filesystem tools BEFORE building
|
|
110
|
+
validate_all_agents_filesystem_tools if @all_agents_config
|
|
111
|
+
validate_agent_filesystem_tools
|
|
112
|
+
|
|
113
|
+
build_workflow
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Build a node-based workflow
|
|
119
|
+
#
|
|
120
|
+
# @return [Workflow] Configured workflow instance
|
|
121
|
+
def build_workflow
|
|
122
|
+
# Build agent definitions
|
|
123
|
+
agent_definitions = build_agent_definitions
|
|
124
|
+
|
|
125
|
+
# Create workflow
|
|
126
|
+
workflow = Workflow.new(
|
|
127
|
+
swarm_name: @swarm_name,
|
|
128
|
+
swarm_id: @swarm_id,
|
|
129
|
+
agent_definitions: agent_definitions,
|
|
130
|
+
nodes: @nodes,
|
|
131
|
+
start_node: @start_node,
|
|
132
|
+
scratchpad: @scratchpad,
|
|
133
|
+
allow_filesystem_tools: @allow_filesystem_tools,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Pass swarm registry config to workflow if external swarms registered
|
|
137
|
+
workflow.swarm_registry_config = @swarm_registry_config if @swarm_registry_config.any?
|
|
138
|
+
|
|
139
|
+
workflow
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|