claude_swarm 1.0.9 → 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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +10 -0
  3. data/CLAUDE.md +346 -191
  4. data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
  5. data/docs/v2/CHANGELOG.swarm_cli.md +20 -0
  6. data/docs/v2/CHANGELOG.swarm_memory.md +146 -1
  7. data/docs/v2/CHANGELOG.swarm_sdk.md +433 -10
  8. data/docs/v2/README.md +20 -5
  9. data/docs/v2/guides/complete-tutorial.md +95 -9
  10. data/docs/v2/guides/getting-started.md +10 -8
  11. data/docs/v2/guides/memory-adapters.md +41 -0
  12. data/docs/v2/guides/migrating-to-2.x.md +746 -0
  13. data/docs/v2/guides/plugins.md +52 -5
  14. data/docs/v2/guides/rails-integration.md +6 -0
  15. data/docs/v2/guides/snapshots.md +14 -14
  16. data/docs/v2/guides/swarm-memory.md +2 -13
  17. data/docs/v2/reference/architecture-flow.md +3 -3
  18. data/docs/v2/reference/cli.md +0 -1
  19. data/docs/v2/reference/configuration_reference.md +300 -0
  20. data/docs/v2/reference/event_payload_structures.md +27 -5
  21. data/docs/v2/reference/ruby-dsl.md +614 -18
  22. data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
  23. data/docs/v2/reference/yaml.md +172 -54
  24. data/examples/snapshot_demo.rb +2 -2
  25. data/lib/claude_swarm/mcp_generator.rb +8 -21
  26. data/lib/claude_swarm/orchestrator.rb +8 -1
  27. data/lib/claude_swarm/version.rb +1 -1
  28. data/lib/swarm_cli/commands/run.rb +2 -2
  29. data/lib/swarm_cli/config_loader.rb +11 -11
  30. data/lib/swarm_cli/formatters/human_formatter.rb +0 -33
  31. data/lib/swarm_cli/interactive_repl.rb +2 -2
  32. data/lib/swarm_cli/ui/icons.rb +0 -23
  33. data/lib/swarm_cli/version.rb +1 -1
  34. data/lib/swarm_memory/adapters/filesystem_adapter.rb +11 -34
  35. data/lib/swarm_memory/core/semantic_index.rb +10 -2
  36. data/lib/swarm_memory/core/storage.rb +7 -2
  37. data/lib/swarm_memory/dsl/memory_config.rb +37 -0
  38. data/lib/swarm_memory/integration/sdk_plugin.rb +201 -28
  39. data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
  40. data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
  41. data/lib/swarm_memory/tools/load_skill.rb +0 -1
  42. data/lib/swarm_memory/tools/memory_edit.rb +2 -1
  43. data/lib/swarm_memory/tools/memory_read.rb +1 -1
  44. data/lib/swarm_memory/version.rb +1 -1
  45. data/lib/swarm_memory.rb +8 -6
  46. data/lib/swarm_sdk/agent/builder.rb +58 -0
  47. data/lib/swarm_sdk/agent/chat.rb +527 -1061
  48. data/lib/swarm_sdk/agent/{chat → chat_helpers}/context_tracker.rb +13 -88
  49. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +204 -0
  50. data/lib/swarm_sdk/agent/{chat → chat_helpers}/hook_integration.rb +108 -46
  51. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +78 -0
  52. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +267 -0
  53. data/lib/swarm_sdk/agent/{chat → chat_helpers}/logging_helpers.rb +3 -3
  54. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +83 -0
  55. data/lib/swarm_sdk/agent/{chat → chat_helpers}/system_reminder_injector.rb +11 -13
  56. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +79 -0
  57. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +146 -0
  58. data/lib/swarm_sdk/agent/context.rb +1 -2
  59. data/lib/swarm_sdk/agent/definition.rb +66 -154
  60. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +4 -2
  61. data/lib/swarm_sdk/agent/system_prompt_builder.rb +161 -0
  62. data/lib/swarm_sdk/agent_registry.rb +146 -0
  63. data/lib/swarm_sdk/builders/base_builder.rb +488 -0
  64. data/lib/swarm_sdk/concerns/cleanupable.rb +39 -0
  65. data/lib/swarm_sdk/concerns/snapshotable.rb +67 -0
  66. data/lib/swarm_sdk/concerns/validatable.rb +55 -0
  67. data/lib/swarm_sdk/config.rb +302 -0
  68. data/lib/swarm_sdk/configuration/parser.rb +373 -0
  69. data/lib/swarm_sdk/configuration/translator.rb +255 -0
  70. data/lib/swarm_sdk/configuration.rb +77 -546
  71. data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
  72. data/lib/swarm_sdk/context_compactor.rb +6 -11
  73. data/lib/swarm_sdk/context_management/builder.rb +128 -0
  74. data/lib/swarm_sdk/context_management/context.rb +328 -0
  75. data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
  76. data/lib/swarm_sdk/defaults.rb +196 -0
  77. data/lib/swarm_sdk/events_to_messages.rb +18 -0
  78. data/lib/swarm_sdk/hooks/adapter.rb +3 -3
  79. data/lib/swarm_sdk/hooks/shell_executor.rb +4 -2
  80. data/lib/swarm_sdk/log_collector.rb +179 -29
  81. data/lib/swarm_sdk/log_stream.rb +29 -0
  82. data/lib/swarm_sdk/models.json +4333 -1
  83. data/lib/swarm_sdk/models.rb +43 -2
  84. data/lib/swarm_sdk/node_context.rb +1 -1
  85. data/lib/swarm_sdk/observer/builder.rb +81 -0
  86. data/lib/swarm_sdk/observer/config.rb +45 -0
  87. data/lib/swarm_sdk/observer/manager.rb +236 -0
  88. data/lib/swarm_sdk/patterns/agent_observer.rb +160 -0
  89. data/lib/swarm_sdk/plugin.rb +95 -5
  90. data/lib/swarm_sdk/result.rb +52 -0
  91. data/lib/swarm_sdk/snapshot.rb +6 -6
  92. data/lib/swarm_sdk/snapshot_from_events.rb +13 -2
  93. data/lib/swarm_sdk/state_restorer.rb +136 -151
  94. data/lib/swarm_sdk/state_snapshot.rb +65 -100
  95. data/lib/swarm_sdk/swarm/agent_initializer.rb +181 -137
  96. data/lib/swarm_sdk/swarm/builder.rb +44 -578
  97. data/lib/swarm_sdk/swarm/executor.rb +213 -0
  98. data/lib/swarm_sdk/swarm/hook_triggers.rb +151 -0
  99. data/lib/swarm_sdk/swarm/logging_callbacks.rb +341 -0
  100. data/lib/swarm_sdk/swarm/mcp_configurator.rb +7 -4
  101. data/lib/swarm_sdk/swarm/tool_configurator.rb +58 -140
  102. data/lib/swarm_sdk/swarm.rb +203 -683
  103. data/lib/swarm_sdk/tools/bash.rb +14 -8
  104. data/lib/swarm_sdk/tools/delegate.rb +61 -43
  105. data/lib/swarm_sdk/tools/edit.rb +8 -13
  106. data/lib/swarm_sdk/tools/glob.rb +12 -4
  107. data/lib/swarm_sdk/tools/grep.rb +7 -0
  108. data/lib/swarm_sdk/tools/multi_edit.rb +15 -11
  109. data/lib/swarm_sdk/tools/path_resolver.rb +51 -2
  110. data/lib/swarm_sdk/tools/read.rb +16 -18
  111. data/lib/swarm_sdk/tools/registry.rb +122 -10
  112. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +9 -5
  113. data/lib/swarm_sdk/tools/stores/storage.rb +0 -6
  114. data/lib/swarm_sdk/tools/todo_write.rb +7 -0
  115. data/lib/swarm_sdk/tools/web_fetch.rb +20 -17
  116. data/lib/swarm_sdk/tools/write.rb +8 -13
  117. data/lib/swarm_sdk/version.rb +1 -1
  118. data/lib/swarm_sdk/{node → workflow}/agent_config.rb +1 -1
  119. data/lib/swarm_sdk/workflow/builder.rb +192 -0
  120. data/lib/swarm_sdk/workflow/executor.rb +497 -0
  121. data/lib/swarm_sdk/{node/builder.rb → workflow/node_builder.rb} +7 -5
  122. data/lib/swarm_sdk/{node → workflow}/transformer_executor.rb +5 -3
  123. data/lib/swarm_sdk/{node_orchestrator.rb → workflow.rb} +152 -456
  124. data/lib/swarm_sdk.rb +294 -108
  125. data/rubocop/cop/security/no_reflection_methods.rb +1 -1
  126. data/swarm_cli.gemspec +1 -1
  127. data/swarm_memory.gemspec +8 -3
  128. data/swarm_sdk.gemspec +6 -4
  129. data/team_full.yml +124 -320
  130. metadata +42 -14
  131. data/lib/swarm_memory/chat_extension.rb +0 -34
  132. data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
  133. data/lib/swarm_sdk/providers/openai_with_responses.rb +0 -589
  134. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -10,7 +10,7 @@ module SwarmSDK
