swarm_memory 2.1.3 → 2.1.4

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/claude_swarm/claude_mcp_server.rb +1 -0
  3. data/lib/claude_swarm/cli.rb +5 -18
  4. data/lib/claude_swarm/configuration.rb +2 -15
  5. data/lib/claude_swarm/mcp_generator.rb +1 -0
  6. data/lib/claude_swarm/openai/chat_completion.rb +4 -12
  7. data/lib/claude_swarm/openai/executor.rb +3 -1
  8. data/lib/claude_swarm/openai/responses.rb +13 -32
  9. data/lib/claude_swarm/version.rb +1 -1
  10. data/lib/swarm_cli/commands/run.rb +2 -2
  11. data/lib/swarm_cli/config_loader.rb +11 -11
  12. data/lib/swarm_cli/formatters/human_formatter.rb +70 -0
  13. data/lib/swarm_cli/interactive_repl.rb +11 -5
  14. data/lib/swarm_cli/ui/icons.rb +0 -23
  15. data/lib/swarm_cli/version.rb +1 -1
  16. data/lib/swarm_memory/adapters/filesystem_adapter.rb +11 -34
  17. data/lib/swarm_memory/integration/sdk_plugin.rb +87 -7
  18. data/lib/swarm_memory/version.rb +1 -1
  19. data/lib/swarm_memory.rb +1 -1
  20. data/lib/swarm_sdk/agent/builder.rb +58 -0
  21. data/lib/swarm_sdk/agent/chat.rb +527 -1059
  22. data/lib/swarm_sdk/agent/{chat → chat_helpers}/context_tracker.rb +9 -88
  23. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +204 -0
  24. data/lib/swarm_sdk/agent/{chat → chat_helpers}/hook_integration.rb +111 -44
  25. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +78 -0
  26. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +233 -0
  27. data/lib/swarm_sdk/agent/{chat → chat_helpers}/logging_helpers.rb +1 -1
  28. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +83 -0
  29. data/lib/swarm_sdk/agent/{chat → chat_helpers}/system_reminder_injector.rb +12 -12
  30. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +79 -0
  31. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +98 -0
  32. data/lib/swarm_sdk/agent/context.rb +2 -2
  33. data/lib/swarm_sdk/agent/definition.rb +66 -154
  34. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +4 -2
  35. data/lib/swarm_sdk/agent/system_prompt_builder.rb +161 -0
  36. data/lib/swarm_sdk/builders/base_builder.rb +409 -0
  37. data/lib/swarm_sdk/concerns/cleanupable.rb +39 -0
  38. data/lib/swarm_sdk/concerns/snapshotable.rb +67 -0
  39. data/lib/swarm_sdk/concerns/validatable.rb +55 -0
  40. data/lib/swarm_sdk/configuration/parser.rb +353 -0
  41. data/lib/swarm_sdk/configuration/translator.rb +255 -0
  42. data/lib/swarm_sdk/configuration.rb +65 -543
  43. data/lib/swarm_sdk/context_compactor/token_counter.rb +3 -3
  44. data/lib/swarm_sdk/context_compactor.rb +6 -11
  45. data/lib/swarm_sdk/context_management/builder.rb +128 -0
  46. data/lib/swarm_sdk/context_management/context.rb +328 -0
  47. data/lib/swarm_sdk/defaults.rb +196 -0
  48. data/lib/swarm_sdk/events_to_messages.rb +18 -0
  49. data/lib/swarm_sdk/hooks/shell_executor.rb +2 -1
  50. data/lib/swarm_sdk/log_collector.rb +179 -29
  51. data/lib/swarm_sdk/log_stream.rb +29 -0
  52. data/lib/swarm_sdk/node_context.rb +1 -1
  53. data/lib/swarm_sdk/observer/builder.rb +81 -0
  54. data/lib/swarm_sdk/observer/config.rb +45 -0
  55. data/lib/swarm_sdk/observer/manager.rb +236 -0
  56. data/lib/swarm_sdk/patterns/agent_observer.rb +160 -0
  57. data/lib/swarm_sdk/plugin.rb +93 -3
  58. data/lib/swarm_sdk/snapshot.rb +6 -6
  59. data/lib/swarm_sdk/snapshot_from_events.rb +13 -2
  60. data/lib/swarm_sdk/state_restorer.rb +136 -151
  61. data/lib/swarm_sdk/state_snapshot.rb +65 -100
  62. data/lib/swarm_sdk/swarm/agent_initializer.rb +180 -136
  63. data/lib/swarm_sdk/swarm/builder.rb +44 -578
  64. data/lib/swarm_sdk/swarm/executor.rb +213 -0
  65. data/lib/swarm_sdk/swarm/hook_triggers.rb +150 -0
  66. data/lib/swarm_sdk/swarm/logging_callbacks.rb +340 -0
  67. data/lib/swarm_sdk/swarm/mcp_configurator.rb +7 -4
  68. data/lib/swarm_sdk/swarm/tool_configurator.rb +42 -138
  69. data/lib/swarm_sdk/swarm.rb +137 -679
  70. data/lib/swarm_sdk/tools/bash.rb +11 -3
  71. data/lib/swarm_sdk/tools/delegate.rb +61 -43
  72. data/lib/swarm_sdk/tools/edit.rb +8 -13
  73. data/lib/swarm_sdk/tools/glob.rb +9 -1
  74. data/lib/swarm_sdk/tools/grep.rb +7 -0
  75. data/lib/swarm_sdk/tools/multi_edit.rb +15 -11
  76. data/lib/swarm_sdk/tools/path_resolver.rb +51 -2
  77. data/lib/swarm_sdk/tools/read.rb +11 -13
  78. data/lib/swarm_sdk/tools/registry.rb +122 -10
  79. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +8 -5
  80. data/lib/swarm_sdk/tools/stores/storage.rb +0 -6
  81. data/lib/swarm_sdk/tools/todo_write.rb +7 -0
  82. data/lib/swarm_sdk/tools/web_fetch.rb +3 -2
  83. data/lib/swarm_sdk/tools/write.rb +8 -13
  84. data/lib/swarm_sdk/version.rb +1 -1
  85. data/lib/swarm_sdk/{node → workflow}/agent_config.rb +1 -1
  86. data/lib/swarm_sdk/workflow/builder.rb +143 -0
  87. data/lib/swarm_sdk/workflow/executor.rb +497 -0
  88. data/lib/swarm_sdk/{node/builder.rb → workflow/node_builder.rb} +3 -3
  89. data/lib/swarm_sdk/{node → workflow}/transformer_executor.rb +3 -2
  90. data/lib/swarm_sdk/{node_orchestrator.rb → workflow.rb} +152 -456
  91. data/lib/swarm_sdk.rb +33 -3
  92. metadata +37 -14
  93. data/lib/swarm_memory/chat_extension.rb +0 -34
  94. data/lib/swarm_sdk/providers/openai_with_responses.rb +0 -589
