swarm_memory 2.1.5 → 2.1.6
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_memory/version.rb +1 -1
- metadata +5 -184
- data/lib/claude_swarm/base_executor.rb +0 -133
- data/lib/claude_swarm/claude_code_executor.rb +0 -349
- data/lib/claude_swarm/claude_mcp_server.rb +0 -78
- data/lib/claude_swarm/cli.rb +0 -697
- data/lib/claude_swarm/commands/ps.rb +0 -215
- data/lib/claude_swarm/commands/show.rb +0 -139
- data/lib/claude_swarm/configuration.rb +0 -373
- data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
- data/lib/claude_swarm/json_handler.rb +0 -91
- data/lib/claude_swarm/mcp_generator.rb +0 -230
- data/lib/claude_swarm/openai/chat_completion.rb +0 -256
- data/lib/claude_swarm/openai/executor.rb +0 -256
- data/lib/claude_swarm/openai/responses.rb +0 -319
- data/lib/claude_swarm/orchestrator.rb +0 -878
- data/lib/claude_swarm/process_tracker.rb +0 -78
- data/lib/claude_swarm/session_cost_calculator.rb +0 -209
- data/lib/claude_swarm/session_path.rb +0 -42
- data/lib/claude_swarm/settings_generator.rb +0 -77
- data/lib/claude_swarm/system_utils.rb +0 -46
- data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
- data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
- data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
- data/lib/claude_swarm/tools/task_tool.rb +0 -63
- data/lib/claude_swarm/version.rb +0 -5
- data/lib/claude_swarm/worktree_manager.rb +0 -475
- data/lib/claude_swarm/yaml_loader.rb +0 -22
- data/lib/claude_swarm.rb +0 -67
- data/lib/swarm_cli/cli.rb +0 -201
- data/lib/swarm_cli/command_registry.rb +0 -61
- data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
- data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
- data/lib/swarm_cli/commands/migrate.rb +0 -55
- data/lib/swarm_cli/commands/run.rb +0 -173
- data/lib/swarm_cli/config_loader.rb +0 -98
- data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
- data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
- data/lib/swarm_cli/interactive_repl.rb +0 -924
- data/lib/swarm_cli/mcp_serve_options.rb +0 -44
- data/lib/swarm_cli/mcp_tools_options.rb +0 -59
- data/lib/swarm_cli/migrate_options.rb +0 -54
- data/lib/swarm_cli/migrator.rb +0 -132
- data/lib/swarm_cli/options.rb +0 -151
- data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
- data/lib/swarm_cli/ui/components/content_block.rb +0 -120
- data/lib/swarm_cli/ui/components/divider.rb +0 -57
- data/lib/swarm_cli/ui/components/panel.rb +0 -62
- data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
- data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
- data/lib/swarm_cli/ui/formatters/number.rb +0 -58
- data/lib/swarm_cli/ui/formatters/text.rb +0 -77
- data/lib/swarm_cli/ui/formatters/time.rb +0 -73
- data/lib/swarm_cli/ui/icons.rb +0 -36
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
- data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
- data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
- data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
- data/lib/swarm_cli/version.rb +0 -5
- data/lib/swarm_cli.rb +0 -46
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
- data/lib/swarm_sdk/agent/builder.rb +0 -552
- data/lib/swarm_sdk/agent/chat.rb +0 -774
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
- data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
- data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
- data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
- data/lib/swarm_sdk/agent/context.rb +0 -116
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -477
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- data/lib/swarm_sdk/builders/base_builder.rb +0 -409
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
- data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
- data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
- data/lib/swarm_sdk/concerns/validatable.rb +0 -55
- data/lib/swarm_sdk/configuration/parser.rb +0 -353
- data/lib/swarm_sdk/configuration/translator.rb +0 -255
- data/lib/swarm_sdk/configuration.rb +0 -135
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
- data/lib/swarm_sdk/context_compactor.rb +0 -335
- data/lib/swarm_sdk/context_management/builder.rb +0 -128
- data/lib/swarm_sdk/context_management/context.rb +0 -328
- data/lib/swarm_sdk/defaults.rb +0 -196
- data/lib/swarm_sdk/events_to_messages.rb +0 -199
- data/lib/swarm_sdk/hooks/adapter.rb +0 -359
- data/lib/swarm_sdk/hooks/context.rb +0 -197
- data/lib/swarm_sdk/hooks/definition.rb +0 -80
- data/lib/swarm_sdk/hooks/error.rb +0 -29
- data/lib/swarm_sdk/hooks/executor.rb +0 -146
- data/lib/swarm_sdk/hooks/registry.rb +0 -147
- data/lib/swarm_sdk/hooks/result.rb +0 -150
- data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
- data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
- data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
- data/lib/swarm_sdk/log_collector.rb +0 -227
- data/lib/swarm_sdk/log_stream.rb +0 -127
- data/lib/swarm_sdk/markdown_parser.rb +0 -75
- data/lib/swarm_sdk/model_aliases.json +0 -8
- data/lib/swarm_sdk/models.json +0 -1
- data/lib/swarm_sdk/models.rb +0 -120
- data/lib/swarm_sdk/node_context.rb +0 -245
- data/lib/swarm_sdk/observer/builder.rb +0 -81
- data/lib/swarm_sdk/observer/config.rb +0 -45
- data/lib/swarm_sdk/observer/manager.rb +0 -236
- data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
- data/lib/swarm_sdk/permissions/config.rb +0 -239
- data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
- data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
- data/lib/swarm_sdk/permissions/validator.rb +0 -173
- data/lib/swarm_sdk/permissions_builder.rb +0 -122
- data/lib/swarm_sdk/plugin.rb +0 -309
- data/lib/swarm_sdk/plugin_registry.rb +0 -101
- data/lib/swarm_sdk/proc_helpers.rb +0 -53
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
- data/lib/swarm_sdk/restore_result.rb +0 -65
- data/lib/swarm_sdk/result.rb +0 -123
- data/lib/swarm_sdk/snapshot.rb +0 -156
- data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
- data/lib/swarm_sdk/state_restorer.rb +0 -476
- data/lib/swarm_sdk/state_snapshot.rb +0 -334
- data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
- data/lib/swarm_sdk/swarm/builder.rb +0 -249
- data/lib/swarm_sdk/swarm/executor.rb +0 -213
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
- data/lib/swarm_sdk/swarm.rb +0 -717
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/bash.rb +0 -282
- data/lib/swarm_sdk/tools/clock.rb +0 -44
- data/lib/swarm_sdk/tools/delegate.rb +0 -267
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
- data/lib/swarm_sdk/tools/edit.rb +0 -145
- data/lib/swarm_sdk/tools/glob.rb +0 -166
- data/lib/swarm_sdk/tools/grep.rb +0 -235
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
- data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
- data/lib/swarm_sdk/tools/read.rb +0 -261
- data/lib/swarm_sdk/tools/registry.rb +0 -205
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
- data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
- data/lib/swarm_sdk/tools/think.rb +0 -98
- data/lib/swarm_sdk/tools/todo_write.rb +0 -235
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
- data/lib/swarm_sdk/tools/write.rb +0 -112
- data/lib/swarm_sdk/utils.rb +0 -68
- data/lib/swarm_sdk/validation_result.rb +0 -33
- data/lib/swarm_sdk/version.rb +0 -5
- data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
- data/lib/swarm_sdk/workflow/builder.rb +0 -143
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
- data/lib/swarm_sdk/workflow.rb +0 -554
- data/lib/swarm_sdk.rb +0 -524
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Tools
|
|
5
|
-
module Stores
|
|
6
|
-
# ScratchpadStorage provides volatile, shared storage
|
|
7
|
-
#
|
|
8
|
-
# Features:
|
|
9
|
-
# - Shared: All agents share the same scratchpad
|
|
10
|
-
# - Volatile: NEVER persists - all data lost when process ends
|
|
11
|
-
# - Path-based: Hierarchical organization using file-path-like addresses
|
|
12
|
-
# - Metadata-rich: Stores content + title + timestamp + size
|
|
13
|
-
# - Thread-safe: Mutex-protected operations
|
|
14
|
-
#
|
|
15
|
-
# Use for temporary, cross-agent communication within a single session.
|
|
16
|
-
class ScratchpadStorage < Storage
|
|
17
|
-
# Initialize scratchpad storage (always volatile)
|
|
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)
|
|
21
|
-
super() # Initialize parent Storage class
|
|
22
|
-
@entries = {}
|
|
23
|
-
@total_size = 0
|
|
24
|
-
@total_size_limit = total_size_limit || Defaults::Storage::TOTAL_SIZE_BYTES
|
|
25
|
-
@mutex = Mutex.new
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Write content to scratchpad
|
|
29
|
-
#
|
|
30
|
-
# @param file_path [String] Path to store content
|
|
31
|
-
# @param content [String] Content to store
|
|
32
|
-
# @param title [String] Brief title describing the content
|
|
33
|
-
# @raise [ArgumentError] If size limits are exceeded
|
|
34
|
-
# @return [Entry] The created entry
|
|
35
|
-
def write(file_path:, content:, title:)
|
|
36
|
-
@mutex.synchronize do
|
|
37
|
-
raise ArgumentError, "file_path is required" if file_path.nil? || file_path.to_s.strip.empty?
|
|
38
|
-
raise ArgumentError, "content is required" if content.nil?
|
|
39
|
-
raise ArgumentError, "title is required" if title.nil? || title.to_s.strip.empty?
|
|
40
|
-
|
|
41
|
-
content_size = content.bytesize
|
|
42
|
-
|
|
43
|
-
# Check entry size limit
|
|
44
|
-
if content_size > Defaults::Storage::ENTRY_SIZE_BYTES
|
|
45
|
-
raise ArgumentError, "Content exceeds maximum size (#{format_bytes(Defaults::Storage::ENTRY_SIZE_BYTES)}). " \
|
|
46
|
-
"Current: #{format_bytes(content_size)}"
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Calculate new total size
|
|
50
|
-
existing_entry = @entries[file_path]
|
|
51
|
-
existing_size = existing_entry ? existing_entry.size : 0
|
|
52
|
-
new_total_size = @total_size - existing_size + content_size
|
|
53
|
-
|
|
54
|
-
# Check total size limit
|
|
55
|
-
if new_total_size > @total_size_limit
|
|
56
|
-
raise ArgumentError, "Scratchpad full (#{format_bytes(@total_size_limit)} limit). " \
|
|
57
|
-
"Current: #{format_bytes(@total_size)}, " \
|
|
58
|
-
"Would be: #{format_bytes(new_total_size)}. " \
|
|
59
|
-
"Clear old entries or use smaller content."
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Create entry
|
|
63
|
-
entry = Entry.new(
|
|
64
|
-
content: content,
|
|
65
|
-
title: title,
|
|
66
|
-
updated_at: Time.now,
|
|
67
|
-
size: content_size,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# Update storage
|
|
71
|
-
@entries[file_path] = entry
|
|
72
|
-
@total_size = new_total_size
|
|
73
|
-
|
|
74
|
-
entry
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Read content from scratchpad
|
|
79
|
-
#
|
|
80
|
-
# @param file_path [String] Path to read from
|
|
81
|
-
# @raise [ArgumentError] If path not found
|
|
82
|
-
# @return [String] Content at the path
|
|
83
|
-
def read(file_path:)
|
|
84
|
-
raise ArgumentError, "file_path is required" if file_path.nil? || file_path.to_s.strip.empty?
|
|
85
|
-
|
|
86
|
-
entry = @entries[file_path]
|
|
87
|
-
raise ArgumentError, "scratchpad://#{file_path} not found" unless entry
|
|
88
|
-
|
|
89
|
-
entry.content
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Delete a specific entry
|
|
93
|
-
#
|
|
94
|
-
# @param file_path [String] Path to delete
|
|
95
|
-
# @raise [ArgumentError] If path not found
|
|
96
|
-
# @return [void]
|
|
97
|
-
def delete(file_path:)
|
|
98
|
-
@mutex.synchronize do
|
|
99
|
-
raise ArgumentError, "file_path is required" if file_path.nil? || file_path.to_s.strip.empty?
|
|
100
|
-
|
|
101
|
-
entry = @entries[file_path]
|
|
102
|
-
raise ArgumentError, "scratchpad://#{file_path} not found" unless entry
|
|
103
|
-
|
|
104
|
-
# Update total size
|
|
105
|
-
@total_size -= entry.size
|
|
106
|
-
|
|
107
|
-
# Remove entry
|
|
108
|
-
@entries.delete(file_path)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# List scratchpad entries, optionally filtered by prefix
|
|
113
|
-
#
|
|
114
|
-
# @param prefix [String, nil] Filter by path prefix
|
|
115
|
-
# @return [Array<Hash>] Array of entry metadata (path, title, size, updated_at)
|
|
116
|
-
def list(prefix: nil)
|
|
117
|
-
entries = @entries
|
|
118
|
-
|
|
119
|
-
# Filter by prefix if provided
|
|
120
|
-
if prefix && !prefix.empty?
|
|
121
|
-
entries = entries.select { |path, _| path.start_with?(prefix) }
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Return metadata sorted by path
|
|
125
|
-
entries.map do |path, entry|
|
|
126
|
-
{
|
|
127
|
-
path: path,
|
|
128
|
-
title: entry.title,
|
|
129
|
-
size: entry.size,
|
|
130
|
-
updated_at: entry.updated_at,
|
|
131
|
-
}
|
|
132
|
-
end.sort_by { |e| e[:path] }
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Search entries by glob pattern
|
|
136
|
-
#
|
|
137
|
-
# @param pattern [String] Glob pattern (e.g., "**/*.txt", "parallel/*/task_*")
|
|
138
|
-
# @return [Array<Hash>] Array of matching entry metadata, sorted by most recent first
|
|
139
|
-
def glob(pattern:)
|
|
140
|
-
raise ArgumentError, "pattern is required" if pattern.nil? || pattern.to_s.strip.empty?
|
|
141
|
-
|
|
142
|
-
# Convert glob pattern to regex
|
|
143
|
-
regex = glob_to_regex(pattern)
|
|
144
|
-
|
|
145
|
-
# Filter entries by pattern
|
|
146
|
-
matching_entries = @entries.select { |path, _| regex.match?(path) }
|
|
147
|
-
|
|
148
|
-
# Return metadata sorted by most recent first
|
|
149
|
-
matching_entries.map do |path, entry|
|
|
150
|
-
{
|
|
151
|
-
path: path,
|
|
152
|
-
title: entry.title,
|
|
153
|
-
size: entry.size,
|
|
154
|
-
updated_at: entry.updated_at,
|
|
155
|
-
}
|
|
156
|
-
end.sort_by { |e| -e[:updated_at].to_f }
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Search entry content by pattern
|
|
160
|
-
#
|
|
161
|
-
# @param pattern [String] Regular expression pattern to search for
|
|
162
|
-
# @param case_insensitive [Boolean] Whether to perform case-insensitive search
|
|
163
|
-
# @param output_mode [String] Output mode: "files_with_matches" (default), "content", or "count"
|
|
164
|
-
# @return [Array<Hash>, String] Results based on output_mode
|
|
165
|
-
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
166
|
-
raise ArgumentError, "pattern is required" if pattern.nil? || pattern.to_s.strip.empty?
|
|
167
|
-
|
|
168
|
-
# Create regex from pattern
|
|
169
|
-
flags = case_insensitive ? Regexp::IGNORECASE : 0
|
|
170
|
-
regex = Regexp.new(pattern, flags)
|
|
171
|
-
|
|
172
|
-
case output_mode
|
|
173
|
-
when "files_with_matches"
|
|
174
|
-
# Return just the paths that match
|
|
175
|
-
matching_paths = @entries.select { |_path, entry| regex.match?(entry.content) }
|
|
176
|
-
.map { |path, _| path }
|
|
177
|
-
.sort
|
|
178
|
-
matching_paths
|
|
179
|
-
when "content"
|
|
180
|
-
# Return paths with matching lines, sorted by most recent first
|
|
181
|
-
results = []
|
|
182
|
-
@entries.each do |path, entry|
|
|
183
|
-
matching_lines = []
|
|
184
|
-
entry.content.each_line.with_index(1) do |line, line_num|
|
|
185
|
-
matching_lines << { line_number: line_num, content: line.chomp } if regex.match?(line)
|
|
186
|
-
end
|
|
187
|
-
results << { path: path, matches: matching_lines, updated_at: entry.updated_at } unless matching_lines.empty?
|
|
188
|
-
end
|
|
189
|
-
results.sort_by { |r| -r[:updated_at].to_f }.map { |r| r.except(:updated_at) }
|
|
190
|
-
when "count"
|
|
191
|
-
# Return paths with match counts, sorted by most recent first
|
|
192
|
-
results = []
|
|
193
|
-
@entries.each do |path, entry|
|
|
194
|
-
count = entry.content.scan(regex).size
|
|
195
|
-
results << { path: path, count: count, updated_at: entry.updated_at } if count > 0
|
|
196
|
-
end
|
|
197
|
-
results.sort_by { |r| -r[:updated_at].to_f }.map { |r| r.except(:updated_at) }
|
|
198
|
-
else
|
|
199
|
-
raise ArgumentError, "Invalid output_mode: #{output_mode}. Must be 'files_with_matches', 'content', or 'count'"
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Clear all entries
|
|
204
|
-
#
|
|
205
|
-
# @return [void]
|
|
206
|
-
def clear
|
|
207
|
-
@mutex.synchronize do
|
|
208
|
-
@entries.clear
|
|
209
|
-
@total_size = 0
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Get current total size
|
|
214
|
-
#
|
|
215
|
-
# @return [Integer] Total size in bytes
|
|
216
|
-
attr_reader :total_size
|
|
217
|
-
|
|
218
|
-
# Get number of entries
|
|
219
|
-
#
|
|
220
|
-
# @return [Integer] Number of entries
|
|
221
|
-
def size
|
|
222
|
-
@entries.size
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Get all entries with content for snapshot
|
|
226
|
-
#
|
|
227
|
-
# Thread-safe method that returns a copy of all entries.
|
|
228
|
-
# Used by snapshot/restore functionality.
|
|
229
|
-
#
|
|
230
|
-
# @return [Hash] { path => Entry }
|
|
231
|
-
def all_entries
|
|
232
|
-
@mutex.synchronize do
|
|
233
|
-
@entries.dup
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
# Restore entries from snapshot
|
|
238
|
-
#
|
|
239
|
-
# Restores entries directly without using write() to preserve timestamps.
|
|
240
|
-
# This ensures entry ordering and metadata accuracy after restore.
|
|
241
|
-
#
|
|
242
|
-
# @param entries_data [Hash] { path => { content:, title:, updated_at:, size: } }
|
|
243
|
-
# @return [void]
|
|
244
|
-
def restore_entries(entries_data)
|
|
245
|
-
@mutex.synchronize do
|
|
246
|
-
entries_data.each do |path, data|
|
|
247
|
-
# Handle both symbol and string keys from JSON
|
|
248
|
-
content = data[:content] || data["content"]
|
|
249
|
-
title = data[:title] || data["title"]
|
|
250
|
-
updated_at_str = data[:updated_at] || data["updated_at"]
|
|
251
|
-
|
|
252
|
-
# Parse timestamp from ISO8601 string
|
|
253
|
-
updated_at = Time.parse(updated_at_str)
|
|
254
|
-
|
|
255
|
-
# Create entry with preserved timestamp
|
|
256
|
-
entry = Entry.new(
|
|
257
|
-
content: content,
|
|
258
|
-
title: title,
|
|
259
|
-
updated_at: updated_at,
|
|
260
|
-
size: content.bytesize,
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
# Update storage
|
|
264
|
-
@entries[path] = entry
|
|
265
|
-
@total_size += entry.size
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
end
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Tools
|
|
5
|
-
module Stores
|
|
6
|
-
# Abstract base class for hierarchical key-value storage with metadata
|
|
7
|
-
#
|
|
8
|
-
# Provides session-scoped storage for agents with path-based organization.
|
|
9
|
-
# Subclasses implement persistence behavior (volatile vs persistent).
|
|
10
|
-
#
|
|
11
|
-
# Features:
|
|
12
|
-
# - Path-based: Hierarchical organization using file-path-like addresses
|
|
13
|
-
# - Metadata-rich: Stores content + title + timestamp + size
|
|
14
|
-
# - Search capabilities: Glob patterns and grep-style content search
|
|
15
|
-
# - Thread-safe: Mutex-protected operations
|
|
16
|
-
class Storage
|
|
17
|
-
# Represents a single storage entry with metadata
|
|
18
|
-
Entry = Struct.new(:content, :title, :updated_at, :size, keyword_init: true)
|
|
19
|
-
|
|
20
|
-
# Initialize storage
|
|
21
|
-
#
|
|
22
|
-
# Subclasses should call super() in their initialize method.
|
|
23
|
-
# This base implementation does nothing - it exists only to satisfy RuboCop.
|
|
24
|
-
def initialize
|
|
25
|
-
# Base class initialization - subclasses implement their own logic
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Write content to storage
|
|
29
|
-
#
|
|
30
|
-
# @param file_path [String] Path to store content
|
|
31
|
-
# @param content [String] Content to store
|
|
32
|
-
# @param title [String] Brief title describing the content
|
|
33
|
-
# @raise [ArgumentError] If size limits are exceeded
|
|
34
|
-
# @return [Entry] The created entry
|
|
35
|
-
def write(file_path:, content:, title:)
|
|
36
|
-
raise NotImplementedError, "Subclass must implement #write"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Read content from storage
|
|
40
|
-
#
|
|
41
|
-
# @param file_path [String] Path to read from
|
|
42
|
-
# @raise [ArgumentError] If path not found
|
|
43
|
-
# @return [String] Content at the path
|
|
44
|
-
def read(file_path:)
|
|
45
|
-
raise NotImplementedError, "Subclass must implement #read"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Delete a specific entry
|
|
49
|
-
#
|
|
50
|
-
# @param file_path [String] Path to delete
|
|
51
|
-
# @raise [ArgumentError] If path not found
|
|
52
|
-
# @return [void]
|
|
53
|
-
def delete(file_path:)
|
|
54
|
-
raise NotImplementedError, "Subclass must implement #delete"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# List entries, optionally filtered by prefix
|
|
58
|
-
#
|
|
59
|
-
# @param prefix [String, nil] Filter by path prefix
|
|
60
|
-
# @return [Array<Hash>] Array of entry metadata (path, title, size, updated_at)
|
|
61
|
-
def list(prefix: nil)
|
|
62
|
-
raise NotImplementedError, "Subclass must implement #list"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Search entries by glob pattern
|
|
66
|
-
#
|
|
67
|
-
# @param pattern [String] Glob pattern (e.g., "**/*.txt", "parallel/*/task_*")
|
|
68
|
-
# @return [Array<Hash>] Array of matching entry metadata, sorted by most recent first
|
|
69
|
-
def glob(pattern:)
|
|
70
|
-
raise NotImplementedError, "Subclass must implement #glob"
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Search entry content by pattern
|
|
74
|
-
#
|
|
75
|
-
# @param pattern [String] Regular expression pattern to search for
|
|
76
|
-
# @param case_insensitive [Boolean] Whether to perform case-insensitive search
|
|
77
|
-
# @param output_mode [String] Output mode: "files_with_matches" (default), "content", or "count"
|
|
78
|
-
# @return [Array<Hash>, String] Results based on output_mode
|
|
79
|
-
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
80
|
-
raise NotImplementedError, "Subclass must implement #grep"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Clear all entries
|
|
84
|
-
#
|
|
85
|
-
# @return [void]
|
|
86
|
-
def clear
|
|
87
|
-
raise NotImplementedError, "Subclass must implement #clear"
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Get current total size
|
|
91
|
-
#
|
|
92
|
-
# @return [Integer] Total size in bytes
|
|
93
|
-
def total_size
|
|
94
|
-
raise NotImplementedError, "Subclass must implement #total_size"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Get number of entries
|
|
98
|
-
#
|
|
99
|
-
# @return [Integer] Number of entries
|
|
100
|
-
def size
|
|
101
|
-
raise NotImplementedError, "Subclass must implement #size"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
protected
|
|
105
|
-
|
|
106
|
-
# Format bytes to human-readable size
|
|
107
|
-
#
|
|
108
|
-
# @param bytes [Integer] Number of bytes
|
|
109
|
-
# @return [String] Formatted size (e.g., "1.5MB", "500.0KB")
|
|
110
|
-
def format_bytes(bytes)
|
|
111
|
-
if bytes >= 1_000_000
|
|
112
|
-
"#{(bytes.to_f / 1_000_000).round(1)}MB"
|
|
113
|
-
elsif bytes >= 1_000
|
|
114
|
-
"#{(bytes.to_f / 1_000).round(1)}KB"
|
|
115
|
-
else
|
|
116
|
-
"#{bytes}B"
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Convert glob pattern to regex
|
|
121
|
-
#
|
|
122
|
-
# @param pattern [String] Glob pattern
|
|
123
|
-
# @return [Regexp] Regular expression
|
|
124
|
-
def glob_to_regex(pattern)
|
|
125
|
-
# Escape special regex characters except glob wildcards
|
|
126
|
-
escaped = Regexp.escape(pattern)
|
|
127
|
-
|
|
128
|
-
# Convert glob wildcards to regex
|
|
129
|
-
# ** matches any number of directories (including zero)
|
|
130
|
-
escaped = escaped.gsub('\*\*', ".*")
|
|
131
|
-
# * matches anything except directory separator
|
|
132
|
-
escaped = escaped.gsub('\*', "[^/]*")
|
|
133
|
-
# ? matches single character except directory separator
|
|
134
|
-
escaped = escaped.gsub('\?', "[^/]")
|
|
135
|
-
|
|
136
|
-
# Anchor to start and end
|
|
137
|
-
Regexp.new("\\A#{escaped}\\z")
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Tools
|
|
5
|
-
module Stores
|
|
6
|
-
# TodoManager provides per-agent todo list storage
|
|
7
|
-
#
|
|
8
|
-
# Each agent maintains its own independent todo list that persists
|
|
9
|
-
# throughout the agent's execution session. This allows agents to
|
|
10
|
-
# track progress on complex multi-step tasks.
|
|
11
|
-
class TodoManager
|
|
12
|
-
@storage = {}
|
|
13
|
-
@mutex = Mutex.new
|
|
14
|
-
|
|
15
|
-
class << self
|
|
16
|
-
# Get the current todo list for an agent
|
|
17
|
-
#
|
|
18
|
-
# @param agent_id [Symbol, String] Unique agent identifier
|
|
19
|
-
# @return [Array<Hash>] Array of todo items
|
|
20
|
-
def get_todos(agent_id)
|
|
21
|
-
@mutex.synchronize do
|
|
22
|
-
@storage[agent_id.to_sym] ||= []
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Set the todo list for an agent
|
|
27
|
-
#
|
|
28
|
-
# @param agent_id [Symbol, String] Unique agent identifier
|
|
29
|
-
# @param todos [Array<Hash>] Array of todo items
|
|
30
|
-
# @return [Array<Hash>] The stored todos
|
|
31
|
-
def set_todos(agent_id, todos)
|
|
32
|
-
@mutex.synchronize do
|
|
33
|
-
@storage[agent_id.to_sym] = todos
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Clear all todos for an agent
|
|
38
|
-
#
|
|
39
|
-
# @param agent_id [Symbol, String] Unique agent identifier
|
|
40
|
-
def clear_todos(agent_id)
|
|
41
|
-
@mutex.synchronize do
|
|
42
|
-
@storage.delete(agent_id.to_sym)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Clear all todos for all agents
|
|
47
|
-
def clear_all
|
|
48
|
-
@mutex.synchronize do
|
|
49
|
-
@storage.clear
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Get summary of all agent todo lists
|
|
54
|
-
#
|
|
55
|
-
# @return [Hash] Map of agent_id => todo count
|
|
56
|
-
def summary
|
|
57
|
-
@mutex.synchronize do
|
|
58
|
-
@storage.transform_values(&:size)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Tools
|
|
5
|
-
# Think tool for explicit reasoning and planning
|
|
6
|
-
#
|
|
7
|
-
# Allows the agent to write down thoughts, plans, strategies, and intermediate
|
|
8
|
-
# calculations. These thoughts become part of the conversation context, enabling
|
|
9
|
-
# better attention and reasoning through complex problems.
|
|
10
|
-
#
|
|
11
|
-
# This is inspired by research showing that explicitly articulating reasoning steps
|
|
12
|
-
# (chain-of-thought prompting) leads to significantly better outcomes, especially
|
|
13
|
-
# for complex tasks requiring multi-step reasoning or arithmetic.
|
|
14
|
-
class Think < RubyLLM::Tool
|
|
15
|
-
def name
|
|
16
|
-
"Think"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
description <<~DESC
|
|
20
|
-
**IMPORTANT: You SHOULD use this tool frequently throughout your work. Using this tool leads to significantly
|
|
21
|
-
better outcomes and more accurate solutions. Make it a habit to think before acting.**
|
|
22
|
-
|
|
23
|
-
This tool allows you to write down your thoughts, plans, strategies, and intermediate calculations.
|
|
24
|
-
Think of it as your working memory - just as humans think before speaking or acting, you should think before
|
|
25
|
-
using other tools or providing responses.
|
|
26
|
-
|
|
27
|
-
**STRONGLY RECOMMENDED to use this tool:**
|
|
28
|
-
- **ALWAYS** before starting any task (even simple ones)
|
|
29
|
-
- **ALWAYS** when you need to do any arithmetic or counting
|
|
30
|
-
- **ALWAYS** after reading files or getting search results to process what you learned
|
|
31
|
-
- **FREQUENTLY** between steps to track progress and plan next actions
|
|
32
|
-
|
|
33
|
-
This is your private thinking space - use it liberally to enhance your problem-solving capabilities. Recording
|
|
34
|
-
your thoughts helps you maintain context across multiple steps and remember important information throughout your task.
|
|
35
|
-
|
|
36
|
-
When and how to use this tool:
|
|
37
|
-
|
|
38
|
-
1. **Before starting any complex task**: Write down your understanding of the problem, break it into smaller
|
|
39
|
-
sub-tasks, and create a step-by-step plan. Example:
|
|
40
|
-
- "The user wants me to refactor this codebase. Let me first understand the structure..."
|
|
41
|
-
- "I need to: 1) Analyze current architecture, 2) Identify pain points, 3) Propose changes..."
|
|
42
|
-
|
|
43
|
-
2. **For arithmetic and calculations**: Work through math problems step by step. Example:
|
|
44
|
-
- "If we have 150 requests/second and each takes 20ms, that's 150 * 0.02 = 3 seconds of CPU time..."
|
|
45
|
-
- "Converting 2GB to bytes: 2 * 1024 * 1024 * 1024 = 2,147,483,648 bytes"
|
|
46
|
-
|
|
47
|
-
3. **After completing sub-tasks**: Summarize what you've accomplished and what remains. Example:
|
|
48
|
-
- "I've successfully implemented the authentication module. Next, I need to integrate it with the API..."
|
|
49
|
-
- "Fixed 3 out of 5 bugs. Remaining: memory leak in parser, race condition in worker thread"
|
|
50
|
-
|
|
51
|
-
4. **When encountering complexity**: Break down complex logic or decisions. Example:
|
|
52
|
-
- "This function has multiple edge cases. Let me list them: null input, empty array, negative numbers..."
|
|
53
|
-
- "The user's request is ambiguous. Possible interpretations: A) modify existing code, B) create new module..."
|
|
54
|
-
|
|
55
|
-
5. **For remembering context**: Store important information you'll need later. Example:
|
|
56
|
-
- "Important: The user mentioned they're using Ruby 3.2, so I can use pattern matching"
|
|
57
|
-
- "File structure: main.rb requires from lib/, config is in config.yml"
|
|
58
|
-
|
|
59
|
-
6. **When debugging or analyzing**: Track your investigation process. Example:
|
|
60
|
-
- "The error occurs in line 42. Let me trace backwards: function called from main(), receives data from..."
|
|
61
|
-
- "Hypothesis: the bug might be due to timezone differences. Let me check..."
|
|
62
|
-
|
|
63
|
-
7. **For creative problem-solving**: Brainstorm multiple approaches before choosing one. Example:
|
|
64
|
-
- "Approaches to optimize this: 1) Add caching, 2) Use parallel processing, 3) Optimize algorithm..."
|
|
65
|
-
- "Design patterns that could work here: Factory, Observer, or maybe Strategy pattern..."
|
|
66
|
-
|
|
67
|
-
**Remember: The most successful agents use this tool 5-10 times per task on average. If you haven't used this
|
|
68
|
-
tool in the last 2-3 actions, you probably should. Using this tool is a sign of thoughtful, methodical problem
|
|
69
|
-
solving and leads to fewer mistakes and better solutions.**
|
|
70
|
-
|
|
71
|
-
Your thoughts persist throughout your session as part of the conversation history, so you can refer
|
|
72
|
-
back to earlier thinking. Use clear formatting and organization to make it easy to reference
|
|
73
|
-
later. Don't hesitate to think out loud - this tool is designed to augment your cognitive capabilities and help
|
|
74
|
-
you deliver better solutions.
|
|
75
|
-
|
|
76
|
-
**CRITICAL:** The Think tool takes only one parameter: thoughts. Do not include any other parameters.
|
|
77
|
-
DESC
|
|
78
|
-
|
|
79
|
-
param :thoughts,
|
|
80
|
-
type: "string",
|
|
81
|
-
desc: "Your thoughts, plans, calculations, or any notes you want to record",
|
|
82
|
-
required: true
|
|
83
|
-
|
|
84
|
-
def execute(**kwargs)
|
|
85
|
-
<<~RESP
|
|
86
|
-
Thought noted.
|
|
87
|
-
RESP
|
|
88
|
-
# <system-reminder>The user cannot see your thoughts. You MUST NOT stop without giving the user a response.</system-reminder>
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
private
|
|
92
|
-
|
|
93
|
-
def validation_error(message)
|
|
94
|
-
"<tool_use_error>InputValidationError: #{message}</tool_use_error>"
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|