10
10
  # ## Adding Custom Attributes to Agents
11
11
  #
12
12
  # Plugins can add custom attributes to Agent::Definition that are preserved
13
- # when agents are cloned (e.g., in NodeOrchestrator). To do this:
13
+ # when agents are cloned (e.g., in Workflow). To do this:
14
14
  #
15
15
  # 1. Add attr_reader to Agent::Definition for your attribute
16
16
  # 2. Parse the attribute in Agent::Definition#initialize
@@ -62,7 +62,7 @@ module SwarmSDK
62
62
  # my_custom_config { option: "value" }
63
63
  # end
64
64
  #
65
- # And it will be preserved when NodeOrchestrator clones the agent!
65
+ # And it will be preserved when Workflow clones the agent!
66
66
  #
67
67
  # @example Real-world: SwarmMemory plugin
68
68
  # # SwarmMemory adds 'memory' attribute to agents
@@ -139,11 +139,11 @@ module SwarmSDK
139
139
  []
140
140
  end
141
141
 
142
- # Agent storage enabled for this agent? (optional)
142
+ # Check if memory is configured for this agent (optional)
143
143
  #
144
144
  # @param agent_definition [Agent::Definition] Agent definition
145
145
  # @return [Boolean] True if storage should be created
146
- def storage_enabled?(agent_definition)
146
+ def memory_configured?(agent_definition)
147
147
  false