@@ -156,7 +156,7 @@ module SwarmMemory
156
156
  # @return [String] Memory prompt contribution
157
157
  def system_prompt_contribution(agent_definition:, storage:)
158
158
  # Extract mode from memory config
159
- memory_config = agent_definition.memory
159
+ memory_config = agent_definition.plugin_config(:memory)
160
160
  mode = if memory_config.is_a?(SwarmMemory::DSL::MemoryConfig)
161
161
  memory_config.mode # MemoryConfig object from DSL
162
162
  elsif memory_config.respond_to?(:mode)
@@ -204,20 +204,100 @@ module SwarmMemory
204
204
  # @param agent_definition [Agent::Definition] Agent definition
205
205
  # @return [Boolean] True if agent has memory configuration
206
206
  def storage_enabled?(agent_definition)
207
- agent_definition.memory_enabled?
207
+ memory_config = agent_definition.plugin_config(:memory)
208
+ return false if memory_config.nil?
209
+
210
+ # MemoryConfig object (from DSL)
211
+ return memory_config.enabled? if memory_config.respond_to?(:enabled?)
212
+
213
+ # Hash (from YAML) - check for directory key
214
+ if memory_config.is_a?(Hash)
215
+ directory = memory_config[:directory] || memory_config["directory"]
216
+ return !directory.nil? && !directory.to_s.strip.empty?
217
+ end
218
+
219
+ false
208
220
  end
209
221
 
