swarm_sdk 2.7.14 → 3.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  42. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  43. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  44. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  45. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  46. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  47. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  48. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  49. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  50. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  51. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  52. data/lib/swarm_sdk/v3.rb +145 -0
  53. metadata +83 -148
  54. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  55. data/lib/swarm_sdk/agent/builder.rb +0 -705
  56. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  57. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  58. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  59. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  60. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  61. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  62. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  63. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  64. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  65. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  66. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  67. data/lib/swarm_sdk/agent/context.rb +0 -115
  68. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  69. data/lib/swarm_sdk/agent/definition.rb +0 -588
  70. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  71. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  72. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  73. data/lib/swarm_sdk/agent_registry.rb +0 -146
  74. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  75. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  76. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  77. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  78. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  79. data/lib/swarm_sdk/config.rb +0 -368
  80. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  81. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  82. data/lib/swarm_sdk/configuration.rb +0 -165
  83. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  84. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  85. data/lib/swarm_sdk/context_compactor.rb +0 -335
  86. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  87. data/lib/swarm_sdk/context_management/context.rb +0 -328
  88. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  89. data/lib/swarm_sdk/defaults.rb +0 -251
  90. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  91. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  92. data/lib/swarm_sdk/hooks/context.rb +0 -197
  93. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  94. data/lib/swarm_sdk/hooks/error.rb +0 -29
  95. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  96. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  97. data/lib/swarm_sdk/hooks/result.rb +0 -150
  98. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  99. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  100. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  101. data/lib/swarm_sdk/log_collector.rb +0 -227
  102. data/lib/swarm_sdk/log_stream.rb +0 -127
  103. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  104. data/lib/swarm_sdk/model_aliases.json +0 -8
  105. data/lib/swarm_sdk/models.json +0 -44002
  106. data/lib/swarm_sdk/models.rb +0 -161
  107. data/lib/swarm_sdk/node_context.rb +0 -245
  108. data/lib/swarm_sdk/observer/builder.rb +0 -81
  109. data/lib/swarm_sdk/observer/config.rb +0 -45
  110. data/lib/swarm_sdk/observer/manager.rb +0 -248
  111. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  112. data/lib/swarm_sdk/permissions/config.rb +0 -239
  113. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  114. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  115. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  116. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  117. data/lib/swarm_sdk/plugin.rb +0 -309
  118. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  119. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  120. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  121. data/lib/swarm_sdk/restore_result.rb +0 -65
  122. data/lib/swarm_sdk/result.rb +0 -241
  123. data/lib/swarm_sdk/snapshot.rb +0 -156
  124. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  125. data/lib/swarm_sdk/state_restorer.rb +0 -476
  126. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  127. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  128. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  129. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  130. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  131. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  132. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  133. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  134. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  135. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  136. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  137. data/lib/swarm_sdk/swarm.rb +0 -973
  138. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  139. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  140. data/lib/swarm_sdk/tools/base.rb +0 -63
  141. data/lib/swarm_sdk/tools/bash.rb +0 -280
  142. data/lib/swarm_sdk/tools/clock.rb +0 -46
  143. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  144. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  145. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  146. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  147. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  148. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  149. data/lib/swarm_sdk/tools/edit.rb +0 -145
  150. data/lib/swarm_sdk/tools/glob.rb +0 -166
  151. data/lib/swarm_sdk/tools/grep.rb +0 -235
  152. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  153. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  154. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  155. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  156. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  157. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  158. data/lib/swarm_sdk/tools/read.rb +0 -261
  159. data/lib/swarm_sdk/tools/registry.rb +0 -205
  160. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  161. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  163. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  164. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  165. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  166. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  167. data/lib/swarm_sdk/tools/think.rb +0 -100
  168. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  169. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  170. data/lib/swarm_sdk/tools/write.rb +0 -112
  171. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  172. data/lib/swarm_sdk/utils.rb +0 -68
  173. data/lib/swarm_sdk/validation_result.rb +0 -33
  174. data/lib/swarm_sdk/version.rb +0 -5
  175. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  176. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  177. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  178. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  179. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  180. data/lib/swarm_sdk/workflow.rb +0 -589
  181. data/lib/swarm_sdk.rb +0 -721
