swarm_sdk 2.7.14 → 3.0.0.alpha1

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 (181) 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/edit.rb +111 -0
  42. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  43. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  44. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  45. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  46. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  47. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  48. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  49. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  50. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  51. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  52. data/lib/swarm_sdk/v3.rb +145 -0
  53. metadata +83 -148
  54. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  55. data/lib/swarm_sdk/agent/builder.rb +0 -705
  56. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  57. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  58. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  59. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  60. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  61. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  62. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  63. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  64. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  65. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  66. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  67. data/lib/swarm_sdk/agent/context.rb +0 -115
  68. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  69. data/lib/swarm_sdk/agent/definition.rb +0 -588
  70. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  71. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  72. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  73. data/lib/swarm_sdk/agent_registry.rb +0 -146
  74. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  75. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  76. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  77. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  78. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  79. data/lib/swarm_sdk/config.rb +0 -368
  80. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  81. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  82. data/lib/swarm_sdk/configuration.rb +0 -165
  83. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  84. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  85. data/lib/swarm_sdk/context_compactor.rb +0 -335
  86. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  87. data/lib/swarm_sdk/context_management/context.rb +0 -328
  88. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  89. data/lib/swarm_sdk/defaults.rb +0 -251
  90. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  91. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  92. data/lib/swarm_sdk/hooks/context.rb +0 -197
  93. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  94. data/lib/swarm_sdk/hooks/error.rb +0 -29
  95. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  96. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  97. data/lib/swarm_sdk/hooks/result.rb +0 -150
  98. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  99. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  100. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  101. data/lib/swarm_sdk/log_collector.rb +0 -227
  102. data/lib/swarm_sdk/log_stream.rb +0 -127
  103. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  104. data/lib/swarm_sdk/model_aliases.json +0 -8
  105. data/lib/swarm_sdk/models.json +0 -44002
  106. data/lib/swarm_sdk/models.rb +0 -161
  107. data/lib/swarm_sdk/node_context.rb +0 -245
  108. data/lib/swarm_sdk/observer/builder.rb +0 -81
  109. data/lib/swarm_sdk/observer/config.rb +0 -45
  110. data/lib/swarm_sdk/observer/manager.rb +0 -248
  111. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  112. data/lib/swarm_sdk/permissions/config.rb +0 -239
  113. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  114. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  115. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  116. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  117. data/lib/swarm_sdk/plugin.rb +0 -309
  118. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  119. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  120. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  121. data/lib/swarm_sdk/restore_result.rb +0 -65
  122. data/lib/swarm_sdk/result.rb +0 -241
  123. data/lib/swarm_sdk/snapshot.rb +0 -156
  124. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  125. data/lib/swarm_sdk/state_restorer.rb +0 -476
  126. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  127. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  128. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  129. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  130. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  131. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  132. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  133. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  134. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  135. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  136. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  137. data/lib/swarm_sdk/swarm.rb +0 -973
  138. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  139. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  140. data/lib/swarm_sdk/tools/base.rb +0 -63
  141. data/lib/swarm_sdk/tools/bash.rb +0 -280
  142. data/lib/swarm_sdk/tools/clock.rb +0 -46
  143. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  144. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  145. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  146. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  147. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  148. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  149. data/lib/swarm_sdk/tools/edit.rb +0 -145
  150. data/lib/swarm_sdk/tools/glob.rb +0 -166
  151. data/lib/swarm_sdk/tools/grep.rb +0 -235
  152. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  153. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  154. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  155. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  156. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  157. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  158. data/lib/swarm_sdk/tools/read.rb +0 -261
  159. data/lib/swarm_sdk/tools/registry.rb +0 -205
  160. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  161. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  163. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  164. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  165. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  166. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  167. data/lib/swarm_sdk/tools/think.rb +0 -100
  168. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  169. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  170. data/lib/swarm_sdk/tools/write.rb +0 -112
  171. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  172. data/lib/swarm_sdk/utils.rb +0 -68
  173. data/lib/swarm_sdk/validation_result.rb +0 -33
  174. data/lib/swarm_sdk/version.rb +0 -5
  175. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  176. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  177. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  178. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  179. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  180. data/lib/swarm_sdk/workflow.rb +0 -589
  181. 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
- }