148
148
  end
149
149
 
@@ -197,7 +197,7 @@ module SwarmSDK
197
197
  # Contribute to agent serialization (optional)
198
198
  #
199
199
  # Called when Agent::Definition.to_h is invoked (e.g., for cloning agents
200
- # in NodeOrchestrator). Plugins can return config keys that should be
200
+ # in Workflow). Plugins can return config keys that should be
201
201
  # included in the serialized hash to preserve their state.
202
202
  #
203
203
  # This allows plugins to maintain their configuration when agents are
@@ -215,5 +215,95 @@ module SwarmSDK
215
215
  def serialize_config(agent_definition:)
216
216
  {}
217
217
  end
218
+
219
+ # Snapshot plugin-specific state for an agent
220
+ #
221
+ # Called during state snapshot creation (e.g., session persistence).
222
+ # Return any state your plugin needs to persist for this agent.
223
+ # The returned hash will be JSON serialized.
224
+ #
225
+ # @param agent_name [Symbol] Agent identifier
226
+ # @return [Hash] Plugin-specific state (empty hash if nothing to snapshot)
227
+ #
228
+ # @example Memory read tracking
229
+ # def snapshot_agent_state(agent_name)
230
+ # entries = StorageReadTracker.get_read_entries(agent_name)
231
+ # return {} if entries.empty?
232
+ #
233
+ # { read_entries: entries }
234
+ # end
235
+ def snapshot_agent_state(agent_name)
236
+ {}
237
+ end
238
+
239
+ # Restore plugin-specific state for an agent
240
+ #
241
+ # Called during state restoration. Restore any persisted state.
242
+ # This method is idempotent - calling it multiple times with
243
+ # the same state should produce the same result.
244
+ #
245
+ # @param agent_name [Symbol] Agent identifier
246
+ # @param state [Hash] Previously snapshotted state (with symbol keys)
247
+ # @return [void]
248
+ #
249
+ # @example Memory read tracking
250
+ # def restore_agent_state(agent_name, state)
251
+ # entries = state[:read_entries] || state["read_entries"]
252
+ # return unless entries
253
+ #
254
+ # StorageReadTracker.restore_read_entries(agent_name, entries)
255
+ # end
256
+ def restore_agent_state(agent_name, state)
257
+ # Override if needed
258
+ end
259
+
260
+ # Get digest for a tool result (e.g., file hash, memory entry hash)
261
+ #
262
+ # Called during tool result metadata collection. Returns a digest
263
+ # that can be used to detect if the resource has changed since
264
+ # it was last read. This enables change detection hooks.
265
+ #
266
+ # @param agent_name [Symbol] Agent identifier
267
+ # @param tool_name [String] Name of the tool (e.g., "MemoryRead")
268
+ # @param path [String] Path or identifier of the resource
269
+ # @return [String, nil] Digest string or nil if not tracked by this plugin
270
+ #
271
+ # @example Memory read tracking
272
+ # def get_tool_result_digest(agent_name:, tool_name:, path:)
273
+ # return unless tool_name == "MemoryRead"
274
+ #
275
+ # StorageReadTracker.get_read_entries(agent_name)[path]
276
+ # end
277
+ def get_tool_result_digest(agent_name:, tool_name:, path:)
278
+ nil
279
+ end
280
+
281
+ # Translate YAML configuration into DSL calls
282
+ #
283
+ # Called during YAML-to-DSL translation. Plugins can translate their
284
+ # specific YAML configuration keys into DSL method calls on the builder.
285
+ # This allows SDK to remain plugin-agnostic while plugins can add
286
+ # YAML configuration support.
287
+ #
288
+ # @param builder [Agent::Builder] Builder instance (self in DSL context)
289
+ # @param agent_config [Hash] Full agent config from YAML
290
+ # @return [void]
291
+ #
292
+ # @example Memory plugin YAML translation
293
+ # def translate_yaml_config(builder, agent_config)
294
+ # memory_config = agent_config[:memory]
295
+ # return unless memory_config
296
+ #
297
+ # builder.instance_eval do
298
+ # memory do
299
+ # directory(memory_config[:directory])
300
+ # adapter(memory_config[:adapter]) if memory_config[:adapter]
301
+ # mode(memory_config[:mode]) if memory_config[:mode]
302
+ # end
303
+ # end
304
+ # end
305
+ def translate_yaml_config(builder, agent_config)
306
+ # Override if plugin needs YAML configuration support
307
+ end
218
308
  end
