swarm_sdk 2.7.14 → 3.0.0.alpha2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Hooks
5
- # Represents a tool call in the hooks system
6
- #
7
- # This is a simple value object that wraps tool call information
8
- # from RubyLLM in a consistent, immutable format for hooks.
9
- #
10
- # @example Access tool call information in a hook
11
- # swarm.add_callback(:pre_tool_use) do |context|
12
- # puts "Tool: #{context.tool_call.name}"
13
- # puts "Parameters: #{context.tool_call.parameters.inspect}"
14
- # end
15
- class ToolCall
16
- attr_reader :id, :name, :parameters
17
-
18
- # @param id [String] Unique identifier for this tool call
19
- # @param name [String] Name of the tool being called
20
- # @param parameters [Hash] Parameters passed to the tool
21
- def initialize(id:, name:, parameters:)
22
- @id = id
23
- @name = name
24
- @parameters = parameters
25
- end
26
-
27
- # Convert to hash representation
28
- #
29
- # @return [Hash] Hash with id, name, and parameters
30
- def to_h
31
- { id: @id, name: @name, parameters: @parameters }
32
- end
33
- end
34
- end
35
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Hooks
5
- # Represents the result of a tool execution
6
- #
7
- # This is a simple value object that wraps tool execution results
8
- # in a consistent format for post-tool-use hooks.
9
- #
10
- # @example Access tool result in a hook
11
- # swarm.add_callback(:post_tool_use) do |context|
12
- # if context.tool_result.success?
13
- # puts "Tool succeeded: #{context.tool_result.content}"
14
- # else
15
- # puts "Tool failed: #{context.tool_result.error}"
16
- # end
17
- # end
18
- class ToolResult
19
- attr_reader :tool_call_id, :tool_name, :content, :success, :error
20
-
21
- # @param tool_call_id [String] ID of the tool call this result corresponds to
22
- # @param tool_name [String] Name of the tool that was executed
23
- # @param content [String, nil] Result content (if successful)
24
- # @param success [Boolean] Whether the tool execution succeeded
25
- # @param error [String, nil] Error message (if failed)
26
- def initialize(tool_call_id:, tool_name:, content: nil, success: true, error: nil)
27
- @tool_call_id = tool_call_id
28
- @tool_name = tool_name
29
- @content = content
30
- @success = success
31
- @error = error
32
- end
33
-
34
- # Check if the tool execution succeeded
35
- #
36
- # @return [Boolean] true if successful
37
- def success?
38
- @success
39
- end
40
-
41
- # Check if the tool execution failed
42
- #
43
- # @return [Boolean] true if failed
44
- def failure?
45
- !@success
46
- end
47
-
48
- # Convert to hash representation
49
- #
50
- # @return [Hash] Hash with all result attributes
51
- def to_h
52
- {
53
- tool_call_id: @tool_call_id,
54
- tool_name: @tool_name,
55
- content: @content,
56
- success: @success,
57
- error: @error,
58
- }
59
- end
60
- end
61
- end
62
- end
@@ -1,227 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # LogCollector manages subscriber callbacks for log events with filtering support.
5
- #
6
- # This module acts as an emitter implementation that forwards events
7
- # to user-registered callbacks. It's designed to be set as the LogStream
8
- # emitter during swarm execution.
9
- #
10
- # ## Features
11
- #
12
- # - **Filtered Subscriptions**: Subscribe to specific agents, event types, or swarm IDs
13
- # - **Unsubscribe Support**: Remove subscriptions by ID to prevent memory leaks
14
- # - **Error Isolation**: One subscriber's error doesn't break others
15
- # - **Thread Safety**: Fiber-local storage for multi-threaded environments
16
- #
17
- # ## Thread Safety for Multi-Threaded Environments (Puma, Sidekiq)
18
- #
19
- # Subscriptions are stored in Fiber-local storage (Fiber[:log_subscriptions]) instead
20
- # of class instance variables. This ensures callbacks registered in the parent
21
- # thread/fiber are accessible to child fibers created by Async reactor.
22
- #
23
- # Why: In Puma/Sidekiq, class instance variables (@subscriptions) are thread-isolated
24
- # and don't properly propagate to child fibers. Using Fiber-local storage ensures
25
- # events emitted from within Async blocks can reach registered subscriptions.
26
- #
27
- # Child fibers inherit parent fiber-local storage automatically, so events
28
- # emitted from agent callbacks (on_tool_call, on_end_message, etc.) executing
29
- # in child fibers can still reach the parent's registered subscriptions.
30
- #
31
- # ## Usage
32
- #
33
- # # Subscribe to all events
34
- # sub_id = LogCollector.subscribe { |event| puts event }
35
- #
36
- # # Subscribe to specific agent
37
- # sub_id = LogCollector.subscribe(filter: { agent: :backend }) { |event|
38
- # puts "Backend: #{event}"
39
- # }
40
- #
41
- # # Subscribe to specific event types
42
- # sub_id = LogCollector.subscribe(filter: { type: ["tool_call", "tool_result"] }) { |event|
43
- # log_tool_activity(event)
44
- # }
45
- #
46
- # # Subscribe with regex matching
47
- # sub_id = LogCollector.subscribe(filter: { type: /^tool_/ }) { |event|
48
- # track_tool_usage(event)
49
- # }
50
- #
51
- # # Unsubscribe when done
52
- # LogCollector.unsubscribe(sub_id)
53
- #
54
- # # After execution, reset for next use
55
- # LogCollector.reset!
56
- #
57
- module LogCollector
58
- # Subscription object with filtering capabilities
59
- #
60
- # Encapsulates a callback with optional filters. Filters can match
61
- # against agent name, event type, swarm_id, or any event field.
62
- class Subscription
63
- attr_reader :id, :filter, :callback
64
-
65
- # Initialize a subscription
66
- #
67
- # @param filter [Hash] Filter criteria
68
- # @param callback [Proc] Block to call when event matches
69
- def initialize(filter: {}, &callback)
70
- @id = SecureRandom.uuid
71
- @filter = normalize_filter(filter)
72
- @callback = callback
73
- end
74
-
75
- # Check if event matches filter criteria
76
- #
77
- # Empty filter matches all events. Multiple filter keys are AND'd together.
78
- #
79
- # @param event [Hash] Event entry with :type, :agent, etc.
80
- # @return [Boolean] True if event matches filter
81
- def matches?(event)
82
- return true if @filter.empty?
83
-
84
- @filter.all? do |key, matcher|
85
- value = event[key]
86
- case matcher
87
- when Array
88
- # Match if value is in array (handles symbols/strings)
89
- matcher.include?(value) || matcher.map(&:to_s).include?(value.to_s)
90
- when Regexp
91
- # Regex matching
92
- value.to_s.match?(matcher)
93
- when Proc
94
- # Custom matcher
95
- matcher.call(value)
96
- else
97
- # Exact match (handles symbols/strings)
98
- matcher == value || matcher.to_s == value.to_s
99
- end
100
- end
101
- end
102
-
103
- private
104
-
105
- # Normalize filter keys to symbols for consistent matching
106
- #
107
- # @param filter [Hash] Raw filter
108
- # @return [Hash] Normalized filter with symbol keys
109
- def normalize_filter(filter)
110
- filter.transform_keys(&:to_sym)
111
- end
112
- end
113
-
114
- class << self
115
- # Subscribe to log events with optional filtering
116
- #
117
- # Registers a callback that will receive events matching the filter criteria.
118
- # Returns a subscription ID that can be used to unsubscribe later.
119
- #
120
- # @param filter [Hash] Filter criteria (empty = all events)
121
- # - :agent [Symbol, String, Array, Regexp] Agent name(s) to observe
122
- # - :type [String, Array, Regexp] Event type(s) to receive
123
- # - :swarm_id [String] Specific swarm instance
124
- # - Any other key matches against event fields
125
- # @yield [Hash] Log event entry
126
- # @return [String] Subscription ID for unsubscribe
127
- #
128
- # @example Subscribe to all events
129
- # LogCollector.subscribe { |event| puts event }
130
- #
131
- # @example Subscribe to specific agent's tool calls
132
- # sub_id = LogCollector.subscribe(
133
- # filter: { agent: :backend, type: /^tool_/ }
134
- # ) do |event|
135
- # puts "Backend used tool: #{event[:tool]}"
136
- # end
137
- #
138
- # @example Subscribe to multiple agents
139
- # LogCollector.subscribe(
140
- # filter: { agent: [:backend, :frontend], type: "agent_stop" }
141
- # ) { |e| record_completion(e) }
142
- def subscribe(filter: {}, &block)
143
- subscription = Subscription.new(filter: filter, &block)
144
- subscriptions << subscription
145
- subscription.id
146
- end
147
-
148
- # Unsubscribe by ID
149
- #
150
- # Removes a subscription to prevent memory leaks and stop receiving events.
151
- #
152
- # @param subscription_id [String] ID returned from subscribe
153
- # @return [Subscription, nil] Removed subscription or nil if not found
154
- def unsubscribe(subscription_id)
155
- index = subscriptions.find_index { |s| s.id == subscription_id }
156
- return unless index
157
-
158
- subscriptions.delete_at(index)
159
- end
160
-
161
- # Clear all subscriptions
162
- #
163
- # Removes all subscriptions. Useful for testing or execution cleanup.
164
- #
165
- # @return [void]
166
- def clear_subscriptions
167
- subscriptions.clear
168
- end
169
-
170
- # Get current subscription count
171
- #
172
- # @return [Integer] Number of active subscriptions
173
- def subscription_count
174
- subscriptions.size
175
- end
176
-
177
- # Emit an event to all matching subscribers
178
- #
179
- # Automatically adds a timestamp if one doesn't exist.
180
- # Errors in individual subscribers are isolated - one bad subscriber
181
- # won't prevent others from receiving events.
182
- #
183
- # @param entry [Hash] Log event entry
184
- # @return [void]
185
- def emit(entry)
186
- entry_with_timestamp = ensure_timestamp(entry)
187
-
188
- subscriptions.each do |subscription|
189
- next unless subscription.matches?(entry_with_timestamp)
190
-
191
- begin
192
- subscription.callback.call(entry_with_timestamp)
193
- rescue StandardError => e
194
- # Error isolation - don't let one subscriber break others
195
- RubyLLM.logger.error("SwarmSDK: Subscription #{subscription.id} error: #{e.message}")
196
- end
197
- end
198
- end
199
-
200
- # Reset the collector (clears subscriptions for next execution)
201
- #
202
- # @return [void]
203
- def reset!
204
- Fiber[:log_subscriptions] = []
205
- end
206
-
207
- private
208
-
209
- # Get subscriptions from Fiber-local storage
210
- #
211
- # @return [Array<Subscription>] Current subscriptions
212
- def subscriptions
213
- Fiber[:log_subscriptions] ||= []
214
- end
215
-
216
- # Ensure event has a timestamp
217
- #
218
- # @param entry [Hash] Event entry
219
- # @return [Hash] Entry with timestamp
220
- def ensure_timestamp(entry)
221
- return entry if entry.key?(:timestamp)
222
-
223
- entry.merge(timestamp: Time.now.utc.iso8601(6))
224
- end
225
- end
226
- end
227
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # LogStream provides a module-level singleton for emitting log events.
5
- #
6
- # This allows any component (tools, providers, agents) to emit structured
7
- # log events without needing references to logger instances.
8
- #
9
- # ## Usage
10
- #
11
- # # Emit an event from anywhere in the SDK
12
- # LogStream.emit(
13
- # type: "user_prompt",
14
- # agent: :backend,
15
- # model: "claude-sonnet-4",
16
- # message_count: 5
17
- # )
18
- #
19
- # ## Thread Safety
20
- #
21
- # LogStream is thread-safe and fiber-safe:
22
- # - Uses Fiber storage for per-request isolation in multi-threaded servers (Puma, Sidekiq)
23
- # - Each thread/request has its own emitter instance
24
- # - Child fibers inherit the emitter from their parent fiber
25
- # - No cross-thread contamination of log events
26
- #
27
- # Usage pattern:
28
- # 1. Set emitter BEFORE starting Async execution
29
- # 2. During Async execution, only emit() (reads emitter)
30
- # 3. Each event includes agent context for identification
31
- #
32
- # ## Testing
33
- #
34
- # # Inject a test emitter
35
- # LogStream.emitter = TestEmitter.new
36
- # # ... run tests ...
37
- # LogStream.reset!
38
- #
39
- module LogStream
40
- class << self
41
- # Emit a log event
42
- #
43
- # Adds timestamp and forwards to the registered emitter.
44
- # Auto-injects execution_id, swarm_id, and parent_swarm_id from Fiber storage.
45
- # Explicit values in data override auto-injected ones.
46
- #
47
- # @param data [Hash] Event data (type, agent, and event-specific fields)
48
- # @return [void]
49
- def emit(**data)
50
- emitter = Fiber[:log_stream_emitter]
51
- return unless emitter
52
-
53
- # Auto-inject execution context from Fiber storage
54
- # Explicit values in data override auto-injected ones
55
- auto_injected = {
56
- execution_id: Fiber[:execution_id],
57
- swarm_id: Fiber[:swarm_id],
58
- parent_swarm_id: Fiber[:parent_swarm_id],
59
- }.compact
60
-
61
- entry = auto_injected.merge(data).merge(timestamp: Time.now.utc.iso8601(6)).compact
62
-
63
- emitter.emit(entry)
64
- end
65
-
66
- # Set the emitter (for dependency injection in tests)
67
- #
68
- # Stores emitter in Fiber storage for thread-safe, per-request isolation.
69
- #
70
- # @param emitter [#emit] Object responding to emit(Hash)
71
- # @return [void]
72
- def emitter=(emitter)
73
- Fiber[:log_stream_emitter] = emitter
74
- end
75
-
76
- # Get the current emitter
77
- #
78
- # @return [#emit, nil] Current emitter or nil if not set
79
- def emitter
80
- Fiber[:log_stream_emitter]
81
- end
82
-
83
- # Reset the emitter (for test cleanup)
84
- #
85
- # @return [void]
86
- def reset!
87
- Fiber[:log_stream_emitter] = nil
88
- end
89
-
90
- # Check if logging is enabled
91
- #
92
- # @return [Boolean] true if an emitter is configured
93
- def enabled?
94
- !Fiber[:log_stream_emitter].nil?
95
- end
96
-
97
- # Emit an internal error event
98
- #
99
- # Provides consistent error event emission for all internal errors.
100
- # These are errors that occur during execution but are handled gracefully
101
- # (with fallback behavior) rather than causing failures.
102
- #
103
- # @param error [Exception] The caught exception
104
- # @param source [String] Source module/class (e.g., "hook_triggers", "context_compactor")
105
- # @param context [String] Specific operation context (e.g., "swarm_stop", "summarization")
106
- # @param agent [Symbol, String, nil] Agent name if applicable
107
- # @param metadata [Hash] Additional context data
108
- # @return [void]
109
- def emit_error(error, source:, context:, agent: nil, **metadata)
110
- emit(
111
- type: "internal_error",
112
- source: source,
113
- context: context,
114
- agent: agent,
115
- error_class: error.class.name,
116
- error_message: error.message,
117
- backtrace: error.backtrace&.first(5),
118
- **metadata,
119
- )
120
- rescue StandardError
121
- # Absolute fallback - if emit_error itself fails, don't break execution
122
- # This should never happen, but we must be defensive
123
- nil
124
- end
125
- end
126
- end
127
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Parser for agent markdown files with YAML frontmatter
5
- #
6
- # Supports two formats:
7
- # 1. SwarmSDK format - YAML frontmatter with array-based tools
8
- # 2. Claude Code format - Detected and converted via ClaudeCodeAgentAdapter
9
- #
10
- # Format detection is automatic based on frontmatter structure.
11
- class MarkdownParser
12
- FRONTMATTER_PATTERN = /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
13
-
14
- class << self
15
- # Parse markdown content into an Agent::Definition
16
- #
17
- # Automatically detects format (SwarmSDK or Claude Code) and routes
18
- # to appropriate parser.
19
- #
20
- # @param content [String] Markdown content with YAML frontmatter
21
- # @param agent_name [Symbol, String, nil] Name of the agent
22
- # @return [Agent::Definition] Parsed agent definition
23
- # @raise [ConfigurationError] if format is invalid
24
- def parse(content, agent_name = nil)
25
- # Detect Claude Code format and route to adapter
26
- if ClaudeCodeAgentAdapter.claude_code_format?(content)
27
- config = ClaudeCodeAgentAdapter.parse(content, agent_name)
28
- # For Claude Code format, agent_name parameter is required since
29
- # the 'name' field in frontmatter is Claude Code specific and not used
30
- unless agent_name
31
- raise ConfigurationError, "Agent name must be provided when parsing Claude Code format"
32
- end
33
-
34
- Agent::Definition.new(agent_name.to_sym, config)
35
- else
36
- # Use standard SwarmSDK format parsing
37
- new(content, agent_name).parse
38
- end
39
- end
40
- end
41
-
42
- def initialize(content, agent_name = nil)
43
- @content = content
44
- @agent_name = agent_name
45
- end
46
-
47
- def parse
48
- if @content =~ FRONTMATTER_PATTERN
49
- frontmatter_yaml = Regexp.last_match(1)
50
- prompt_content = Regexp.last_match(2).strip
51
-
52
- frontmatter = YAML.safe_load(frontmatter_yaml, permitted_classes: [Symbol], aliases: true)
53
-
54
- unless frontmatter.is_a?(Hash)
55
- raise ConfigurationError, "Invalid frontmatter format in agent definition"
56
- end
57
-
58
- # Symbolize keys for AgentDefinition
59
- config = Utils.symbolize_keys(frontmatter).merge(system_prompt: prompt_content)
60
-
61
- name = @agent_name || frontmatter["name"]
62
- unless name
63
- raise ConfigurationError, "Agent definition must include 'name' in frontmatter or be specified externally"
64
- end
65
-
66
- # Convert name to symbol
67
- name = name.to_sym
68
-
69
- Agent::Definition.new(name, config)
70
- else
71
- raise ConfigurationError, "Invalid Markdown agent definition format. Expected YAML frontmatter followed by prompt content."
72
- end
73
- end
74
- end
75
- end
@@ -1,8 +0,0 @@
1
- {
2
- "sonnet": "claude-sonnet-4-5-20250929",
3
- "opus": "claude-opus-4-1-20250805",
4
- "haiku": "claude-haiku-4-5-20251001",
5
- "claude-sonnet-4-5": "claude-sonnet-4-5-20250929",
6
- "claude-opus-4-1": "claude-opus-4-1-20250805",
7
- "claude-haiku-4-5": "claude-haiku-4-5-20251001"
8
- }