210
222
  # Contribute to agent serialization
211
223
  #
212
- # Preserves memory configuration when agents are cloned (e.g., in NodeOrchestrator).
224
+ # Preserves memory configuration when agents are cloned (e.g., in Workflow).
213
225
  # This allows memory configuration to persist across node transitions.
214
226
  #
215
227
  # @param agent_definition [Agent::Definition] Agent definition
216
228
  # @return [Hash] Memory config to include in to_h
217
229
  def serialize_config(agent_definition:)
218
- return {} unless agent_definition.memory
230
+ memory_config = agent_definition.plugin_config(:memory)
231
+ return {} unless memory_config
232
+
233
+ { memory: memory_config }
234
+ end
235
+
236
+ # Snapshot plugin-specific state for an agent
237
+ #
238
+ # Captures memory read tracking state for session persistence.
239
+ # This allows agents to remember which memory entries they've read
240
+ # across sessions.
241
+ #
242
+ # @param agent_name [Symbol] Agent identifier
243
+ # @return [Hash] Plugin-specific state
244
+ def snapshot_agent_state(agent_name)
245
+ entries_with_digests = Core::StorageReadTracker.get_read_entries(agent_name)
246
+ return {} if entries_with_digests.empty?
247
+
248
+ { read_entries: entries_with_digests }
249
+ end
250
+
251
+ # Restore plugin-specific state for an agent
252
+ #
253
+ # Restores memory read tracking state from snapshot.
254
+ # This is idempotent - calling multiple times with same state
255
+ # produces the same result.
256
+ #
257
+ # @param agent_name [Symbol] Agent identifier
258
+ # @param state [Hash] Previously snapshotted state (with symbol keys)
259
+ # @return [void]
260
+ def restore_agent_state(agent_name, state)
261
+ entries = state[:read_entries] || state["read_entries"]
262
+ return unless entries
263
+
264
+ Core::StorageReadTracker.restore_read_entries(agent_name, entries)
265
+ end
266
+
267
+ # Get digest for a memory tool result
268
+ #
269
+ # Returns the digest for a MemoryRead tool call, enabling change detection
270
+ # hooks to know if a memory entry has been modified since last read.
271
+ #
272
+ # @param agent_name [Symbol] Agent identifier
273
+ # @param tool_name [String] Name of the tool
274
+ # @param path [String] Path of the memory entry
275
+ # @return [String, nil] Digest string or nil if not a memory tool
276
+ def get_tool_result_digest(agent_name:, tool_name:, path:)
277
+ return unless tool_name == "MemoryRead"
219
278
 
220
- { memory: agent_definition.memory }
279
+ Core::StorageReadTracker.get_read_entries(agent_name)[path]
280
+ end
281
+
282
+ # Translate YAML configuration into DSL calls
283
+ #
284
+ # Called during YAML-to-DSL translation. Handles memory-specific YAML
285
+ # configuration and translates it into DSL method calls on the builder.
286
+ #
287
+ # @param builder [Agent::Builder] Builder instance (self in DSL context)
288
+ # @param agent_config [Hash] Full agent config from YAML
289
+ # @return [void]
290
+ def translate_yaml_config(builder, agent_config)
291
+ memory_config = agent_config[:memory]
292
+ return unless memory_config
293
+
294
+ builder.instance_eval do
295
+ memory do
296
+ directory(memory_config[:directory]) if memory_config[:directory]
297
+ adapter(memory_config[:adapter]) if memory_config[:adapter]
298
+ mode(memory_config[:mode]) if memory_config[:mode]
299
+ end
300
+ end
221
301
  end
222
302
 
223
303
  # Lifecycle: Agent initialized
@@ -239,7 +319,7 @@ module SwarmMemory
239
319
  return unless storage # Only proceed if memory is enabled for this agent
240
320
 
241
321
  # Extract mode from memory config
242
- memory_config = agent_definition.memory
322
+ memory_config = agent_definition.plugin_config(:memory)
243
323
  mode = if memory_config.is_a?(SwarmMemory::DSL::MemoryConfig)
244
324
  memory_config.mode # MemoryConfig object from DSL
245
325
  elsif memory_config.respond_to?(:mode)