219
309
  end
@@ -109,6 +109,58 @@ module SwarmSDK
109
109
  @logs.map { |entry| entry[:agent] }.compact.uniq.map(&:to_sym)
110
110
  end
111
111
 
112
+ # Get per-agent usage breakdown from logs
113
+ #
114
+ # Aggregates context usage, tokens, and cost for each agent from their
115
+ # final agent_stop or agent_step events. Each agent's entry includes:
116
+ # - input_tokens, output_tokens, total_tokens
117
+ # - context_limit, usage_percentage, tokens_remaining
118
+ # - input_cost, output_cost, total_cost
119
+ #
120
+ # @return [Hash{Symbol => Hash}] Per-agent usage breakdown
121
+ #
122
+ # @example
123
+ # result.per_agent_usage[:backend]
124
+ # # => {
125
+ # # input_tokens: 15000,
126
+ # # output_tokens: 5000,
127
+ # # total_tokens: 20000,
128
+ # # context_limit: 200000,
129
+ # # usage_percentage: "10.0%",
130
+ # # tokens_remaining: 180000,
131
+ # # input_cost: 0.045,
132
+ # # output_cost: 0.075,
133
+ # # total_cost: 0.12
134
+ # # }
135
+ def per_agent_usage
136
+ # Find the last usage entry for each agent
137
+ agent_entries = {}
138
+
139
+ @logs.each do |entry|
140
+ next unless entry[:usage] && entry[:agent]
141
+ next unless entry[:type] == "agent_step" || entry[:type] == "agent_stop"
142
+
143
+ agent_name = entry[:agent].to_sym
144
+ agent_entries[agent_name] = entry[:usage]
145
+ end
146
+
147
+ # Build breakdown from final usage entries
148
+ agent_entries.transform_values do |usage|
149
+ {
150
+ input_tokens: usage[:cumulative_input_tokens] || 0,
151
+ output_tokens: usage[:cumulative_output_tokens] || 0,
152
+ total_tokens: usage[:cumulative_total_tokens] || 0,
153
+ cached_tokens: usage[:cumulative_cached_tokens] || 0,
154
+ context_limit: usage[:context_limit],
155
+ usage_percentage: usage[:tokens_used_percentage],
156
+ tokens_remaining: usage[:tokens_remaining],
157
+ input_cost: usage[:input_cost] || 0.0,
158
+ output_cost: usage[:output_cost] || 0.0,
159
+ total_cost: usage[:total_cost] || 0.0,
160
+ }
161
+ end
162
+ end
163
+
112
164
  # Count total LLM requests made
