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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 427d736e32c386d76df7481b50b95e272045b88eb93290353528fd48c2525d91
4
- data.tar.gz: fc3cf69eaa253871f0db9f6332dd62669af2941082f622e266f65e035f6b2b21
3
+ metadata.gz: 373c4df61931acf2a3ed260a6686068dc918f9d3ad6f3eea746f81a38711e2b5
4
+ data.tar.gz: 2b5414dd35362e4e58616092717b47b2207dcf54a886c10e74f5408f07686192
5
5
  SHA512:
6
- metadata.gz: d08958fe437094ce91bdae63e6e20290158417270d1d39446fbb9cc45b2ea02bfbbae99dccfb36097fb053a2190f4cee1fd101c876eda1a388c19ba3ba4cdf4c
7
- data.tar.gz: cf06e09fe354ca95035d27e5ee4d710be461757829c1448c5d178b9f8a9b9372419e27bc036101d9ad0add625459784fa766413695f3f030efcba8fa97ee8467
6
+ metadata.gz: 46a0bba9e559d4acc92dbb5bf68776471bf2409a2aa0cee9b3ed458e0d9028b24b69534b6963a9a1b57de23ff305c9d69c7a927828f80de4de7750561311bb4f
7
+ data.tar.gz: add457e2f8a04cca43f0fd9f169207695cad562a7f7c0b98a217a002560d335e693cca5ef0a39ad0438a7d0ff818246f97d43a0155d103f9d3944e73cb9e3027
@@ -63,6 +63,7 @@ module RubyLLM
63
63
  # Initialize around hooks
64
64
  @around_tool_execution_hook = nil
65
65
  @around_llm_request_hook = nil
66
+ @after_tool_calls_hook = nil
66
67
 
67
68
  # Keep @on for backward compatibility (read-only)
68
69
  @on = nil
@@ -130,6 +131,19 @@ module RubyLLM
130
131
  self
131
132
  end
132
133
 
134
+ # Sets a hook that fires once per tool-call round, after all tool
135
+ # results are committed to chat history and before the next LLM call
136
+ #
137
+ # Skipped when a tool returns {Tool::Halt} — pending work stays
138
+ # queued for the next {#ask} call.
139
+ #
140
+ # @yield Block called after tool results are added to history
141
+ # @return [self] for chaining
142
+ def after_tool_calls(&block)
143
+ @after_tool_calls_hook = block
144
+ self
145
+ end
146
+
133
147
  # Clears all callbacks for the specified event, or all events if none specified
134
148
  def clear_callbacks(event = nil)
135
149
  @callback_monitor.synchronize do
@@ -153,30 +167,36 @@ module RubyLLM
153
167
  end
154
168
  end
155
169
 
156
- # Override complete to use emit() and support around_llm_request hook
157
- # Follows fork pattern: tool call handling wraps message addition
170
+ # Override complete to use emit() and support around_llm_request hook.
171
+ # Uses a trampoline loop instead of mutual recursion with handle_tool_calls
172
+ # to avoid stack growth during multi-round tool-call conversations.
158
173
  def complete(&block)
159
- # Execute LLM request (potentially wrapped by around_llm_request hook)
160
- response = execute_llm_request(&block)
174
+ loop do
175
+ response = execute_llm_request(&block)
161
176
 
162
- emit(:new_message) unless block_given?
177
+ emit(:new_message) unless block_given?
163
178
 
164
- if @schema && response.content.is_a?(String)
165
- begin
166
- response.content = JSON.parse(response.content)
167
- rescue JSON::ParserError
168
- # If parsing fails, keep content as string
179
+ if @schema && response.content.is_a?(String)
180
+ begin
181
+ response.content = JSON.parse(response.content)
182
+ rescue JSON::ParserError
183
+ # If parsing fails, keep content as string
184
+ end
169
185
  end
170
- end
171
186
 
