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
@@ -0,0 +1,490 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ # Global configuration for V3 agents
6
+ #
7
+ # Provides sensible defaults that can be overridden per-agent
8
+ # via AgentDefinition attributes. Also forwards provider API keys
9
+ # to RubyLLM when {#apply_provider_config!} is called.
10
+ #
11
+ # @example Configure defaults
12
+ # SwarmSDK::V3.configure do |config|
13
+ # config.default_model = "claude-sonnet-4"
14
+ # config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
15
+ # end
16
+ #
17
+ # @example Reset to defaults
18
+ # SwarmSDK::V3.reset_configuration!
19
+ class Configuration
20
+ # --- LLM defaults ---
21
+
22
+ # @return [String] Default LLM model for agents without explicit model
23
+ attr_accessor :default_model
24
+
25
+ # --- Provider API keys and endpoints ---
26
+
27
+ # @return [String, nil] OpenAI API key
28
+ attr_accessor :openai_api_key
29
+
30
+ # @return [String, nil] OpenAI custom API base URL
31
+ attr_accessor :openai_api_base
32
+
33
+ # @return [Boolean] Use "system" role instead of "developer" for OpenAI.
34
+ # OpenAI's API uses "developer" by default, but some OpenAI-compatible
35
+ # servers still require the traditional "system" role.
36
+ attr_accessor :openai_use_system_role
37
+
38
+ # @return [String, nil] Anthropic API key
39
+ attr_accessor :anthropic_api_key
40
+
41
+ # @return [String, nil] Anthropic custom API base URL
42
+ attr_accessor :anthropic_api_base
43
+
44
+ # @return [String, nil] Google Gemini API key
45
+ attr_accessor :gemini_api_key
46
+
47
+ # @return [String, nil] Gemini custom API base URL
48
+ attr_accessor :gemini_api_base
49
+
50
+ # @return [String, nil] AWS Bedrock access key
51
+ attr_accessor :bedrock_api_key
52
+
53
+ # @return [String, nil] AWS Bedrock secret key
54
+ attr_accessor :bedrock_secret_key
55
+
56
+ # @return [String, nil] AWS Bedrock region
57
+ attr_accessor :bedrock_region
58
+
59
+ # @return [String, nil] AWS Bedrock session token
60
+ attr_accessor :bedrock_session_token
61
+
62
+ # --- Background LLM configuration ---
63
+
64
+ # @return [String, nil] Model for background tasks (compression, summarization)
65
+ attr_accessor :background_model
66
+
67
+ # @return [String, nil] Provider for background model
68
+ attr_accessor :background_provider
69
+
70
+ # @return [String, nil] Custom API base URL for background model
71
+ attr_accessor :background_base_url
72
+
73
+ # @return [Hash] Custom HTTP headers for background model
74
+ attr_accessor :background_headers
75
+
76
+ # @return [Hash] Raw API body parameters for background model (temperature, etc.)
77
+ attr_accessor :background_parameters
78
+
79
+ # --- Subtask LLM configuration ---
80
+
81
+ # @return [String, nil] Model for subtask agents (nil = inherit from parent)
82
+ attr_accessor :subtask_model
83
+
84
+ # @return [String, nil] Provider for subtask model (nil = inherit from parent)
85
+ attr_accessor :subtask_provider
86
+
87
+ # @return [String, nil] Custom API base URL for subtask model
88
+ attr_accessor :subtask_base_url
89
+
90
+ # @return [Hash] Custom HTTP headers for subtask model
91
+ attr_accessor :subtask_headers
92
+
93
+ # @return [Hash] Raw API body parameters for subtask model (temperature, etc.)
94
+ attr_accessor :subtask_parameters
95
+
96
+ # --- Tool configuration ---
97
+
98
+ # @return [Array<Symbol>] Tools added to every agent (defaults to [])
99
+ attr_accessor :default_tools
100
+
101
+ # @return [Array<Symbol>, nil] Available tools (nil = all built-in tools)
102
+ attr_accessor :registered_tools
103
+
104
+ # @return [Integer, nil] Default max concurrent tool executions per agent.
105
+ # When set, agents execute multiple tool calls from a single LLM response
106
+ # concurrently using Async. nil disables concurrency (sequential execution).
107
+ attr_accessor :default_max_concurrent_tools
108
+
109
+ # --- Embedding configuration ---
110
+
111
+ # @return [String] Sentence-transformer model name for embeddings
112
+ attr_accessor :embedding_model
113
+
114
+ # @return [String, nil] Cache directory for downloaded embedding models
115
+ attr_accessor :embedding_cache_dir
116
+
117
+ # --- Memory defaults ---
118
+
119
+ # @return [Integer] Default number of recent turns to keep in short-term memory
120
+ attr_accessor :default_stm_turns
121
+
122
+ # @return [Integer] Default number of memory cards to retrieve per turn
123
+ attr_accessor :default_retrieval_top_k
124
+
125
+ # @return [Float] Default semantic weight for hybrid search (0.0-1.0)
126
+ attr_accessor :default_semantic_weight
127
+
128
+ # @return [Float] Default keyword weight for hybrid search (0.0-1.0)
129
+ attr_accessor :default_keyword_weight
130
+
131
+ # --- Exposure score weights ---
132
+
133
+ # @return [Float] Weight for frequency component in exposure score (α)
134
+ attr_accessor :exposure_frequency_weight
135
+
136
+ # @return [Float] Weight for recency component in exposure score (β)
137
+ attr_accessor :exposure_recency_weight
138
+
139
+ # @return [Float] Weight for dwell component in exposure score (γ)
140
+ attr_accessor :exposure_dwell_weight
141
+
142
+ # @return [Integer] Half-life for recency decay in seconds (default: 7 days)
143
+ attr_accessor :exposure_recency_half_life
144
+
145
+ # --- Memory lifecycle tuning ---
146
+ #
147
+ # These parameters control how often the agent runs background memory
148
+ # maintenance tasks (compression, consolidation, promotion) during
149
+ # conversation. Lower intervals mean more frequent maintenance.
150
+
151
+ # How often the agent compresses low-exposure memory cards.
152
+ # Compression advances cards through the L0→L4 ladder, reducing
153
+ # storage and context window usage. Lower values compress more
154
+ # aggressively (every 3 turns vs every 10).
155
+ #
156
+ # @return [Integer] Run compression every N turns (default: 5)
157
+ attr_accessor :compression_interval
158
+
159
+ # How often the agent runs consolidation (dedup, cluster updates,
160
+ # conflict detection). Consolidation merges near-duplicate cards,
161
+ # updates cluster rolling summaries, and creates `contradicts`
162
+ # edges between conflicting cards.
163
+ #
164
+ # @return [Integer] Run consolidation every N turns (default: 10)
165
+ attr_accessor :consolidation_interval
166
+
167
+ # Minimum access count before a compressed card (L1-L4) is promoted
168
+ # back toward L0. Promotion rebuilds richer text from graph neighbors
169
+ # and LLM, counterbalancing compression for frequently-accessed cards.
170
+ #
171
+ # @return [Integer] Min access count for promotion (default: 5)
172
+ attr_accessor :promotion_access_threshold
173
+
174
+ # --- Retrieval tuning ---
175
+ #
176
+ # These parameters control hybrid search: how semantic embeddings and
177
+ # BM25 keyword scores are combined, and how graph expansion works.
178
+
179
+ # Reciprocal Rank Fusion (RRF) constant. Controls how semantic and
180
+ # keyword search ranks are combined. Higher values flatten rank
181
+ # differences, giving later results more weight relative to top hits.
182
+ # Standard value from the RRF paper is 60.
183
+ #
184
+ # @return [Integer] RRF constant k (default: 60)
185
+ attr_accessor :rrf_k
186
+
187
+ # BM25 term frequency saturation parameter (k1). Controls how quickly
188
+ # repeated terms in a document contribute diminishing returns.
189
+ # Higher values give more weight to repeated terms. Standard
190
+ # Okapi BM25 default is 1.2.
191
+ #
192
+ # @return [Float] BM25 k1 parameter (default: 1.2)
193
+ attr_accessor :bm25_k1
194
+
195
+ # BM25 document length normalization parameter (b). Controls how
196
+ # much longer documents are penalized relative to average length.
197
+ # 0.0 = no penalty (all docs treated equally), 1.0 = full penalty.
198
+ # Standard Okapi BM25 default is 0.75.
199
+ #
200
+ # @return [Float] BM25 b parameter (default: 0.75)
201
+ attr_accessor :bm25_b
202
+
203
+ # Extra score boost per entity match in keyword search. When a
204
+ # query term appears in a card's entity list (not just its text),
205
+ # this boost is added to the BM25 score. Helps surface cards about
206
+ # specific named entities like "JWT", "PostgreSQL", etc.
207
+ #
208
+ # @return [Float] Entity match boost per match (default: 0.5)
209
+ attr_accessor :bm25_entity_boost
210
+
211
+ # Maximum 1-hop graph neighbors to expand per seed card during
212
+ # retrieval. After finding the top-k cards, the retriever traverses
213
+ # graph edges to pull in related cards. This caps how many neighbors
214
+ # any single card can contribute, preventing highly-connected cards
215
+ # from dominating results.
216
+ #
217
+ # @return [Integer] Max neighbors per seed (default: 4)
218
+ attr_accessor :max_neighbors_per_seed
219
+
220
+ # --- Context builder tuning ---
221
+ #
222
+ # These parameters control how the working context is assembled from
223
+ # memory tiers: retrieved cards, exploration sprinkle, and dedup.
224
+
225
+ # Number of low-exposure "anti-forgetting" cards sprinkled into each
226
+ # context build. These are rarely-accessed cards with some relevance
227
+ # to the query, included to prevent permanent forgetting. Set to 0
228
+ # to disable exploration entirely.
229
+ #
230
+ # @return [Integer] Exploration sprinkle size (default: 2)
231
+ attr_accessor :exploration_sample_size
232
+
233
+ # Minimum embedding similarity between the query and an exploration
234
+ # candidate card. Lower values include more diverse (less relevant)
235
+ # cards in the sprinkle. Higher values restrict to cards that are
236
+ # at least loosely relevant.
237
+ #
238
+ # @return [Float] Min similarity for exploration (default: 0.15)
239
+ attr_accessor :exploration_min_similarity
240
+
241
+ # Similarity threshold above which cards are considered near-duplicates
242
+ # during context assembly. When two cards in the working context have
243
+ # similarity above this threshold, the duplicate is removed. Uses the
244
+ # adapter's {Adapters::Base#similarity} method.
245
+ #
246
+ # @return [Float] Dedup threshold (default: 0.92)
247
+ attr_accessor :dedup_similarity_threshold
248
+
249
+ # @return [Boolean] Default associative memory setting for agents.
250
+ # When enabled, exploration cards are labeled distinctly and guidance
251
+ # is injected encouraging the LLM to naturally surface tangential memories.
252
+ attr_accessor :default_associative_memory
253
+
254
+ # --- Ingestion tuning ---
255
+ #
256
+ # These parameters control how new conversation turns are processed
257
+ # into memory cards, entities, and graph edges.
258
+
259
+ # Minimum embedding similarity for creating `same_entity` edges
260
+ # between new cards and existing cards during cross-turn entity
261
+ # linking. Lower values create more connections but may produce
262
+ # false positives.
263
+ #
264
+ # @return [Float] Min similarity for entity edges (default: 0.3)
265
+ attr_accessor :entity_edge_min_similarity
266
+
267
+ # Maximum number of existing cards to scan when creating cross-turn
268
+ # entity edges. Limits the O(n*m) scan to prevent slowdowns as the
269
+ # memory store grows. Scans the most recent cards first.
270
+ #
271
+ # @return [Integer] Cross-turn edge scan limit (default: 50)
272
+ attr_accessor :cross_turn_edge_scan_limit
273
+
274
+ # --- Consolidation tuning ---
275
+ #
276
+ # These parameters control periodic maintenance: dedup, merging,
277
+ # cluster updates, and conflict detection.
278
+
279
+ # Similarity threshold for detecting duplicate cards during
280
+ # consolidation. Cards with vector similarity above this threshold
281
+ # are merged into a canonical card. Lower values merge more
282
+ # aggressively but risk merging distinct-but-similar concepts.
283
+ #
284
+ # @return [Float] Consolidator dedup threshold (default: 0.92)
285
+ attr_accessor :consolidator_dedup_threshold
286
+
287
+ # Minimum similarity for considering two cards as potential conflicts.
288
+ # The conflict band is: conflict_threshold <= similarity < dedup_threshold.
289
+ # Raise to reduce false positives; lower to catch subtler contradictions.
290
+ #
291
+ # @return [Float] Min similarity for conflict detection (default: 0.75)
292
+ attr_accessor :consolidator_conflict_threshold
293
+
294
+ # --- MCP settings ---
295
+
296
+ # @return [Boolean] Global SSL verification for MCP HTTP connections (default: true).
297
+ # OpenSSL 3.6 enforces CRL checking by default, which breaks most HTTPS MCP
298
+ # endpoints. Set to false to disable verification for local development.
299
+ # Can be overridden per-server via ServerDefinition's ssl_verify option.
300
+ attr_accessor :mcp_ssl_verify
301
+
302
+ # --- Subtask limits ---
303
+
304
+ # @return [Integer] Maximum nesting depth for SubTask spawning (default: 1)
305
+ attr_accessor :max_subtask_depth
306
+
307
+ # --- Tool limits ---
308
+
309
+ # @return [Integer] Maximum output character limit for tool results
310
+ attr_accessor :output_character_limit
311
+
312
+ # @return [Integer] Maximum line character limit for Read tool
313
+ attr_accessor :line_character_limit
314
+
315
+ # @return [Integer] Default line limit for Read tool
316
+ attr_accessor :read_line_limit
317
+
318
+ # @return [Integer] Maximum results from Glob tool
319
+ attr_accessor :glob_result_limit
320
+
321
+ # @return [Integer] Default Bash command timeout in milliseconds
322
+ attr_accessor :bash_command_timeout
323
+
324
+ # @return [Integer] Maximum Bash command timeout in milliseconds
325
+ attr_accessor :bash_command_max_timeout
326
+
327
+ def initialize
328
+ reset!
329
+ end
330
+
331
+ # Reset all configuration to defaults
332
+ #
333
+ # @return [void]
334
+ def reset!
335
+ # LLM defaults
336
+ @default_model = "claude-sonnet-4"
337
+
338
+ # Provider keys
339
+ @openai_api_key = nil
340
+ @openai_api_base = nil
341
+ @openai_use_system_role = false
342
+ @anthropic_api_key = nil
343
+ @anthropic_api_base = nil
344
+ @gemini_api_key = nil
345
+ @gemini_api_base = nil
346
+ @bedrock_api_key = nil
347
+ @bedrock_secret_key = nil
348
+ @bedrock_region = nil
349
+ @bedrock_session_token = nil
350
+
351
+ # Background LLM
352
+ @background_model = nil
353
+ @background_provider = nil
354
+ @background_base_url = nil
355
+ @background_headers = {}
356
+ @background_parameters = {}
357
+
358
+ # Subtask LLM
359
+ @subtask_model = nil
360
+ @subtask_provider = nil
361
+ @subtask_base_url = nil
362
+ @subtask_headers = {}
363
+ @subtask_parameters = {}
364
+
365
+ # Tools
366
+ @default_tools = []
367
+ @registered_tools = nil
368
+ @default_max_concurrent_tools = nil
369
+
370
+ # Embedding
371
+ @embedding_model = "sentence-transformers/multi-qa-MiniLM-L6-cos-v1"
372
+ @embedding_cache_dir = nil
373
+
374
+ # Memory retrieval (optimized via manual-memory-evals.md Phases 1-2)
375
+ @default_stm_turns = 8
376
+ @default_retrieval_top_k = 10 # was 15 — less noise, better precision
377
+ @default_semantic_weight = 0.4 # was 0.5 — slightly keyword-leaning for entity recall
378
+ @default_keyword_weight = 0.6 # was 0.5 — better for named entities and numbers
379
+
380
+ # Exposure score weights (additive: α·frequency + β·recency + γ·dwell)
381
+ @exposure_frequency_weight = 0.4
382
+ @exposure_recency_weight = 0.4
383
+ @exposure_dwell_weight = 0.2
384
+ @exposure_recency_half_life = 7 * 24 * 3600 # 7 days in seconds
385
+
386
+ # Memory lifecycle
387
+ @compression_interval = 5
388
+ @consolidation_interval = 10
389
+ @promotion_access_threshold = 5
390
+
391
+ # Retrieval (optimized via manual-memory-evals.md Phases 3-4)
392
+ @rrf_k = 120 # was 60 — flatter ranking gives more diverse results
393
+ @bm25_k1 = 1.2
394
+ @bm25_b = 1.0 # was 0.75 — full length normalization for short memory cards
395
+ @bm25_entity_boost = 0.5
396
+ @max_neighbors_per_seed = 4
397
+
398
+ # Context builder
399
+ @exploration_sample_size = 2
400
+ @exploration_min_similarity = 0.15
401
+ @dedup_similarity_threshold = 0.92
402
+ @default_associative_memory = false
403
+
404
+ # Ingestion
405
+ @entity_edge_min_similarity = 0.3
406
+ @cross_turn_edge_scan_limit = 50
407
+
408
+ # Consolidation
409
+ @consolidator_dedup_threshold = 0.92
410
+ @consolidator_conflict_threshold = 0.75
411
+
412
+ # MCP
413
+ @mcp_ssl_verify = true
414
+
415
+ # Subtask limits
416
+ @max_subtask_depth = 1
417
+
418
+ # Tool limits
419
+ @output_character_limit = 30_000
420
+ @line_character_limit = 2_000
421
+ @read_line_limit = 2_000
422
+ @glob_result_limit = 500
423
+ @bash_command_timeout = 120_000
424
+ @bash_command_max_timeout = 600_000
425
+ end
426
+
427
+ # Forward provider API keys and settings to RubyLLM
428
+ #
429
+ # Call this after setting provider keys to configure RubyLLM.
430
+ # Only sets values that are non-nil.
431
+ #
432
+ # @return [void]
433
+ #
434
+ # @example
435
+ # config = SwarmSDK::V3::Configuration.instance
436
+ # config.anthropic_api_key = "sk-ant-..."
437
+ # config.apply_provider_config!
438
+ def apply_provider_config!
439
+ RubyLLM.configure do |c|
440
+ c.openai_api_key = @openai_api_key if @openai_api_key
441
+ c.openai_api_base = @openai_api_base if @openai_api_base
442
+ c.openai_use_system_role = @openai_use_system_role
443
+ c.anthropic_api_key = @anthropic_api_key if @anthropic_api_key
444
+ c.anthropic_api_base = @anthropic_api_base if @anthropic_api_base
445
+ c.gemini_api_key = @gemini_api_key if @gemini_api_key
446
+ c.gemini_api_base = @gemini_api_base if @gemini_api_base
447
+ c.bedrock_api_key = @bedrock_api_key if @bedrock_api_key
448
+ c.bedrock_secret_key = @bedrock_secret_key if @bedrock_secret_key
449
+ c.bedrock_region = @bedrock_region if @bedrock_region
450
+ c.bedrock_session_token = @bedrock_session_token if @bedrock_session_token
451
+ end
452
+ end
453
+
454
+ class << self
455
+ # Get the global configuration instance
456
+ #
457
+ # @return [Configuration]
458
+ def instance
459
+ @instance ||= new
460
+ end
461
+
462
+ # Configure global settings
463
+ #
464
+ # Yields the configuration instance, then applies provider settings
465
+ # to RubyLLM automatically.
466
+ #
467
+ # @yield [Configuration] The configuration instance
468
+ # @return [Configuration]
469
+ #
470
+ # @example
471
+ # SwarmSDK::V3::Configuration.configure do |config|
472
+ # config.default_model = "claude-sonnet-4"
473
+ # config.anthropic_api_key = "sk-ant-..."
474
+ # end
475
+ def configure
476
+ yield(instance) if block_given?
477
+ instance.apply_provider_config!
478
+ instance
479
+ end
480
+
481
+ # Reset configuration to defaults
482
+ #
483
+ # @return [void]
484
+ def reset!
485
+ instance.reset!
486
+ end
487
+ end
488
+ end
489
+ end
490
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ # Lightweight debug logging for V3 agents
6
+ #
7
+ # Activated by setting the `DEBUG` or `SWARM_DEBUG` environment variable.
8
+ # Outputs timestamped debug messages to stderr with timing information
9
+ # for performance profiling.
10
+ #
11
+ # @example Enable debug logging
12
+ # DEBUG=1 bundle exec ruby my_script.rb
13
+ #
14
+ # @example Use in code
15
+ # DebugLog.log("retriever", "semantic search found 15 cards")
16
+ # DebugLog.time("retriever", "keyword_search") { keyword_search(query) }
17
+ module DebugLog
18
+ class << self
19
+ # Whether debug logging is enabled
20
+ #
21
+ # @return [Boolean]
22
+ def enabled?
23
+ return @enabled if defined?(@enabled)
24
+
25
+ @enabled = ENV["DEBUG"] == "1" || ENV["SWARM_DEBUG"] == "1"
26
+ end
27
+
28
+ # Log a debug message to stderr
29
+ #
30
+ # @param component [String] Component name (e.g., "retriever", "ingestion")
31
+ # @param message [String] Debug message
32
+ # @return [void]
33
+ #
34
+ # @example
35
+ # DebugLog.log("agent", "initializing memory store")
36
+ def log(component, message)
37
+ return unless enabled?
38
+
39
+ timestamp = Time.now.strftime("%H:%M:%S.%L")
40
+ warn("[DEBUG #{timestamp}] [#{component}] #{message}")
41
+ end
42
+
43
+ # Execute a block and log its execution time
44
+ #
45
+ # @param component [String] Component name
46
+ # @param label [String] Operation label
47
+ # @yield Block to time
48
+ # @return [Object] Block return value
49
+ #
50
+ # @example
51
+ # result = DebugLog.time("retriever", "semantic_search") do
52
+ # adapter.vector_search(embedding, top_k: 30)
53
+ # end
54
+ def time(component, label)
55
+ unless enabled?
56
+ return yield
57
+ end
58
+
59
+ log(component, "#{label} START")
60
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
61
+ result = yield
62
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
63
+ log(component, "#{label} DONE (#{format_duration(elapsed)})")
64
+
65
+ result
66
+ end
67
+
68
+ private
69
+
70
+ # Format duration in human-readable form
71
+ #
72
+ # @param seconds [Float] Duration in seconds
73
+ # @return [String] Formatted duration
74
+ def format_duration(seconds)
75
+ if seconds < 0.001
76
+ "#{(seconds * 1_000_000).round}us"
77
+ elsif seconds < 1.0
78
+ "#{(seconds * 1_000).round(1)}ms"
79
+ else
80
+ "#{seconds.round(2)}s"
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ # EventStream provides fiber-local event emission for V3 agents
6
+ #
7
+ # Thread-safe and fiber-safe event system using Fiber storage.
8
+ # Each fiber gets its own emitter instance, preventing cross-fiber contamination.
9
+ #
10
+ # Two emitter slots are available:
11
+ # - **Global emitter** (via `emitter=`): For logging/monitoring across all operations
12
+ # - **Block emitter** (via `block_emitter=`): For per-call event handling in `ask()` blocks
13
+ #
14
+ # @example Emit an event
15
+ # EventStream.emit(type: "agent_start", agent: :backend, model: "claude-sonnet-4")
16
+ #
17
+ # @example Subscribe to events globally
18
+ # EventStream.emitter = ->(event) { puts event.inspect }
19
+ # agent.ask("Hello")
20
+ # EventStream.reset!
21
+ #
22
+ # @example Receive events via block (set internally by Agent#ask)
23
+ # agent.ask("Hello") do |event|
24
+ # case event[:type]
25
+ # when "content_chunk"
26
+ # print event[:content]
27
+ # end
28
+ # end
29
+ module EventStream
30
+ FIBER_KEY = :v3_event_stream_emitter
31
+ BLOCK_KEY = :v3_event_stream_block_emitter
32
+
33
+ class << self
34
+ # Emit a structured event
35
+ #
36
+ # Adds timestamp and forwards to both the global emitter and block emitter.
37
+ # No-op if no emitters are configured.
38
+ #
39
+ # @param data [Hash] Event data (type, agent, and event-specific fields)
40
+ # @return [void]
41
+ #
42
+ # @example
43
+ # EventStream.emit(
44
+ # type: "tool_call",
45
+ # agent: :backend,
46
+ # tool: "Read",
47
+ # arguments: { file_path: "src/main.rb" },
48
+ # )
49
+ def emit(**data)
50
+ global = Fiber[FIBER_KEY]
51
+ block = Fiber[BLOCK_KEY]
52
+ return if global.nil? && block.nil?
53
+
54
+ entry = data.merge(timestamp: Time.now.utc.iso8601(6)).compact
55
+
56
+ call_emitter(global, entry)
57
+ call_emitter(block, entry)
58
+ end
59
+
60
+ # Set the global emitter for the current fiber
61
+ #
62
+ # Use this for logging, monitoring, and analytics that should
63
+ # receive all events regardless of where they originate.
64
+ #
65
+ # @param callable [#call] Object responding to call(Hash)
66
+ # @return [void]
67
+ def emitter=(callable)
68
+ Fiber[FIBER_KEY] = callable
69
+ end
70
+
71
+ # Get the current global emitter
72
+ #
73
+ # @return [#call, nil] Current emitter or nil
74
+ def emitter
75
+ Fiber[FIBER_KEY]
76
+ end
77
+
78
+ # Set the block emitter for the current fiber
79
+ #
80
+ # Used internally by Agent#ask to wire the user's block to receive events.
81
+ # Not typically called directly by SDK users.
82
+ #
83
+ # @param callable [#call, nil] Object responding to call(Hash), or nil to clear
84
+ # @return [void]
85
+ def block_emitter=(callable)
86
+ Fiber[BLOCK_KEY] = callable
87
+ end
88
+
89
+ # Get the current block emitter
90
+ #
91
+ # @return [#call, nil] Current block emitter or nil
92
+ def block_emitter
93
+ Fiber[BLOCK_KEY]
94
+ end
95
+
96
+ # Reset both emitters for the current fiber
97
+ #
98
+ # @return [void]
99
+ def reset!
100
+ Fiber[FIBER_KEY] = nil
101
+ Fiber[BLOCK_KEY] = nil
102
+ end
103
+
104
+ # Check if any emitter is configured
105
+ #
106
+ # @return [Boolean]
107
+ def enabled?
108
+ !Fiber[FIBER_KEY].nil? || !Fiber[BLOCK_KEY].nil?
109
+ end
110
+
111
+ private
112
+
113
+ # Call an emitter with error handling
114
+ #
115
+ # @param emitter [#call, nil] Emitter to call
116
+ # @param entry [Hash] Event data to pass
117
+ # @return [void]
118
+ def call_emitter(emitter, entry)
119
+ return unless emitter
120
+
121
+ emitter.call(entry)
122
+ rescue StandardError => e
123
+ # Never let event emission break agent execution, but log to stderr
124
+ # so failures are visible during debugging rather than silently lost.
125
+ warn("[SwarmSDK::V3::EventStream] Emitter error: #{e.class}: #{e.message}")
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end