@@ -1,251 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Centralized configuration defaults for SwarmSDK
5
- #
6
- # This module provides well-documented default values for all configurable
7
- # aspects of the SDK. Values are organized by category and include explanations
8
- # for their purpose and rationale.
9
- #
10
- # @example Accessing defaults
11
- # SwarmSDK::Defaults::Timeouts::AGENT_REQUEST_SECONDS
12
- # SwarmSDK::Defaults::Concurrency::GLOBAL_LIMIT
13
- # SwarmSDK::Defaults::Limits::OUTPUT_CHARACTERS
14
- module Defaults
15
- # Concurrency limits for parallel execution
16
- #
17
- # These limits prevent overwhelming external services and ensure
18
- # fair resource usage across the system.
19
- module Concurrency
20
- # Maximum concurrent API calls across entire swarm
21
- #
22
- # This limits total parallel LLM API requests to prevent rate limiting
23
- # and excessive resource consumption. 50 is a balanced value that allows
24
- # good parallelism while respecting API rate limits.
25
- GLOBAL_LIMIT = 50
26
-
27
- # Maximum parallel tool executions per agent
28
- #
29
- # Limits concurrent tool calls within a single agent. 10 allows
30
- # meaningful parallelism (e.g., reading multiple files) without
31
- # overwhelming the system.
32
- LOCAL_LIMIT = 10
33
- end
34
-
35
- # Timeout values for various operations
36
- #
37
- # All timeouts are in seconds unless explicitly marked as milliseconds.
38
- # Timeouts balance responsiveness with allowing enough time for operations
39
- # to complete successfully.
40
- module Timeouts
41
- # LLM API request timeout (seconds)
42
- #
43
- # Default timeout for Claude/GPT API calls. 5 minutes accommodates
44
- # reasoning models (o1, Claude with extended thinking) which can take
45
- # longer to process complex queries.
46
- AGENT_REQUEST_SECONDS = 300
47
-
48
- # Bash command execution timeout (milliseconds)
49
- #
50
- # Default timeout for shell commands. 2 minutes balances allowing
51
- # build/test commands while preventing runaway processes.
52
- BASH_COMMAND_MS = 120_000
53
-
54
- # Maximum Bash command timeout (milliseconds)
55
- #
56
- # Hard upper limit for bash commands. 10 minutes prevents indefinitely
57
- # running commands while allowing long builds/tests.
58
- BASH_COMMAND_MAX_MS = 600_000
59
-
60
- # Web fetch timeout (seconds)
61
- #
62
- # Timeout for HTTP requests in WebFetch tool. 30 seconds is standard
63
- # for web requests, allowing slow servers while timing out unresponsive ones.
64
- WEB_FETCH_SECONDS = 30
65
-
66
- # Shell hook executor timeout (seconds)
67
- #
68
- # Default timeout for hook shell commands. 60 seconds allows complex
69
- # pre/post hooks while preventing indefinite blocking.
70
- HOOK_SHELL_SECONDS = 60
71
-
72
- # Workflow transformer command timeout (seconds)
73
- #
74
- # Timeout for input/output transformer bash commands. 60 seconds allows
75
- # data transformation operations while preventing stalls.
76
- TRANSFORMER_COMMAND_SECONDS = 60
77
-
78
- # Execution timeout (seconds)
79
- #
80
- # Maximum wall-clock time for entire swarm.execute() call.
81
- # 30 minutes allows complex multi-agent workflows while preventing
82
- # runaway execution.
83
- EXECUTION_TIMEOUT_SECONDS = 1800
84
-
85
- # Turn timeout (seconds)
86
- #
87
- # Maximum time for a single agent.ask() call, including all LLM requests
88
- # and tool executions. 30 minutes accommodates extended thinking models
89
- # and complex tool chains.
90
- TURN_TIMEOUT_SECONDS = 1800
91
-
92
- # OpenAI responses API ID TTL (seconds)
93
- #
94
- # Time-to-live for cached response IDs. 5 minutes allows conversation
95
- # continuity while preventing stale cache issues.
96
- RESPONSES_API_TTL_SECONDS = 300
97
-
98
- # MCP client request timeout (seconds)
99
- #
100
- # Default timeout for MCP server connections. 5 minutes accommodates
101
- # long-running SSE streams and tool executions. This timeout applies to
102
- # the entire operation (operation_timeout in HTTPX), so it must be long
103
- # enough for SSE connections that may run for extended periods.
104
- MCP_REQUEST_SECONDS = 300
105
- end
106
-
107
- # MCP reconnection configuration
108
- #
109
- # Settings for automatic reconnection when SSE/streamable connections drop.
110
- # Note: The background SSE notification stream uses operation_timeout which
111
- # limits total connection duration. Since this stream is meant to stay open
112
- # indefinitely for server notifications, we configure aggressive reconnection
113
- # so timeouts are transparent to users. Tool calls use separate connections
114
- # and are unaffected by SSE stream timeouts.
115
- module McpReconnection
116
- # Maximum number of reconnection attempts
117
- #
118
- # Very high value (effectively infinite) because the SSE notification stream
119
- # is expected to timeout periodically due to operation_timeout limitations.
120
- # Reconnection is transparent - tool calls continue working regardless.
121
- MAX_RETRIES = 1000
122
-
123
- # Initial delay between reconnection attempts (milliseconds)
124
- #
125
- # Fast initial reconnect (500ms) to minimize notification gaps.
126
- INITIAL_DELAY_MS = 500
127
-
128
- # Exponential backoff growth factor
129
- #
130
- # Slow growth (1.2x) because we expect frequent reconnections.
131
- # 500ms -> 600ms -> 720ms -> 864ms -> 1037ms -> ...
132
- DELAY_GROW_FACTOR = 1.2
133
-
134
- # Maximum delay between reconnection attempts (milliseconds)
135
- #
136
- # Caps at 10 seconds to ensure responsive reconnection even after many retries.
137
- MAX_DELAY_MS = 10_000
138
- end
139
-
140
- # Output and content size limits
141
- #
142
- # These limits prevent overwhelming context windows and ensure
143
- # reasonable memory usage.
144
- module Limits
145
- # Maximum Bash output characters
146
- #
147
- # Truncates command output to prevent overwhelming agent context.
148
- # 30,000 characters balances useful information with context constraints.
149
- OUTPUT_CHARACTERS = 30_000
150
-
151
- # Default lines to read from files
152
- #
153
- # When no explicit limit is set, Read tool returns first 2000 lines.
154
- # This provides substantial file content while preventing huge files
155
- # from overwhelming context.
156
- READ_LINES = 2000
157
-
158
- # Maximum characters per line in Read output
159
- #
160
- # Truncates very long lines to prevent single lines from consuming
161
- # excessive context. 2000 characters per line is generous while
162
- # protecting against minified files.
163
- LINE_CHARACTERS = 2000
164
-
165
- # Maximum WebFetch content length
166
- #
167
- # Limits web content fetched from URLs. 100,000 characters provides
168
- # substantial page content while preventing huge pages from overwhelming context.
169
- WEB_FETCH_CHARACTERS = 100_000
170
-
171
- # Maximum Glob search results
172
- #
173
- # Limits number of file paths returned by Glob tool. 1000 results
174
- # provides comprehensive search while preventing overwhelming output.
175
- GLOB_RESULTS = 1000
176
- end
177
-
178
- # Storage limits for persistent data
179
- module Storage
180
- # Maximum size for single scratchpad entry (bytes)
181
- #
182
- # 3MB per entry prevents individual entries from consuming excessive storage
183
- # while allowing substantial content (code, large texts).
184
- ENTRY_SIZE_BYTES = 3_000_000
185
-
186
- # Maximum total scratchpad storage (bytes)
187
- #
188
- # 100GB total storage provides ample room for extensive projects
189
- # while preventing unbounded growth.
190
- TOTAL_SIZE_BYTES = 100_000_000_000
191
- end
192
-
193
- # Context management settings
194
- module Context
195
- # Context usage percentage triggering compression warning
196
- #
197
- # When context usage reaches 60%, agents should consider compaction.
198
- # This threshold provides buffer before hitting limits.
199
- COMPRESSION_THRESHOLD_PERCENT = 60
200
-
201
- # Message count between TodoWrite reminders
202
- #
203
- # After 8 messages without using TodoWrite, a gentle reminder is injected.
204
- # Balances helpfulness without being annoying.
205
- TODOWRITE_REMINDER_INTERVAL = 8
206
- end
207
-
208
- # Token estimation factors
209
- #
210
- # Used for approximate token counting when precise counts aren't available.
211
- module TokenEstimation
212
- # Characters per token for prose text
213
- #
214
- # Average of ~4 characters per token for natural language text.
215
- # Based on empirical analysis of tokenization patterns.
216
- CHARS_PER_TOKEN_PROSE = 4.0
217
-
218
- # Characters per token for code
219
- #
220
- # Code tends to have shorter tokens due to symbols and operators.
221
- # ~3.5 characters per token accounts for this density.
222
- CHARS_PER_TOKEN_CODE = 3.5
223
- end
224
-
225
- # Logging configuration
226
- module Logging
227
- # Default MCP client log level
228
- #
229
- # WARN level suppresses verbose MCP client logs while still
230
- # reporting important issues.
231
- MCP_LOG_LEVEL = Logger::WARN
232
- end
233
-
234
- # Agent configuration defaults
235
- #
236
- # Default values for agent configuration when not explicitly specified.
237
- module Agent
238
- # Default LLM model identifier
239
- #
240
- # OpenAI's GPT-5 is used as the default model. This can be overridden
241
- # per-agent or globally via all_agents configuration.
242
- MODEL = "gpt-5"
243
-
244
- # Default LLM provider
245
- #
246
- # OpenAI is the default provider. Supported providers include:
247
- # openai, anthropic, gemini, deepseek, openrouter, bedrock, etc.
248
- PROVIDER = "openai"
249
- end
250
- end
251
- end
@@ -1,199 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Reconstructs RubyLLM::Message objects from SwarmSDK event streams
5
- #
6
- # This class enables conversation replay and analysis from event logs.
7
- # It uses timestamps to maintain chronological ordering of messages.
8
- #
9
- # ## Limitations
10
- #
11
- # This reconstructs ONLY conversation messages. It does NOT restore:
12
- # - Context state (warning thresholds, compression, todowrite index)
13
- # - Scratchpad contents
14
- # - Read tracking information
15
- # - Full swarm state
16
- #
17
- # For full state restoration, use StateSnapshot/StateRestorer or SnapshotFromEvents.
18
- #
19
- # ## Usage
20
- #
21
- # # Collect events during execution
22
- # events = []
23
- # swarm.execute("Build feature") do |event|
24
- # events << event
25
- # end
26
- #
27
- # # Reconstruct conversation for an agent
28
- # messages = SwarmSDK::EventsToMessages.reconstruct(events, agent: :backend)
29
- #
30
- # # View conversation
31
- # messages.each do |msg|
32
- # puts "[#{msg.role}] #{msg.content}"
33
- # end
34
- #
35
- # ## Event Requirements
36
- #
37
- # Events must have:
38
- # - `:timestamp` field (ISO 8601 format) for ordering
39
- # - `:agent` field to filter by agent
40
- # - `:type` field to identify event type
41
- #
42
- # Supported event types:
43
- # - `user_prompt`: Reconstructs user message (prompt in metadata or top-level)
44
- # - `agent_step`: Reconstructs assistant message with tool calls
45
- # - `agent_stop`: Reconstructs final assistant message
46
- # - `tool_result`: Reconstructs tool result message
47
- # - `delegation_result`: Reconstructs tool result message from delegation
48
- class EventsToMessages
49
- class << self
50
- # Reconstruct messages for an agent from event stream
51
- #
52
- # @param events [Array<Hash>] Event stream with timestamps
53
- # @param agent [Symbol, String] Agent name to reconstruct messages for
54
- # @return [Array<RubyLLM::Message>] Reconstructed messages in chronological order
55
- #
56
- # @example
57
- # messages = EventsToMessages.reconstruct(events, agent: :backend)
58
- # messages.each { |msg| puts msg.content }
59
- def reconstruct(events, agent:)
60
- new(events, agent).reconstruct
61
- end
62
- end
63
-
64
- # Initialize reconstructor
65
- #
66
- # @param events [Array<Hash>] Event stream
67
- # @param agent [Symbol, String] Agent name
68
- def initialize(events, agent)
69
- @events = events
70
- @agent = agent.to_sym
71
- end
72
-
73
- # Reconstruct messages from events
74
- #
75
- # Filters events by agent, sorts by timestamp, and converts to RubyLLM::Message objects.
76
- #
77
- # @return [Array<RubyLLM::Message>] Reconstructed messages
78
- def reconstruct
79
- messages = []
80
-
81
- # Filter events for this agent and sort by timestamp
82
- agent_events = @events
83
- .select { |e| normalize_agent(e[:agent]) == @agent }
84
- .sort_by { |e| parse_timestamp(e[:timestamp]) }
85
-
86
- agent_events.each do |event|
87
- message = case event[:type]&.to_s
88
- when "user_prompt"
89
- reconstruct_user_message(event)
90
- when "agent_step", "agent_stop"
91
- reconstruct_assistant_message(event)
92
- when "tool_result"
93
- reconstruct_tool_result_message(event)
94
- when "delegation_result"
95
- reconstruct_delegation_result_message(event)
96
- end
97
-
98
- messages << message if message
99
- end
100
-
101
- messages
102
- end
103
-
104
- private
105
-
106
- # Reconstruct user message from user_prompt event
107
- #
108
- # Extracts prompt from metadata or top-level field.
109
- #
110
- # @param event [Hash] user_prompt event
111
- # @return [RubyLLM::Message, nil] User message or nil if prompt not found
112
- def reconstruct_user_message(event)
113
- # Try to extract prompt from metadata (current location) or top-level (potential future location)
114
- prompt = event.dig(:metadata, :prompt) || event[:prompt]
115
- return unless prompt && !prompt.to_s.empty?
116
-
117
- RubyLLM::Message.new(
118
- role: :user,
119
- content: prompt,
120
- )
121
- end
122
-
123
- # Reconstruct assistant message from agent_step or agent_stop event
124
- #
125
- # Converts tool_calls array to hash format expected by RubyLLM.
126
- #
127
- # @param event [Hash] agent_step or agent_stop event
128
- # @return [RubyLLM::Message] Assistant message
129
- def reconstruct_assistant_message(event)
130
- # Convert tool_calls array to hash (RubyLLM format)
131
- # Events emit tool_calls as Array, but RubyLLM expects Hash<String, ToolCall>
132
- tool_calls_hash = if event[:tool_calls] && !event[:tool_calls].empty?
133
- event[:tool_calls].each_with_object({}) do |tc, hash|
134
- hash[tc[:id].to_s] = RubyLLM::ToolCall.new(
135
- id: tc[:id],
136
- name: tc[:name],
137
- arguments: tc[:arguments] || {},
138
- )
139
- end
140
- end
141
-
142
- RubyLLM::Message.new(
143
- role: :assistant,
144
- content: event[:content] || "",
145
- tool_calls: tool_calls_hash,
146
- input_tokens: event.dig(:usage, :input_tokens),
147
- output_tokens: event.dig(:usage, :output_tokens),
148
- model_id: event[:model],
149
- )
150
- end
151
-
152
- # Reconstruct tool result message from tool_result event
153
- #
154
- # @param event [Hash] tool_result event
155
- # @return [RubyLLM::Message] Tool result message
156
- def reconstruct_tool_result_message(event)
157
- RubyLLM::Message.new(
158
- role: :tool,
159
- content: event[:result].to_s,
160
- tool_call_id: event[:tool_call_id],
161
- )
162
- end
163
-
164
- # Reconstruct tool result message from delegation_result event
165
- #
166
- # delegation_result events are emitted when a delegation completes,
167
- # and they should be converted to tool result messages in the conversation.
168
- #
169
- # @param event [Hash] delegation_result event
170
- # @return [RubyLLM::Message] Tool result message
171
- def reconstruct_delegation_result_message(event)
172
- RubyLLM::Message.new(
173
- role: :tool,
174
- content: event[:result].to_s,
175
- tool_call_id: event[:tool_call_id],
176
- )
177
- end
178
-
179
- # Parse timestamp string to Time object
180
- #
181
- # @param timestamp [String, nil] ISO 8601 timestamp
182
- # @return [Time] Parsed time or epoch if nil/invalid
183
- def parse_timestamp(timestamp)
184
- return Time.at(0) unless timestamp
185
-
186
- Time.parse(timestamp)
187
- rescue ArgumentError
188
- Time.at(0)
189
- end
190
-
191
- # Normalize agent name to symbol
192
- #
193
- # @param agent [Symbol, String, nil] Agent name
194
- # @return [Symbol] Normalized agent name
195
- def normalize_agent(agent)
196
- agent.to_s.to_sym
197
- end
198
- end
199
- end