claude_swarm 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +3 -0
- data/CLAUDE.md +0 -1
- data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +12 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +139 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +249 -1
- data/docs/v2/README.md +15 -5
- data/docs/v2/guides/complete-tutorial.md +93 -7
- data/docs/v2/guides/getting-started.md +3 -1
- data/docs/v2/guides/memory-adapters.md +41 -0
- data/docs/v2/guides/{migrating-to-2.3.md → migrating-to-2.x.md} +213 -8
- data/docs/v2/guides/plugins.md +52 -5
- data/docs/v2/guides/rails-integration.md +6 -0
- data/docs/v2/guides/swarm-memory.md +2 -13
- data/docs/v2/reference/cli.md +0 -1
- data/docs/v2/reference/configuration_reference.md +300 -0
- data/docs/v2/reference/event_payload_structures.md +26 -4
- data/docs/v2/reference/ruby-dsl.md +457 -4
- data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
- data/docs/v2/reference/yaml.md +2 -2
- data/lib/claude_swarm/mcp_generator.rb +1 -1
- data/lib/claude_swarm/orchestrator.rb +8 -1
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_memory/core/semantic_index.rb +10 -2
- data/lib/swarm_memory/core/storage.rb +7 -2
- data/lib/swarm_memory/dsl/memory_config.rb +37 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +120 -27
- data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
- data/lib/swarm_memory/tools/load_skill.rb +0 -1
- data/lib/swarm_memory/tools/memory_edit.rb +2 -1
- data/lib/swarm_memory/tools/memory_read.rb +1 -1
- data/lib/swarm_memory/version.rb +1 -1
- data/lib/swarm_memory.rb +7 -5
- data/lib/swarm_sdk/agent/chat.rb +1 -1
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +4 -0
- data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +1 -1
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +38 -4
- data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +2 -2
- data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +3 -5
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +48 -0
- data/lib/swarm_sdk/agent/context.rb +1 -2
- data/lib/swarm_sdk/agent/definition.rb +3 -3
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +1 -1
- data/lib/swarm_sdk/agent_registry.rb +146 -0
- data/lib/swarm_sdk/builders/base_builder.rb +91 -12
- data/lib/swarm_sdk/config.rb +302 -0
- data/lib/swarm_sdk/configuration/parser.rb +22 -2
- data/lib/swarm_sdk/configuration.rb +13 -4
- data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
- data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
- data/lib/swarm_sdk/hooks/adapter.rb +3 -3
- data/lib/swarm_sdk/hooks/shell_executor.rb +4 -3
- data/lib/swarm_sdk/models.json +4333 -1
- data/lib/swarm_sdk/models.rb +43 -2
- data/lib/swarm_sdk/plugin.rb +2 -2
- data/lib/swarm_sdk/result.rb +52 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +1 -1
- data/lib/swarm_sdk/swarm/hook_triggers.rb +1 -0
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +1 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +18 -4
- data/lib/swarm_sdk/swarm.rb +76 -13
- data/lib/swarm_sdk/tools/bash.rb +7 -9
- data/lib/swarm_sdk/tools/glob.rb +5 -5
- data/lib/swarm_sdk/tools/read.rb +8 -8
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +4 -3
- data/lib/swarm_sdk/tools/web_fetch.rb +20 -18
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk/workflow/builder.rb +49 -0
- data/lib/swarm_sdk/workflow/node_builder.rb +4 -2
- data/lib/swarm_sdk/workflow/transformer_executor.rb +4 -3
- data/lib/swarm_sdk.rb +261 -105
- data/swarm_cli.gemspec +1 -1
- data/swarm_memory.gemspec +8 -3
- data/swarm_sdk.gemspec +4 -4
- data/team_full.yml +104 -300
- metadata +9 -5
- data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
- /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
|
@@ -32,6 +32,132 @@ result = swarm.execute("Task prompt")
|
|
|
32
32
|
|
|
33
33
|
## Top-Level Methods
|
|
34
34
|
|
|
35
|
+
### SwarmSDK.agent
|
|
36
|
+
|
|
37
|
+
Register an agent globally for reuse across multiple swarms.
|
|
38
|
+
|
|
39
|
+
**Signature:**
|
|
40
|
+
```ruby
|
|
41
|
+
SwarmSDK.agent(name) {|builder| ... } → void
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Parameters:**
|
|
45
|
+
- `name` (Symbol, required): Unique agent name
|
|
46
|
+
- `block` (required): Agent configuration block (uses [Agent Builder DSL](#agent-builder-dsl))
|
|
47
|
+
|
|
48
|
+
**Description:**
|
|
49
|
+
Registers an agent definition in a global registry, allowing it to be referenced by name in any swarm or workflow without re-defining it. This promotes code reuse and separation of concerns.
|
|
50
|
+
|
|
51
|
+
**Duplicate Registration:**
|
|
52
|
+
Raises `ArgumentError` if an agent with the same name is already registered. Use `SwarmSDK.clear_agent_registry!` to reset.
|
|
53
|
+
|
|
54
|
+
**Example - Define agents in separate files:**
|
|
55
|
+
```ruby
|
|
56
|
+
# agents/backend.rb
|
|
57
|
+
SwarmSDK.agent :backend do
|
|
58
|
+
model "claude-sonnet-4"
|
|
59
|
+
description "Backend developer"
|
|
60
|
+
system_prompt "You build APIs and databases"
|
|
61
|
+
tools :Read, :Edit, :Bash
|
|
62
|
+
# Note: Don't set delegates_to here - it's swarm-specific!
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# agents/database.rb
|
|
66
|
+
SwarmSDK.agent :database do
|
|
67
|
+
model "claude-sonnet-4"
|
|
68
|
+
description "Database specialist"
|
|
69
|
+
system_prompt "You design schemas and write migrations"
|
|
70
|
+
tools :Read, :Edit
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Example - Reference in swarms:**
|
|
75
|
+
```ruby
|
|
76
|
+
# Load agent definitions
|
|
77
|
+
require_relative "agents/backend"
|
|
78
|
+
require_relative "agents/database"
|
|
79
|
+
|
|
80
|
+
# Reference by name and configure delegation (swarm-specific)
|
|
81
|
+
swarm = SwarmSDK.build do
|
|
82
|
+
name "Dev Team"
|
|
83
|
+
lead :backend
|
|
84
|
+
|
|
85
|
+
agent :backend do
|
|
86
|
+
delegates_to :database # Delegation is swarm-specific
|
|
87
|
+
end
|
|
88
|
+
agent :database # Pulls from registry as-is
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Example - Override registry settings:**
|
|
93
|
+
```ruby
|
|
94
|
+
swarm = SwarmSDK.build do
|
|
95
|
+
name "Extended Team"
|
|
96
|
+
lead :backend
|
|
97
|
+
|
|
98
|
+
# Registry config applied first, then override block
|
|
99
|
+
agent :backend do
|
|
100
|
+
delegates_to :database, :cache # Set delegation for this swarm
|
|
101
|
+
tools :CustomTool # Adds to registry tools
|
|
102
|
+
timeout 300 # Overrides registry timeout
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Workflow Auto-Resolution:**
|
|
108
|
+
Agents referenced in workflow nodes are automatically resolved from the registry if not defined at workflow level:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
SwarmSDK.workflow do
|
|
112
|
+
name "Pipeline"
|
|
113
|
+
start_node :build
|
|
114
|
+
|
|
115
|
+
# No need to define :backend here - auto-resolved from registry!
|
|
116
|
+
node :build do
|
|
117
|
+
agent(:backend) # Resolved from global registry
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
node :test do
|
|
121
|
+
agent(:backend).delegates_to(:database) # Both resolved from registry
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Use Cases:**
|
|
127
|
+
- **Code reuse**: Define agents once, use in multiple swarms
|
|
128
|
+
- **Separation of concerns**: Agent definitions in dedicated files
|
|
129
|
+
- **Testing**: Clear registry between tests with `SwarmSDK.clear_agent_registry!`
|
|
130
|
+
- **Organization**: Large projects with many agents
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### SwarmSDK.clear_agent_registry!
|
|
135
|
+
|
|
136
|
+
Clear all registered agents from the global registry.
|
|
137
|
+
|
|
138
|
+
**Signature:**
|
|
139
|
+
```ruby
|
|
140
|
+
SwarmSDK.clear_agent_registry! → void
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Description:**
|
|
144
|
+
Removes all agents from the global registry. Primarily used for testing to ensure isolation between test cases.
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
```ruby
|
|
148
|
+
# In test setup
|
|
149
|
+
def setup
|
|
150
|
+
SwarmSDK.clear_agent_registry!
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Or in RSpec
|
|
154
|
+
before(:each) do
|
|
155
|
+
SwarmSDK.clear_agent_registry!
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
35
161
|
### SwarmSDK.build
|
|
36
162
|
|
|
37
163
|
Build a simple multi-agent swarm.
|
|
@@ -161,6 +287,8 @@ SwarmSDK.configure {|config| ... } → void
|
|
|
161
287
|
- `webfetch_max_tokens` (Integer): Maximum tokens for WebFetch LLM responses (default: 4096)
|
|
162
288
|
- `allow_filesystem_tools` (Boolean): Enable/disable filesystem tools globally (default: true)
|
|
163
289
|
|
|
290
|
+
**See [Configuration Reference](configuration_reference.md) for the complete list of 45+ configuration options including API keys, timeouts, limits, and more.**
|
|
291
|
+
|
|
164
292
|
**Description:**
|
|
165
293
|
Global configuration that applies to all swarms.
|
|
166
294
|
|
|
@@ -198,13 +326,220 @@ SwarmSDK.configure do |config|
|
|
|
198
326
|
end
|
|
199
327
|
|
|
200
328
|
# Can also set directly
|
|
201
|
-
SwarmSDK.
|
|
329
|
+
SwarmSDK.config.allow_filesystem_tools = false
|
|
202
330
|
|
|
203
331
|
# Or via environment variable
|
|
204
332
|
ENV['SWARM_SDK_ALLOW_FILESYSTEM_TOOLS'] = 'false'
|
|
205
333
|
|
|
206
334
|
# Reset to defaults (disables WebFetch LLM processing, enables filesystem tools)
|
|
207
|
-
SwarmSDK.
|
|
335
|
+
SwarmSDK.reset_config!
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### Custom Tool Registration
|
|
341
|
+
|
|
342
|
+
Simple API for registering custom tools without creating full plugins.
|
|
343
|
+
|
|
344
|
+
#### SwarmSDK.register_tool
|
|
345
|
+
|
|
346
|
+
Register a custom RubyLLM::Tool for use in swarms.
|
|
347
|
+
|
|
348
|
+
**Signatures:**
|
|
349
|
+
```ruby
|
|
350
|
+
SwarmSDK.register_tool(tool_class) → Symbol
|
|
351
|
+
SwarmSDK.register_tool(name, tool_class) → Symbol
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Parameters:**
|
|
355
|
+
- `tool_class` (Class): RubyLLM::Tool subclass to register
|
|
356
|
+
- `name` (Symbol, optional): Explicit tool name (if not provided, name is inferred)
|
|
357
|
+
|
|
358
|
+
**Returns:**
|
|
359
|
+
- `Symbol`: The registered tool name
|
|
360
|
+
|
|
361
|
+
**Description:**
|
|
362
|
+
Registers a custom tool globally so it can be used in any swarm. Tools are registered by name and can be referenced in agent configurations.
|
|
363
|
+
|
|
364
|
+
**Tool Lookup Order:**
|
|
365
|
+
1. Plugin tools (from PluginRegistry)
|
|
366
|
+
2. Custom tools (from CustomToolRegistry)
|
|
367
|
+
3. Built-in tools (from Tools::Registry)
|
|
368
|
+
|
|
369
|
+
**Name Inference:**
|
|
370
|
+
If no name is provided, the tool name is inferred from the class name by:
|
|
371
|
+
1. Taking the last component (e.g., `MyApp::Tools::WeatherTool` → `WeatherTool`)
|
|
372
|
+
2. Removing the `Tool` suffix if present (e.g., `WeatherTool` → `Weather`)
|
|
373
|
+
|
|
374
|
+
**Context Support:**
|
|
375
|
+
Tools can declare `creation_requirements` to receive agent context:
|
|
376
|
+
- `:agent_name` - Agent identifier (Symbol)
|
|
377
|
+
- `:directory` - Agent's working directory (String)
|
|
378
|
+
|
|
379
|
+
**Validation:**
|
|
380
|
+
- Tool class must inherit from `RubyLLM::Tool`
|
|
381
|
+
- Cannot override built-in tools (Read, Write, Edit, etc.)
|
|
382
|
+
- Cannot override plugin tools (MemoryWrite, etc.)
|
|
383
|
+
- Cannot register the same name twice
|
|
384
|
+
|
|
385
|
+
**When to Use:**
|
|
386
|
+
- Simple, stateless tools
|
|
387
|
+
- No persistent storage needed
|
|
388
|
+
- No lifecycle hooks required
|
|
389
|
+
- Quick setup without plugin infrastructure
|
|
390
|
+
|
|
391
|
+
**When to Use Plugins Instead:**
|
|
392
|
+
- Need per-agent storage
|
|
393
|
+
- Need lifecycle hooks (on_agent_initialized, on_user_message, etc.)
|
|
394
|
+
- Want to contribute to system prompts
|
|
395
|
+
- Building suite of related tools
|
|
396
|
+
|
|
397
|
+
**Example - Simple Tool:**
|
|
398
|
+
```ruby
|
|
399
|
+
class WeatherTool < RubyLLM::Tool
|
|
400
|
+
description "Get weather for a city"
|
|
401
|
+
param :city, type: "string", required: true
|
|
402
|
+
|
|
403
|
+
def execute(city:)
|
|
404
|
+
# Call weather API
|
|
405
|
+
"Weather in #{city}: Sunny, 72°F"
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Register with inferred name (:Weather)
|
|
410
|
+
SwarmSDK.register_tool(WeatherTool)
|
|
411
|
+
|
|
412
|
+
# Use in swarm
|
|
413
|
+
SwarmSDK.build do
|
|
414
|
+
agent :assistant do
|
|
415
|
+
model "claude-sonnet-4"
|
|
416
|
+
tools :Weather, :Read # Mix custom and built-in tools
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Example - Context-Aware Tool:**
|
|
422
|
+
```ruby
|
|
423
|
+
class AgentLogTool < RubyLLM::Tool
|
|
424
|
+
# Declare required context
|
|
425
|
+
def self.creation_requirements
|
|
426
|
+
[:agent_name, :directory]
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
description "Log a message for this agent"
|
|
430
|
+
param :message, type: "string", required: true
|
|
431
|
+
|
|
432
|
+
def initialize(agent_name:, directory:)
|
|
433
|
+
super()
|
|
434
|
+
@agent_name = agent_name
|
|
435
|
+
@log_file = File.join(directory, ".agent.log")
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def execute(message:)
|
|
439
|
+
File.append(@log_file, "[#{@agent_name}] #{message}\n")
|
|
440
|
+
"Logged message to #{@log_file}"
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
SwarmSDK.register_tool(:AgentLog, AgentLogTool)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Example - Explicit Name:**
|
|
448
|
+
```ruby
|
|
449
|
+
# Register with custom name
|
|
450
|
+
SwarmSDK.register_tool(:GetWeather, WeatherTool)
|
|
451
|
+
|
|
452
|
+
SwarmSDK.build do
|
|
453
|
+
agent :assistant do
|
|
454
|
+
tools :GetWeather # Use the custom name
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### SwarmSDK.custom_tool_registered?
|
|
460
|
+
|
|
461
|
+
Check if a custom tool is registered.
|
|
462
|
+
|
|
463
|
+
**Signature:**
|
|
464
|
+
```ruby
|
|
465
|
+
SwarmSDK.custom_tool_registered?(name) → Boolean
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Parameters:**
|
|
469
|
+
- `name` (Symbol | String): Tool name to check
|
|
470
|
+
|
|
471
|
+
**Returns:**
|
|
472
|
+
- `true` if tool is registered, `false` otherwise
|
|
473
|
+
|
|
474
|
+
**Example:**
|
|
475
|
+
```ruby
|
|
476
|
+
SwarmSDK.register_tool(WeatherTool)
|
|
477
|
+
SwarmSDK.custom_tool_registered?(:Weather) # => true
|
|
478
|
+
SwarmSDK.custom_tool_registered?("Weather") # => true
|
|
479
|
+
SwarmSDK.custom_tool_registered?(:Unknown) # => false
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
#### SwarmSDK.custom_tools
|
|
483
|
+
|
|
484
|
+
List all registered custom tool names.
|
|
485
|
+
|
|
486
|
+
**Signature:**
|
|
487
|
+
```ruby
|
|
488
|
+
SwarmSDK.custom_tools → Array<Symbol>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Returns:**
|
|
492
|
+
- Array of registered tool names (as symbols)
|
|
493
|
+
|
|
494
|
+
**Example:**
|
|
495
|
+
```ruby
|
|
496
|
+
SwarmSDK.register_tool(WeatherTool)
|
|
497
|
+
SwarmSDK.register_tool(:Stock, StockPriceTool)
|
|
498
|
+
|
|
499
|
+
SwarmSDK.custom_tools # => [:Weather, :Stock]
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
#### SwarmSDK.unregister_tool
|
|
503
|
+
|
|
504
|
+
Remove a registered custom tool.
|
|
505
|
+
|
|
506
|
+
**Signature:**
|
|
507
|
+
```ruby
|
|
508
|
+
SwarmSDK.unregister_tool(name) → Class | nil
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Parameters:**
|
|
512
|
+
- `name` (Symbol | String): Tool name to unregister
|
|
513
|
+
|
|
514
|
+
**Returns:**
|
|
515
|
+
- The unregistered tool class, or `nil` if not found
|
|
516
|
+
|
|
517
|
+
**Example:**
|
|
518
|
+
```ruby
|
|
519
|
+
removed = SwarmSDK.unregister_tool(:Weather)
|
|
520
|
+
removed # => WeatherTool
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
#### SwarmSDK.clear_custom_tools!
|
|
524
|
+
|
|
525
|
+
Clear all registered custom tools.
|
|
526
|
+
|
|
527
|
+
**Signature:**
|
|
528
|
+
```ruby
|
|
529
|
+
SwarmSDK.clear_custom_tools! → void
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Description:**
|
|
533
|
+
Removes all custom tool registrations. Primarily useful for testing to ensure clean state between test runs.
|
|
534
|
+
|
|
535
|
+
**Example:**
|
|
536
|
+
```ruby
|
|
537
|
+
SwarmSDK.register_tool(WeatherTool)
|
|
538
|
+
SwarmSDK.register_tool(StockPriceTool)
|
|
539
|
+
|
|
540
|
+
SwarmSDK.clear_custom_tools!
|
|
541
|
+
|
|
542
|
+
SwarmSDK.custom_tools # => []
|
|
208
543
|
```
|
|
209
544
|
|
|
210
545
|
---
|
|
@@ -980,7 +1315,7 @@ tools(*tool_names, include_default: true) → void
|
|
|
980
1315
|
- `ScratchpadWrite`, `ScratchpadRead`, `ScratchpadList`
|
|
981
1316
|
|
|
982
1317
|
**Memory tools** (added if agent has `memory` configured):
|
|
983
|
-
- `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `
|
|
1318
|
+
- `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
|
|
984
1319
|
|
|
985
1320
|
**Additional tools:**
|
|
986
1321
|
- `Write`, `Edit`, `MultiEdit`, `Bash`
|
|
@@ -1129,7 +1464,7 @@ memory(&block) → void
|
|
|
1129
1464
|
- `directory(string)` - Directory where memory.json will be stored (required)
|
|
1130
1465
|
|
|
1131
1466
|
**Description:**
|
|
1132
|
-
Enables persistent memory for the agent. When configured, the agent automatically gets all
|
|
1467
|
+
Enables persistent memory for the agent. When configured, the agent automatically gets all 6 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended to help the agent use memory effectively.
|
|
1133
1468
|
|
|
1134
1469
|
Memory is per-agent (isolated) and persistent (survives across sessions).
|
|
1135
1470
|
|
|
@@ -2231,6 +2566,124 @@ result.to_json → String
|
|
|
2231
2566
|
```
|
|
2232
2567
|
Convert to JSON string.
|
|
2233
2568
|
|
|
2569
|
+
**per_agent_usage**
|
|
2570
|
+
```ruby
|
|
2571
|
+
result.per_agent_usage → Hash<Symbol, Hash>
|
|
2572
|
+
```
|
|
2573
|
+
Returns per-agent usage breakdown extracted from execution logs.
|
|
2574
|
+
|
|
2575
|
+
Each agent entry contains:
|
|
2576
|
+
- `input_tokens` - Input tokens for this agent
|
|
2577
|
+
- `output_tokens` - Output tokens for this agent
|
|
2578
|
+
- `total_tokens` - Total tokens (input + output)
|
|
2579
|
+
- `cached_tokens` - Cached tokens used
|
|
2580
|
+
- `context_limit` - Context window limit
|
|
2581
|
+
- `usage_percentage` - Percentage of context window used
|
|
2582
|
+
- `tokens_remaining` - Tokens remaining in context window
|
|
2583
|
+
- `input_cost` - Input cost in dollars
|
|
2584
|
+
- `output_cost` - Output cost in dollars
|
|
2585
|
+
- `total_cost` - Total cost in dollars
|
|
2586
|
+
|
|
2587
|
+
**Example:**
|
|
2588
|
+
```ruby
|
|
2589
|
+
result = swarm.execute("Build authentication")
|
|
2590
|
+
usage = result.per_agent_usage
|
|
2591
|
+
|
|
2592
|
+
usage[:backend]
|
|
2593
|
+
# => {
|
|
2594
|
+
# input_tokens: 15000,
|
|
2595
|
+
# output_tokens: 5000,
|
|
2596
|
+
# total_tokens: 20000,
|
|
2597
|
+
# cached_tokens: 2000,
|
|
2598
|
+
# context_limit: 200000,
|
|
2599
|
+
# usage_percentage: 10.0,
|
|
2600
|
+
# tokens_remaining: 180000,
|
|
2601
|
+
# input_cost: 0.045,
|
|
2602
|
+
# output_cost: 0.075,
|
|
2603
|
+
# total_cost: 0.12
|
|
2604
|
+
# }
|
|
2605
|
+
```
|
|
2606
|
+
|
|
2607
|
+
---
|
|
2608
|
+
|
|
2609
|
+
## Swarm Methods
|
|
2610
|
+
|
|
2611
|
+
### context_breakdown
|
|
2612
|
+
|
|
2613
|
+
Get real-time context usage breakdown for all agents.
|
|
2614
|
+
|
|
2615
|
+
**Signature:**
|
|
2616
|
+
```ruby
|
|
2617
|
+
swarm.context_breakdown → Hash<Symbol, Hash>
|
|
2618
|
+
```
|
|
2619
|
+
|
|
2620
|
+
**Returns:**
|
|
2621
|
+
Hash with agent names as keys and context usage info as values.
|
|
2622
|
+
|
|
2623
|
+
**Description:**
|
|
2624
|
+
Returns live context metrics for all agents in the swarm, including both primary agents and delegation instances. Useful for monitoring token consumption and costs during execution.
|
|
2625
|
+
|
|
2626
|
+
Each agent entry contains:
|
|
2627
|
+
- `input_tokens` - Cumulative input tokens
|
|
2628
|
+
- `output_tokens` - Cumulative output tokens
|
|
2629
|
+
- `total_tokens` - Total tokens (input + output)
|
|
2630
|
+
- `cached_tokens` - Cached tokens used
|
|
2631
|
+
- `cache_creation_tokens` - Tokens written to cache
|
|
2632
|
+
- `effective_input_tokens` - Actual input tokens charged (input - cached)
|
|
2633
|
+
- `context_limit` - Context window limit for the model
|
|
2634
|
+
- `usage_percentage` - Percentage of context window used
|
|
2635
|
+
- `tokens_remaining` - Tokens remaining in context window
|
|
2636
|
+
- `input_cost` - Cumulative input cost in dollars
|
|
2637
|
+
- `output_cost` - Cumulative output cost in dollars
|
|
2638
|
+
- `total_cost` - Cumulative total cost in dollars
|
|
2639
|
+
|
|
2640
|
+
**Example:**
|
|
2641
|
+
```ruby
|
|
2642
|
+
swarm = SwarmSDK.build do
|
|
2643
|
+
name "Dev Team"
|
|
2644
|
+
lead :backend
|
|
2645
|
+
|
|
2646
|
+
agent :backend do
|
|
2647
|
+
model "claude-sonnet-4"
|
|
2648
|
+
delegates_to :tester
|
|
2649
|
+
end
|
|
2650
|
+
|
|
2651
|
+
agent :tester do
|
|
2652
|
+
model "gpt-4o-mini"
|
|
2653
|
+
end
|
|
2654
|
+
end
|
|
2655
|
+
|
|
2656
|
+
# During or after execution
|
|
2657
|
+
breakdown = swarm.context_breakdown
|
|
2658
|
+
|
|
2659
|
+
# Primary agent
|
|
2660
|
+
breakdown[:backend]
|
|
2661
|
+
# => {
|
|
2662
|
+
# input_tokens: 15000,
|
|
2663
|
+
# output_tokens: 5000,
|
|
2664
|
+
# total_tokens: 20000,
|
|
2665
|
+
# cached_tokens: 2000,
|
|
2666
|
+
# cache_creation_tokens: 1000,
|
|
2667
|
+
# effective_input_tokens: 13000,
|
|
2668
|
+
# context_limit: 200000,
|
|
2669
|
+
# usage_percentage: 10.0,
|
|
2670
|
+
# tokens_remaining: 180000,
|
|
2671
|
+
# input_cost: 0.045,
|
|
2672
|
+
# output_cost: 0.075,
|
|
2673
|
+
# total_cost: 0.12
|
|
2674
|
+
# }
|
|
2675
|
+
|
|
2676
|
+
# Delegation instance
|
|
2677
|
+
breakdown[:"tester@backend"]
|
|
2678
|
+
# => { ... same structure ... }
|
|
2679
|
+
```
|
|
2680
|
+
|
|
2681
|
+
**Use Cases:**
|
|
2682
|
+
- Monitor token consumption during long-running tasks
|
|
2683
|
+
- Track costs per agent in real-time
|
|
2684
|
+
- Identify context-heavy agents approaching limits
|
|
2685
|
+
- Debug context window issues
|
|
2686
|
+
|
|
2234
2687
|
---
|
|
2235
2688
|
|
|
2236
2689
|
## Context Management
|
|
@@ -855,28 +855,7 @@ end
|
|
|
855
855
|
|
|
856
856
|
---
|
|
857
857
|
|
|
858
|
-
### 4.
|
|
859
|
-
|
|
860
|
-
**File:** `lib/swarm_memory/tools/memory_multi_edit.rb`
|
|
861
|
-
|
|
862
|
-
Apply multiple edits sequentially to one entry.
|
|
863
|
-
|
|
864
|
-
**Input (JSON array):**
|
|
865
|
-
```json
|
|
866
|
-
[
|
|
867
|
-
{"old_string": "status: pending", "new_string": "status: resolved"},
|
|
868
|
-
{"old_string": "confidence: medium", "new_string": "confidence: high"}
|
|
869
|
-
]
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
**Behavior:**
|
|
873
|
-
- Edits applied **sequentially** (each sees previous results)
|
|
874
|
-
- **All-or-nothing** - if any edit fails, NO changes saved
|
|
875
|
-
- Shows which edits succeeded before failure
|
|
876
|
-
|
|
877
|
-
---
|
|
878
|
-
|
|
879
|
-
### 5. MemoryGlob
|
|
858
|
+
### 4. MemoryGlob
|
|
880
859
|
|
|
881
860
|
**File:** `lib/swarm_memory/tools/memory_glob.rb`
|
|
882
861
|
|
|
@@ -915,7 +894,7 @@ MAX_RESULTS = 500 # Truncates with system reminder if exceeded
|
|
|
915
894
|
|
|
916
895
|
---
|
|
917
896
|
|
|
918
|
-
###
|
|
897
|
+
### 5. MemoryGrep
|
|
919
898
|
|
|
920
899
|
**File:** `lib/swarm_memory/tools/memory_grep.rb`
|
|
921
900
|
|
|
@@ -949,7 +928,7 @@ path: "skill/debug.md" # Just that file
|
|
|
949
928
|
|
|
950
929
|
---
|
|
951
930
|
|
|
952
|
-
###
|
|
931
|
+
### 6. MemoryDelete
|
|
953
932
|
|
|
954
933
|
**File:** `lib/swarm_memory/tools/memory_delete.rb`
|
|
955
934
|
|
|
@@ -964,7 +943,7 @@ MemoryDelete(file_path: "experience/temp-experiment.md")
|
|
|
964
943
|
|
|
965
944
|
---
|
|
966
945
|
|
|
967
|
-
###
|
|
946
|
+
### 7. MemoryDefrag
|
|
968
947
|
|
|
969
948
|
**File:** `lib/swarm_memory/tools/memory_defrag.rb`
|
|
970
949
|
|
|
@@ -1035,7 +1014,7 @@ MemoryDefrag(action: "full", dry_run: true) # Preview all
|
|
|
1035
1014
|
|
|
1036
1015
|
---
|
|
1037
1016
|
|
|
1038
|
-
###
|
|
1017
|
+
### 8. LoadSkill
|
|
1039
1018
|
|
|
1040
1019
|
**File:** `lib/swarm_memory/tools/load_skill.rb`
|
|
1041
1020
|
|
|
@@ -1079,7 +1058,7 @@ sequenceDiagram
|
|
|
1079
1058
|
**Immutable Tools:**
|
|
1080
1059
|
```ruby
|
|
1081
1060
|
# These tools NEVER removed during skill loading:
|
|
1082
|
-
- MemoryWrite, MemoryRead, MemoryEdit
|
|
1061
|
+
- MemoryWrite, MemoryRead, MemoryEdit
|
|
1083
1062
|
- MemoryDelete, MemoryGlob, MemoryGrep, MemoryDefrag
|
|
1084
1063
|
- LoadSkill (self)
|
|
1085
1064
|
```
|
|
@@ -1243,7 +1222,7 @@ class SDKPlugin < SwarmSDK::Plugin
|
|
|
1243
1222
|
# Return memory prompt based on mode
|
|
1244
1223
|
end
|
|
1245
1224
|
|
|
1246
|
-
def
|
|
1225
|
+
def memory_configured?(agent_definition)
|
|
1247
1226
|
agent_definition.memory_enabled?
|
|
1248
1227
|
end
|
|
1249
1228
|
end
|
|
@@ -2025,7 +2004,6 @@ lib/swarm_memory/
|
|
|
2025
2004
|
│ ├── memory_write.rb
|
|
2026
2005
|
│ ├── memory_read.rb
|
|
2027
2006
|
│ ├── memory_edit.rb
|
|
2028
|
-
│ ├── memory_multi_edit.rb
|
|
2029
2007
|
│ ├── memory_delete.rb
|
|
2030
2008
|
│ ├── memory_glob.rb
|
|
2031
2009
|
│ ├── memory_grep.rb
|
data/docs/v2/reference/yaml.md
CHANGED
|
@@ -649,7 +649,7 @@ agents:
|
|
|
649
649
|
- `ScratchpadWrite`, `ScratchpadRead`, `ScratchpadList`
|
|
650
650
|
|
|
651
651
|
**Memory tools** (added if agent has `memory` configured):
|
|
652
|
-
- `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `
|
|
652
|
+
- `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
|
|
653
653
|
|
|
654
654
|
**Additional tools:**
|
|
655
655
|
- `Write`, `Edit`, `MultiEdit`, `Bash`
|
|
@@ -761,7 +761,7 @@ Plugin storage (like SwarmMemory) is always shared by base agent name:
|
|
|
761
761
|
**Default:** `null` (memory disabled)
|
|
762
762
|
**Description:** Configure persistent memory storage for this agent.
|
|
763
763
|
|
|
764
|
-
When configured, the agent automatically gets all
|
|
764
|
+
When configured, the agent automatically gets all 6 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended.
|
|
765
765
|
|
|
766
766
|
Memory is per-agent (isolated) and persistent (survives across sessions).
|
|
767
767
|
|
|
@@ -123,7 +123,7 @@ module ClaudeSwarm
|
|
|
123
123
|
# Add optional arguments
|
|
124
124
|
# Handle prompt_file by reading the file contents
|
|
125
125
|
if instance[:prompt_file]
|
|
126
|
-
prompt_file_path = File.join(@config.
|
|
126
|
+
prompt_file_path = File.join(@config.base_dir, instance[:prompt_file])
|
|
127
127
|
if File.exist?(prompt_file_path)
|
|
128
128
|
prompt_content = File.read(prompt_file_path)
|
|
129
129
|
args.push("--prompt", prompt_content)
|
|
@@ -568,7 +568,14 @@ module ClaudeSwarm
|
|
|
568
568
|
end
|
|
569
569
|
|
|
570
570
|
# Always add instance prompt if it exists
|
|
571
|
-
if instance[:
|
|
571
|
+
if instance[:prompt_file]
|
|
572
|
+
prompt_file_path = File.join(@config.base_dir, instance[:prompt_file])
|
|
573
|
+
if File.exist?(prompt_file_path)
|
|
574
|
+
prompt_content = File.read(prompt_file_path)
|
|
575
|
+
parts << "--append-system-prompt"
|
|
576
|
+
parts << prompt_content
|
|
577
|
+
end
|
|
578
|
+
elsif instance[:prompt]
|
|
572
579
|
parts << "--append-system-prompt"
|
|
573
580
|
parts << instance[:prompt]
|
|
574
581
|
end
|
data/lib/claude_swarm/version.rb
CHANGED
data/lib/swarm_cli/version.rb
CHANGED
|
@@ -181,6 +181,9 @@ module SwarmMemory
|
|
|
181
181
|
|
|
182
182
|
# Calculate hybrid scores combining semantic similarity and keyword matching
|
|
183
183
|
#
|
|
184
|
+
# When keyword_score is 0 (no tag matches), falls back to pure semantic scoring
|
|
185
|
+
# to avoid penalizing results that have excellent semantic matches but no tag overlap.
|
|
186
|
+
#
|
|
184
187
|
# @param results [Array<Hash>] Results with semantic :similarity scores
|
|
185
188
|
# @param query_keywords [Array<String>] Keywords from query
|
|
186
189
|
# @return [Array<Hash>] Results with updated :similarity (hybrid score) and debug info
|
|
@@ -189,8 +192,13 @@ module SwarmMemory
|
|
|
189
192
|
semantic_score = result[:similarity]
|
|
190
193
|
keyword_score = calculate_keyword_score(result, query_keywords)
|
|
191
194
|
|
|
192
|
-
#
|
|
193
|
-
|
|
195
|
+
# Fallback to pure semantic when no keyword matches
|
|
196
|
+
# This prevents penalizing results with excellent semantic matches but no tag overlap
|
|
197
|
+
hybrid_score = if keyword_score.zero?
|
|
198
|
+
semantic_score
|
|
199
|
+
else
|
|
200
|
+
(@semantic_weight * semantic_score) + (@keyword_weight * keyword_score)
|
|
201
|
+
end
|
|
194
202
|
|
|
195
203
|
# Update result with hybrid score and debug info
|
|
196
204
|
result.merge(
|
|
@@ -18,7 +18,9 @@ module SwarmMemory
|
|
|
18
18
|
#
|
|
19
19
|
# @param adapter [Adapters::Base] Storage adapter
|
|
20
20
|
# @param embedder [Embeddings::Embedder, nil] Optional embedder for semantic search
|
|
21
|
-
|
|
21
|
+
# @param semantic_weight [Float, nil] Weight for semantic similarity in hybrid search (0.0-1.0)
|
|
22
|
+
# @param keyword_weight [Float, nil] Weight for keyword matching in hybrid search (0.0-1.0)
|
|
23
|
+
def initialize(adapter:, embedder: nil, semantic_weight: nil, keyword_weight: nil)
|
|
22
24
|
raise ArgumentError, "adapter is required" unless adapter.is_a?(Adapters::Base)
|
|
23
25
|
|
|
24
26
|
@adapter = adapter
|
|
@@ -26,7 +28,10 @@ module SwarmMemory
|
|
|
26
28
|
|
|
27
29
|
# Create semantic index if embedder is provided
|
|
28
30
|
@semantic_index = if embedder
|
|
29
|
-
|
|
31
|
+
index_options = { adapter: adapter, embedder: embedder }
|
|
32
|
+
index_options[:semantic_weight] = semantic_weight if semantic_weight
|
|
33
|
+
index_options[:keyword_weight] = keyword_weight if keyword_weight
|
|
34
|
+
SemanticIndex.new(**index_options)
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
37
|
|