172
- add_message(response)
173
- emit(:end_message, response)
174
- if response.tool_call?
175
- # For tool calls: add message, emit end_message, then handle tools
176
- handle_tool_calls(response, &block)
177
- else
178
- # For final responses: add message and emit end_message
179
- response
187
+ add_message(response)
188
+ emit(:end_message, response)
189
+
190
+ if response.tool_call?
191
+ halt_result = handle_tool_calls(response, &block)
192
+ return halt_result if halt_result
193
+
194
+ @after_tool_calls_hook&.call
195
+
196
+ # Loop continues: next LLM call with zero stack growth
197
+ else
198
+ return response
199
+ end
180
200
  end
181
201
  end
182
202
 
@@ -238,8 +258,9 @@ module RubyLLM
238
258
  end
239
259
  end
240
260
 
241
- # Override handle_tool_calls to use emit and support around_tool_execution hook
242
- def handle_tool_calls(response, &block)
261
+ # Execute tool calls and return halt result (or nil to continue the loop).
262
+ # Does NOT recurse back into complete() — the trampoline loop handles that.
263
+ def handle_tool_calls(response, &_block)
243
264
  halt_result = nil
244
265
 
245
266
  response.tool_calls.each_value do |tool_call|
@@ -259,7 +280,7 @@ module RubyLLM
259
280
  halt_result = result if result.is_a?(Tool::Halt)
260
281
  end
261
282
 
262
- halt_result || complete(&block)
283
+ halt_result
263
284
  end
264
285
 
265
286
  # Execute tool with around_tool_execution hook if set
@@ -39,3 +39,9 @@ require_relative "message_management_patch"
39
39
 
40
40
  # 7. Responses API patch (depends on configuration, uses error classes)
41
41
  require_relative "responses_api_patch"