113
165
  # Each LLM API call produces either agent_step (tool calls) or agent_stop (final answer)
114
166
  def llm_requests
@@ -102,7 +102,7 @@ module SwarmSDK
102
102
  @data[:version] || @data["version"]
103
103
  end
104
104
 
105
- # Get snapshot type (swarm or node_orchestrator)
105
+ # Get snapshot type (swarm or workflow)
106
106
  #
107
107
  # @return [String] Snapshot type
108
108
  def type
@@ -139,18 +139,18 @@ module SwarmSDK
139
139
  delegations ? delegations.keys.map(&:to_s) : []
140
140
  end
141
141
 
142
- # Check if snapshot is for a swarm (vs node_orchestrator)
142
+ # Check if snapshot is for a swarm (vs workflow)
143
143
  #
144
144
  # @return [Boolean] true if swarm snapshot
145
145
  def swarm?
146
146
  type == "swarm"
147
147
  end
148
148
 
149
- # Check if snapshot is for a node orchestrator
149
+ # Check if snapshot is for a workflow
150
150
  #
151
- # @return [Boolean] true if node orchestrator snapshot
152
- def node_orchestrator?
153
- type == "node_orchestrator"
151
+ # @return [Boolean] true if workflow snapshot
152
+ def workflow?
153
+ type == "workflow"
154
154
  end
155
155
  end
156
156
  end
@@ -69,16 +69,17 @@ module SwarmSDK
69
69
  # @return [Hash] StateSnapshot hash
70
70
  def reconstruct
71
71
  {
72
- version: "1.0.0",
72
+ version: "2.1.0",
73
73
  type: "swarm",
74
74
  snapshot_at: @events.last&.fetch(:timestamp, Time.now.utc.iso8601),
75
75
  swarm_sdk_version: SwarmSDK::VERSION,
76
- swarm: reconstruct_swarm_metadata,
76
+ metadata: reconstruct_swarm_metadata,
77
77
  agents: reconstruct_all_agents,
78
78
  delegation_instances: reconstruct_all_delegations,
79
79
  scratchpad: reconstruct_scratchpad,
80
80
  read_tracking: reconstruct_read_tracking,
81
81
  memory_read_tracking: reconstruct_memory_read_tracking,
82
+ plugin_states: reconstruct_plugin_states,
82
83
  }
83
84
  end
84
85
 
@@ -363,6 +364,16 @@ module SwarmSDK
363
364
  tracking
364
365
  end
365
366
 
367
+ # Reconstruct plugin states
368
+ #
369
+ # Plugin states cannot be fully reconstructed from events alone as they
370
+ # contain internal plugin data. Returns empty hash for compatibility.
371
+ #
372
+ # @return [Hash] Empty plugin states hash
373
+ def reconstruct_plugin_states
374
+ {}
375
+ end
376
+
366
377
  # Parse timestamp string to Time object
367
378
  #
368
379
  # @param timestamp [String, nil] ISO 8601 timestamp