@@ -281,7 +361,7 @@ module SwarmMemory
281
361
  agent_definition: agent_definition,
282
362
  )
283
363
 
284
- agent.with_tool(load_skill_tool)
364
+ agent.add_tool(load_skill_tool)
285
365
  end
286
366
 
287
367
  # Mark mode-specific memory tools + LoadSkill as immutable
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwarmMemory
4
- VERSION = "2.1.3"
4
+ VERSION = "2.1.4"
5
5
  end
data/lib/swarm_memory.rb CHANGED
@@ -42,7 +42,7 @@ loader.setup
42
42
  # These must be loaded after Zeitwerk but before anything uses them
43
43
  require_relative "swarm_memory/dsl/memory_config"
44
44
  require_relative "swarm_memory/dsl/builder_extension"
45
- require_relative "swarm_memory/chat_extension"
45
+ # NOTE: ChatExtension was removed in favor of SDK's built-in remove_tool method
46
46
 
47
47
  module SwarmMemory
48
48
  class << self
@@ -60,6 +60,7 @@ module SwarmSDK
60
60
  @default_permissions = {} # Set by SwarmBuilder from all_agents
61
61
  @memory_config = nil
62
62
  @shared_across_delegations = nil # nil = not set (will default to false in Definition)
63
+ @context_management_config = nil # Context management DSL hooks
63
64
  end
64
65
 
65
66
  # Set/get agent model
@@ -288,6 +289,56 @@ module SwarmSDK
288
289
  self
289
290
  end
290
291
 
292
+ # Configure context management handlers
293
+ #
294
+ # Define custom handlers for context warning thresholds (60%, 80%, 90%).
295
+ # Handlers receive a rich context object with message manipulation methods.
296
+ # When a custom handler is registered, automatic compression is disabled
297
+ # for that threshold, giving full control to the handler.
298
+ #
299
+ # @yield Context management DSL block
300
+ # @return [void]
301
+ #
302
+ # @example Basic compression at 60%
303
+ # context_management do
304
+ # on :warning_60 do |ctx|
305
+ # ctx.compress_tool_results(keep_recent: 10)
306
+ # end
307
+ # end
308
+ #
309
+ # @example Multiple thresholds with different strategies
310
+ # context_management do
311
+ # on :warning_60 do |ctx|
312
+ # ctx.compress_tool_results(keep_recent: 15, truncate_to: 500)
313
+ # end
314
+ #
315
+ # on :warning_80 do |ctx|
316
+ # ctx.prune_old_messages(keep_recent: 30)
317
+ # ctx.compress_tool_results(keep_recent: 5, truncate_to: 200)
318
+ # end
319
+ #
320
+ # on :warning_90 do |ctx|
321
+ # ctx.log_action("emergency_pruning", remaining: ctx.tokens_remaining)
322
+ # ctx.prune_old_messages(keep_recent: 15)
323
+ # end
324
+ # end
325
+ #
326
+ # @example Conditional logic based on metrics
327
+ # context_management do
328
+ # on :warning_80 do |ctx|
329
+ # if ctx.usage_percentage > 85
330
+ # ctx.prune_old_messages(keep_recent: 10)
331
+ # else
332
+ # ctx.compress_tool_results(keep_recent: 5)
333
+ # end
334
+ # end
335
+ # end
336
+ def context_management(&block)
337
+ builder = ContextManagement::Builder.new
338
+ builder.instance_eval(&block)
339
+ @context_management_config = builder.build
340
+ end
341
+
291
342
  # Set permissions directly from hash (for YAML translation)
292
343
  #
293
344
  # This is intentionally separate from permissions() to keep the DSL clean.
@@ -411,6 +462,13 @@ module SwarmSDK
411
462
  # Convert DSL hooks to HookDefinition format
412
463
  agent_config[:hooks] = convert_hooks_to_definitions if @hooks.any?
413
464
 
465
+ # Merge context management hooks into agent hooks
466
+ if @context_management_config
467
+ agent_config[:hooks] ||= {}
468
+ agent_config[:hooks][:context_warning] ||= []
469
+ agent_config[:hooks][:context_warning].concat(@context_management_config)
470
+ end
471
+
414
472
  Agent::Definition.new(@name, agent_config)
415
473
  end
416
474