swarm_sdk 2.7.14 → 3.0.0.alpha2

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