swarm_sdk 2.7.13 → 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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
  4. data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
  5. data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
  6. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  7. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  8. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  9. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  10. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  11. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  12. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  13. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  14. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  15. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  16. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  17. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  18. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  19. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  20. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  24. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  25. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  26. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  27. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  28. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  29. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  30. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  31. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  32. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  33. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  34. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  35. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  36. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  37. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  38. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  39. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  40. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  41. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  42. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  43. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  45. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  46. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  47. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  48. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  49. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  50. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  51. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  52. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  53. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  54. data/lib/swarm_sdk/v3.rb +145 -0
  55. metadata +84 -148
  56. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  57. data/lib/swarm_sdk/agent/builder.rb +0 -680
  58. data/lib/swarm_sdk/agent/chat.rb +0 -1432
  59. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  60. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  61. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  62. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  63. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  64. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  65. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  66. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  67. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  68. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  69. data/lib/swarm_sdk/agent/context.rb +0 -115
  70. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  71. data/lib/swarm_sdk/agent/definition.rb +0 -581
  72. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  73. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  74. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  75. data/lib/swarm_sdk/agent_registry.rb +0 -146
  76. data/lib/swarm_sdk/builders/base_builder.rb +0 -553
  77. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  78. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  79. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  80. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  81. data/lib/swarm_sdk/config.rb +0 -367
  82. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  83. data/lib/swarm_sdk/configuration/translator.rb +0 -283
  84. data/lib/swarm_sdk/configuration.rb +0 -165
  85. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  86. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  87. data/lib/swarm_sdk/context_compactor.rb +0 -335
  88. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  89. data/lib/swarm_sdk/context_management/context.rb +0 -328
  90. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  91. data/lib/swarm_sdk/defaults.rb +0 -251
  92. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  93. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  94. data/lib/swarm_sdk/hooks/context.rb +0 -197
  95. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  96. data/lib/swarm_sdk/hooks/error.rb +0 -29
  97. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  98. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  99. data/lib/swarm_sdk/hooks/result.rb +0 -150
  100. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  101. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  102. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  103. data/lib/swarm_sdk/log_collector.rb +0 -227
  104. data/lib/swarm_sdk/log_stream.rb +0 -127
  105. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  106. data/lib/swarm_sdk/model_aliases.json +0 -8
  107. data/lib/swarm_sdk/models.json +0 -44002
  108. data/lib/swarm_sdk/models.rb +0 -161
  109. data/lib/swarm_sdk/node_context.rb +0 -245
  110. data/lib/swarm_sdk/observer/builder.rb +0 -81
  111. data/lib/swarm_sdk/observer/config.rb +0 -45
  112. data/lib/swarm_sdk/observer/manager.rb +0 -236
  113. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  114. data/lib/swarm_sdk/permissions/config.rb +0 -239
  115. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  116. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  117. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  118. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  119. data/lib/swarm_sdk/plugin.rb +0 -309
  120. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  121. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  122. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  123. data/lib/swarm_sdk/restore_result.rb +0 -65
  124. data/lib/swarm_sdk/result.rb +0 -212
  125. data/lib/swarm_sdk/snapshot.rb +0 -156
  126. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  127. data/lib/swarm_sdk/state_restorer.rb +0 -476
  128. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  129. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  130. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -195
  131. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  132. data/lib/swarm_sdk/swarm/executor.rb +0 -290
  133. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
  134. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  135. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
  136. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
  137. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  138. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  139. data/lib/swarm_sdk/swarm.rb +0 -843
  140. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  141. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  142. data/lib/swarm_sdk/tools/base.rb +0 -63
  143. data/lib/swarm_sdk/tools/bash.rb +0 -280
  144. data/lib/swarm_sdk/tools/clock.rb +0 -46
  145. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  146. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  147. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  148. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  149. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  150. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  151. data/lib/swarm_sdk/tools/edit.rb +0 -145
  152. data/lib/swarm_sdk/tools/glob.rb +0 -166
  153. data/lib/swarm_sdk/tools/grep.rb +0 -235
  154. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  155. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  156. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  157. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -100
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  182. data/lib/swarm_sdk/workflow.rb +0 -589
  183. data/lib/swarm_sdk.rb +0 -718
@@ -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
- }