swarm_memory 2.1.1 → 2.1.3
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/claude_swarm/cli.rb +9 -11
- data/lib/claude_swarm/commands/ps.rb +1 -2
- data/lib/claude_swarm/configuration.rb +30 -7
- data/lib/claude_swarm/mcp_generator.rb +4 -10
- data/lib/claude_swarm/orchestrator.rb +43 -44
- data/lib/claude_swarm/system_utils.rb +4 -4
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm.rb +5 -9
- data/lib/swarm_cli/commands/mcp_serve.rb +2 -2
- data/lib/swarm_cli/commands/mcp_tools.rb +3 -3
- data/lib/swarm_cli/config_loader.rb +14 -13
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_cli.rb +2 -0
- data/lib/swarm_memory/adapters/base.rb +4 -4
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +0 -12
- data/lib/swarm_memory/core/storage.rb +66 -6
- data/lib/swarm_memory/core/storage_read_tracker.rb +51 -14
- data/lib/swarm_memory/integration/cli_registration.rb +3 -2
- data/lib/swarm_memory/integration/sdk_plugin.rb +24 -4
- data/lib/swarm_memory/optimization/defragmenter.rb +4 -0
- data/lib/swarm_memory/tools/memory_edit.rb +3 -2
- data/lib/swarm_memory/tools/memory_glob.rb +24 -1
- data/lib/swarm_memory/tools/memory_multi_edit.rb +2 -2
- data/lib/swarm_memory/tools/memory_read.rb +3 -3
- data/lib/swarm_memory/tools/memory_write.rb +2 -2
- data/lib/swarm_memory/version.rb +1 -1
- data/lib/swarm_memory.rb +7 -0
- data/lib/swarm_sdk/agent/builder.rb +33 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +33 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +41 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +11 -27
- data/lib/swarm_sdk/agent/chat.rb +199 -52
- data/lib/swarm_sdk/agent/context.rb +6 -2
- data/lib/swarm_sdk/agent/context_manager.rb +6 -0
- data/lib/swarm_sdk/agent/definition.rb +32 -23
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +180 -0
- data/lib/swarm_sdk/configuration.rb +420 -103
- data/lib/swarm_sdk/events_to_messages.rb +181 -0
- data/lib/swarm_sdk/log_collector.rb +31 -5
- data/lib/swarm_sdk/log_stream.rb +37 -8
- data/lib/swarm_sdk/model_aliases.json +4 -1
- data/lib/swarm_sdk/node/agent_config.rb +39 -9
- data/lib/swarm_sdk/node/builder.rb +158 -42
- data/lib/swarm_sdk/node_context.rb +75 -0
- data/lib/swarm_sdk/node_orchestrator.rb +492 -18
- data/lib/swarm_sdk/plugin.rb +73 -1
- data/lib/swarm_sdk/proc_helpers.rb +53 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -126
- data/lib/swarm_sdk/providers/openai_with_responses.rb +22 -15
- data/lib/swarm_sdk/restore_result.rb +65 -0
- data/lib/swarm_sdk/result.rb +32 -6
- data/lib/swarm_sdk/snapshot.rb +156 -0
- data/lib/swarm_sdk/snapshot_from_events.rb +386 -0
- data/lib/swarm_sdk/state_restorer.rb +491 -0
- data/lib/swarm_sdk/state_snapshot.rb +369 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +360 -55
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +28 -1
- data/lib/swarm_sdk/swarm/builder.rb +208 -11
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +67 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +46 -11
- data/lib/swarm_sdk/swarm.rb +367 -90
- data/lib/swarm_sdk/swarm_loader.rb +145 -0
- data/lib/swarm_sdk/swarm_registry.rb +136 -0
- data/lib/swarm_sdk/tools/delegate.rb +94 -9
- data/lib/swarm_sdk/tools/read.rb +17 -5
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +23 -2
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +23 -2
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +21 -4
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +47 -12
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +45 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +4 -4
- data/lib/swarm_sdk/tools/think.rb +4 -1
- data/lib/swarm_sdk/tools/todo_write.rb +20 -8
- data/lib/swarm_sdk/utils.rb +18 -0
- data/lib/swarm_sdk/validation_result.rb +33 -0
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk.rb +365 -28
- metadata +17 -5
|
@@ -3,39 +3,74 @@
|
|
|
3
3
|
module SwarmSDK
|
|
4
4
|
module Tools
|
|
5
5
|
module Stores
|
|
6
|
-
# ReadTracker manages read-file tracking for all agents
|
|
6
|
+
# ReadTracker manages read-file tracking for all agents with content digest verification
|
|
7
7
|
#
|
|
8
8
|
# This module maintains a global registry of which files each agent has read
|
|
9
|
-
# during their conversation
|
|
10
|
-
# and "read-before-edit" rules that ensure
|
|
9
|
+
# during their conversation along with SHA256 digests of the content. This enables
|
|
10
|
+
# enforcement of the "read-before-write" and "read-before-edit" rules that ensure
|
|
11
|
+
# agents have context before modifying files, AND prevents editing files that have
|
|
12
|
+
# changed externally since being read.
|
|
11
13
|
#
|
|
12
|
-
# Each agent maintains an independent
|
|
14
|
+
# Each agent maintains an independent map of read files to content digests.
|
|
13
15
|
module ReadTracker
|
|
14
|
-
@read_files = {}
|
|
16
|
+
@read_files = {} # { agent_id => { file_path => sha256_digest } }
|
|
15
17
|
@mutex = Mutex.new
|
|
16
18
|
|
|
17
19
|
class << self
|
|
18
|
-
# Register that an agent has read a file
|
|
20
|
+
# Register that an agent has read a file with content digest
|
|
19
21
|
#
|
|
20
22
|
# @param agent_id [Symbol] The agent identifier
|
|
21
23
|
# @param file_path [String] The absolute path to the file
|
|
22
|
-
|
|
24
|
+
# @param content [String] File content (for digest calculation)
|
|
25
|
+
# @return [String] The calculated SHA256 digest
|
|
26
|
+
def register_read(agent_id, file_path, content)
|
|
23
27
|
@mutex.synchronize do
|
|
24
|
-
@read_files[agent_id] ||=
|
|
25
|
-
|
|
28
|
+
@read_files[agent_id] ||= {}
|
|
29
|
+
digest = Digest::SHA256.hexdigest(content)
|
|
30
|
+
@read_files[agent_id][File.expand_path(file_path)] = digest
|
|
31
|
+
digest
|
|
26
32
|
end
|
|
27
33
|
end
|
|
28
34
|
|
|
29
|
-
# Check if an agent has read a file
|
|
35
|
+
# Check if an agent has read a file AND content hasn't changed
|
|
30
36
|
#
|
|
31
37
|
# @param agent_id [Symbol] The agent identifier
|
|
32
38
|
# @param file_path [String] The absolute path to the file
|
|
33
|
-
# @return [Boolean] true if
|
|
39
|
+
# @return [Boolean] true if agent read file and content matches
|
|
34
40
|
def file_read?(agent_id, file_path)
|
|
35
41
|
@mutex.synchronize do
|
|
36
42
|
return false unless @read_files[agent_id]
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
expanded_path = File.expand_path(file_path)
|
|
45
|
+
stored_digest = @read_files[agent_id][expanded_path]
|
|
46
|
+
return false unless stored_digest
|
|
47
|
+
|
|
48
|
+
# Check if file still exists and matches stored digest
|
|
49
|
+
return false unless File.exist?(expanded_path)
|
|
50
|
+
|
|
51
|
+
current_digest = Digest::SHA256.hexdigest(File.read(expanded_path))
|
|
52
|
+
current_digest == stored_digest
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Get all read files with digests for snapshot
|
|
57
|
+
#
|
|
58
|
+
# @param agent_id [Symbol] The agent identifier
|
|
59
|
+
# @return [Hash] { file_path => digest }
|
|
60
|
+
def get_read_files(agent_id)
|
|
61
|
+
@mutex.synchronize do
|
|
62
|
+
@read_files[agent_id]&.dup || {}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Restore read files with digests from snapshot
|
|
67
|
+
#
|
|
68
|
+
# @param agent_id [Symbol] The agent identifier
|
|
69
|
+
# @param files_with_digests [Hash] { file_path => digest }
|
|
70
|
+
# @return [void]
|
|
71
|
+
def restore_read_files(agent_id, files_with_digests)
|
|
72
|
+
@mutex.synchronize do
|
|
73
|
+
@read_files[agent_id] = files_with_digests.dup
|
|
39
74
|
end
|
|
40
75
|
end
|
|
41
76
|
|
|
@@ -218,6 +218,51 @@ module SwarmSDK
|
|
|
218
218
|
def size
|
|
219
219
|
@entries.size
|
|
220
220
|
end
|
|
221
|
+
|
|
222
|
+
# Get all entries with content for snapshot
|
|
223
|
+
#
|
|
224
|
+
# Thread-safe method that returns a copy of all entries.
|
|
225
|
+
# Used by snapshot/restore functionality.
|
|
226
|
+
#
|
|
227
|
+
# @return [Hash] { path => Entry }
|
|
228
|
+
def all_entries
|
|
229
|
+
@mutex.synchronize do
|
|
230
|
+
@entries.dup
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Restore entries from snapshot
|
|
235
|
+
#
|
|
236
|
+
# Restores entries directly without using write() to preserve timestamps.
|
|
237
|
+
# This ensures entry ordering and metadata accuracy after restore.
|
|
238
|
+
#
|
|
239
|
+
# @param entries_data [Hash] { path => { content:, title:, updated_at:, size: } }
|
|
240
|
+
# @return [void]
|
|
241
|
+
def restore_entries(entries_data)
|
|
242
|
+
@mutex.synchronize do
|
|
243
|
+
entries_data.each do |path, data|
|
|
244
|
+
# Handle both symbol and string keys from JSON
|
|
245
|
+
content = data[:content] || data["content"]
|
|
246
|
+
title = data[:title] || data["title"]
|
|
247
|
+
updated_at_str = data[:updated_at] || data["updated_at"]
|
|
248
|
+
|
|
249
|
+
# Parse timestamp from ISO8601 string
|
|
250
|
+
updated_at = Time.parse(updated_at_str)
|
|
251
|
+
|
|
252
|
+
# Create entry with preserved timestamp
|
|
253
|
+
entry = Entry.new(
|
|
254
|
+
content: content,
|
|
255
|
+
title: title,
|
|
256
|
+
updated_at: updated_at,
|
|
257
|
+
size: content.bytesize,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Update storage
|
|
261
|
+
@entries[path] = entry
|
|
262
|
+
@total_size += entry.size
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
221
266
|
end
|
|
222
267
|
end
|
|
223
268
|
end
|
|
@@ -14,11 +14,11 @@ 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 (
|
|
18
|
-
MAX_ENTRY_SIZE =
|
|
17
|
+
# Maximum size per entry (3MB)
|
|
18
|
+
MAX_ENTRY_SIZE = 3_000_000
|
|
19
19
|
|
|
20
|
-
# Maximum total storage size (
|
|
21
|
-
MAX_TOTAL_SIZE =
|
|
20
|
+
# Maximum total storage size (100GB)
|
|
21
|
+
MAX_TOTAL_SIZE = 100_000_000_000
|
|
22
22
|
|
|
23
23
|
# Represents a single storage entry with metadata
|
|
24
24
|
Entry = Struct.new(:content, :title, :updated_at, :size, keyword_init: true)
|
|
@@ -82,7 +82,10 @@ module SwarmSDK
|
|
|
82
82
|
required: true
|
|
83
83
|
|
|
84
84
|
def execute(**kwargs)
|
|
85
|
-
|
|
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>
|
|
86
89
|
end
|
|
87
90
|
|
|
88
91
|
private
|
|
@@ -8,17 +8,17 @@ module SwarmSDK
|
|
|
8
8
|
# Each agent maintains its own independent todo list.
|
|
9
9
|
class TodoWrite < RubyLLM::Tool
|
|
10
10
|
description <<~DESC
|
|
11
|
-
Use this tool to create and manage a structured task list for your current
|
|
11
|
+
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
12
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
13
13
|
|
|
14
14
|
## When to Use This Tool
|
|
15
15
|
Use this tool proactively in these scenarios:
|
|
16
16
|
|
|
17
17
|
**CRITICAL**: Follow this workflow for multi-step tasks:
|
|
18
|
-
1. FIRST: Analyze the task scope (
|
|
19
|
-
2. SECOND: Create a COMPLETE todo list with ALL known tasks BEFORE starting
|
|
18
|
+
1. FIRST: Analyze the task scope (gather information, understand requirements)
|
|
19
|
+
2. SECOND: Create a COMPLETE todo list with ALL known tasks BEFORE starting work
|
|
20
20
|
3. THIRD: Execute tasks, marking in_progress → completed as you work
|
|
21
|
-
4. ONLY add new todos if unexpected work is discovered during
|
|
21
|
+
4. ONLY add new todos if unexpected work is discovered during execution
|
|
22
22
|
|
|
23
23
|
Use the todo list when:
|
|
24
24
|
1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
@@ -27,7 +27,7 @@ module SwarmSDK
|
|
|
27
27
|
4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
28
28
|
5. After receiving new instructions - After analyzing scope, create complete todo list before starting work
|
|
29
29
|
6. When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time
|
|
30
|
-
7. After completing a task - Mark it as completed and add any new follow-up tasks discovered during
|
|
30
|
+
7. After completing a task - Mark it as completed and add any new follow-up tasks discovered during execution
|
|
31
31
|
|
|
32
32
|
## When NOT to Use This Tool
|
|
33
33
|
|
|
@@ -73,9 +73,21 @@ module SwarmSDK
|
|
|
73
73
|
- Create specific, actionable items
|
|
74
74
|
- Break complex tasks into smaller, manageable steps
|
|
75
75
|
- Use clear, descriptive task names
|
|
76
|
-
- Always provide both forms
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
- Always provide both forms (content and activeForm)
|
|
77
|
+
|
|
78
|
+
## Examples
|
|
79
|
+
|
|
80
|
+
**Coding Tasks**:
|
|
81
|
+
- content: "Fix authentication bug in login handler"
|
|
82
|
+
- activeForm: "Fixing authentication bug in login handler"
|
|
83
|
+
|
|
84
|
+
**Non-Coding Tasks**:
|
|
85
|
+
- content: "Analyze customer feedback from Q4 survey"
|
|
86
|
+
- activeForm: "Analyzing customer feedback from Q4 survey"
|
|
87
|
+
|
|
88
|
+
**Research Tasks**:
|
|
89
|
+
- content: "Research best practices for API rate limiting"
|
|
90
|
+
- activeForm: "Researching best practices for API rate limiting"
|
|
79
91
|
|
|
80
92
|
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.
|
|
81
93
|
DESC
|
data/lib/swarm_sdk/utils.rb
CHANGED
|
@@ -45,6 +45,24 @@ module SwarmSDK
|
|
|
45
45
|
obj
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
# Convert hash to YAML string
|
|
50
|
+
#
|
|
51
|
+
# Converts a Ruby hash to a YAML string. Useful for creating inline
|
|
52
|
+
# swarm definitions from hash configurations.
|
|
53
|
+
#
|
|
54
|
+
# @param hash [Hash] Hash to convert
|
|
55
|
+
# @return [String] YAML string representation
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# config = { version: 2, swarm: { name: "Test" } }
|
|
59
|
+
# Utils.hash_to_yaml(config)
|
|
60
|
+
# # => "---\nversion: 2\nswarm:\n name: Test\n"
|
|
61
|
+
def hash_to_yaml(hash)
|
|
62
|
+
# Convert symbols to strings for valid YAML
|
|
63
|
+
stringified = stringify_keys(hash)
|
|
64
|
+
stringified.to_yaml
|
|
65
|
+
end
|
|
48
66
|
end
|
|
49
67
|
end
|
|
50
68
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmSDK
|
|
4
|
+
# Internal result object for validation phase during snapshot restore
|
|
5
|
+
#
|
|
6
|
+
# Used during restore to track which agents can be restored and which
|
|
7
|
+
# need to be skipped due to configuration mismatches.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class ValidationResult
|
|
11
|
+
attr_reader :warnings,
|
|
12
|
+
:skipped_agents,
|
|
13
|
+
:restorable_agents,
|
|
14
|
+
:skipped_delegations,
|
|
15
|
+
:restorable_delegations
|
|
16
|
+
|
|
17
|
+
# Initialize validation result
|
|
18
|
+
#
|
|
19
|
+
# @param warnings [Array<Hash>] Warning messages with details
|
|
20
|
+
# @param skipped_agents [Array<Symbol>] Names of agents that can't be restored
|
|
21
|
+
# @param restorable_agents [Array<Symbol>] Names of agents that can be restored
|
|
22
|
+
# @param skipped_delegations [Array<String>] Names of delegations that can't be restored
|
|
23
|
+
# @param restorable_delegations [Array<String>] Names of delegations that can be restored
|
|
24
|
+
def initialize(warnings:, skipped_agents:, restorable_agents:,
|
|
25
|
+
skipped_delegations:, restorable_delegations:)
|
|
26
|
+
@warnings = warnings
|
|
27
|
+
@skipped_agents = skipped_agents
|
|
28
|
+
@restorable_agents = restorable_agents
|
|
29
|
+
@skipped_delegations = skipped_delegations
|
|
30
|
+
@restorable_delegations = restorable_delegations
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/swarm_sdk/version.rb
CHANGED