42
+
43
+ # 8. MCP SSL patch (configures SSL for HTTPX connections in ruby_llm-mcp)
44
+ # Only load if RubyLLM::MCP is available
45
+ if defined?(RubyLLM::MCP)
46
+ require_relative "mcp_ssl_patch"
47
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Patches ruby_llm-mcp HTTPX connections to configure SSL verification
4
+ #
5
+ # OpenSSL 3.6 enforces CRL (Certificate Revocation List) checking by default.
6
+ # Most certificates don't provide accessible CRL endpoints (industry moved to OCSP),
7
+ # which breaks HTTPS MCP connections with:
8
+ # "certificate verify failed (unable to get certificate CRL)"
9
+ #
10
+ # This patch injects SSL options into all 5 HTTPX connection creation paths:
11
+ #
12
+ # Via HTTPClient.build_connection (paths 1-3):
13
+ # 1. StreamableHTTP#create_connection
14
+ # 2. StreamableHTTP#create_connection_with_streaming_callbacks
15
+ # 3. SSE#send_request
16
+ #
17
+ # Direct HTTPX.plugin calls (paths 4-5):
18
+ # 4. StreamableHTTP#create_connection_with_sse_callbacks
19
+ # 5. SSE#create_sse_client
20
+ #
21
+ # Default: VERIFY_PEER (validates cert chain without CRL checking)
22
+ # Configurable: VERIFY_NONE for local development via SwarmSDK.config.mcp_ssl_verify
23
+
24
+ require "openssl"
25
+
26
+ module SwarmSDK
27
+ # Module-level SSL configuration for MCP HTTPX connections
28
+ #
29
+ # Set ssl_options before creating MCP clients. The patched HTTPX methods
30
+ # read from this accessor to configure SSL on every connection.
31
+ #
32
+ # @example Default (validates cert chain, skips CRL)
33
+ # McpSslPatch.ssl_options #=> { verify_mode: OpenSSL::SSL::VERIFY_PEER }
34
+ #
35
+ # @example Disable SSL verification (local dev only)
36
+ # McpSslPatch.ssl_options = { verify_mode: OpenSSL::SSL::VERIFY_NONE }
37
+ module McpSslPatch
38
+ @ssl_options = { verify_mode: OpenSSL::SSL::VERIFY_PEER }
39
+
40
+ class << self
41
+ # @return [Hash] SSL options hash passed to HTTPX .with(ssl: ...)
42
+ attr_accessor :ssl_options
43
+
44
+ # Clear the thread-local HTTPX connection cache
45
+ #
46
+ # Must be called after changing ssl_options so that HTTPClient.build_connection
47
+ # runs again with the updated options.
48
+ #
49
+ # @return [void]
50
+ def reset_connection!
51
+ Thread.current[:ruby_llm_mcp_client_connection] = nil
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # Patch 1: HTTPClient.build_connection
58
+ #
59
+ # Covers paths 1-3: StreamableHTTP#create_connection,
60
+ # StreamableHTTP#create_connection_with_streaming_callbacks, SSE#send_request
61
+ #
62
+ # These all chain from HTTPClient.connection which calls build_connection.
63
+ module RubyLLM
64
+ module MCP
65
+ module Transports
66
+ module Support
67
+ class HTTPClient
68
+ class << self
69
+ def build_connection
70
+ HTTPX.with(
71
+ pool_options: {
72
+ max_connections: RubyLLM::MCP.config.max_connections,
73
+ pool_timeout: RubyLLM::MCP.config.pool_timeout,
74
+ },
75
+ ssl: SwarmSDK::McpSslPatch.ssl_options,
76
+ )
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # Patch 2: StreamableHTTP#create_connection_with_sse_callbacks
86
+ #
87
+ # This method calls HTTPX.plugin(:callbacks) directly, bypassing HTTPClient.
88
+ # Merges SSL options with ALPN protocol when version is :http1.
89
+ module SwarmSDK
90
+ module McpSslPatch
91
+ module StreamableHttpSslPatch
92
+ private
93
+
94
+ def create_connection_with_sse_callbacks(options, headers)
95
+ client = HTTPX.plugin(:callbacks)
96
+ client = add_on_response_body_chunk_callback(client, options)
97
+
98
+ ssl = SwarmSDK::McpSslPatch.ssl_options.dup
99
+ ssl[:alpn_protocols] = ["http/1.1"] if @version == :http1
100
+
101
+ client = client.with(
102
+ timeout: {
103
+ connect_timeout: 10,
104
+ read_timeout: @request_timeout / 1000,
105
+ write_timeout: @request_timeout / 1000,
106
+ operation_timeout: @request_timeout / 1000,
107
+ },
108
+ headers: headers,
109
+ ssl: ssl,
110
+ )
111
+
112
+ register_client(client)
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ # Patch 3: SSE#create_sse_client
119
+ #
120
+ # This method calls HTTPX.plugin(:stream) directly, bypassing HTTPClient.
121
+ # Merges SSL options with ALPN protocol when version is :http1.
122
+ module SwarmSDK
123
+ module McpSslPatch
124
+ module SseSslPatch
125
+ private
126
+
127
+ def create_sse_client
128
+ stream_headers = build_request_headers
129
+
130
+ ssl = SwarmSDK::McpSslPatch.ssl_options.dup
131
+ ssl[:alpn_protocols] = ["http/1.1"] if @version == :http1
132
+
133
+ HTTPX.plugin(:stream).with(
134
+ headers: stream_headers,
135
+ ssl: ssl,
136
+ )
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Apply prepend patches for direct HTTPX.plugin calls
143
+ RubyLLM::MCP::Transports::StreamableHTTP.prepend(SwarmSDK::McpSslPatch::StreamableHttpSslPatch)
144
+ RubyLLM::MCP::Transports::SSE.prepend(SwarmSDK::McpSslPatch::SseSslPatch)
@@ -149,14 +149,13 @@ module RubyLLM
149
149
 
150
150
  private
151
151
 
152
- # Override handle_tool_calls to support concurrent execution
153
- # This method is called when tool_concurrency is set
152
+ # Override handle_tool_calls to support concurrent execution.
153
+ # Returns halt result or nil the trampoline loop in complete() handles the next iteration.
154
154
  def handle_tool_calls(response, &block)
155
155
  return super unless @tool_concurrency
156
156
 
157
157
  tool_calls = response.tool_calls
158
- halt_result = execute_tools_concurrently(tool_calls)
159
- halt_result || complete(&block)
158
+ execute_tools_concurrently(tool_calls)
160
159
  end
161
160
 
162
161
  def execute_tools_concurrently(tool_calls)