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
@@ -1,397 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Reconstructs a complete StateSnapshot from event logs
5
- #
6
- # This class enables full swarm state reconstruction from event streams,
7
- # supporting session persistence, time travel debugging, and event sourcing.
8
- #
9
- # ## Usage
10
- #
11
- # # Collect events during execution
12
- # events = []
13
- # swarm.execute("Build feature") { |event| events << event }
14
- #
15
- # # Save events to storage (DB, file, Redis, etc.)
16
- # File.write("session.json", JSON.generate(events))
17
- #
18
- # # Later: Reconstruct snapshot from events
19
- # events = JSON.parse(File.read("session.json"), symbolize_names: true)
20
- # snapshot_data = SwarmSDK::SnapshotFromEvents.reconstruct(events)
21
- #
22
- # # Restore swarm from reconstructed snapshot
23
- # swarm = SwarmSDK::Swarm.from_config("swarm.yml")
24
- # swarm.restore(snapshot_data)
25
- #
26
- # ## What Gets Reconstructed
27
- #
28
- # - Swarm metadata (swarm_id, parent_swarm_id, first_message_sent)
29
- # - Agent conversations (all RubyLLM::Message objects)
30
- # - Agent context state (warnings, compression, todowrite, skills)
31
- # - Delegation instance conversations
32
- # - Scratchpad contents
33
- # - Read tracking with digests
34
- # - Memory read tracking with digests
35
- #
36
- # ## Requirements
37
- #
38
- # Events must have:
39
- # - :timestamp field (ISO 8601 format) for chronological ordering
40
- # - :agent field to identify which agent
41
- # - :type field to identify event type
42
- #
43
- # @see EventsToMessages for message reconstruction details
44
- class SnapshotFromEvents
45
- class << self
46
- # Reconstruct a complete StateSnapshot from event stream
47
- #
48
- # @param events [Array<Hash>] Event stream with timestamps
49
- # @return [Hash] Complete StateSnapshot hash compatible with StateRestorer
50
- #
51
- # @example
52
- # snapshot_data = SnapshotFromEvents.reconstruct(events)
53
- # swarm.restore(snapshot_data)
54
- def reconstruct(events)
55
- new(events).reconstruct
56
- end
57
- end
58
-
59
- # Initialize reconstructor
60
- #
61
- # @param events [Array<Hash>] Event stream
62
- def initialize(events)
63
- # Sort events by timestamp for chronological processing
64
- @events = events.sort_by { |e| parse_timestamp(e[:timestamp]) }
65
- end
66
-
67
- # Reconstruct complete snapshot
68
- #
69
- # @return [Hash] StateSnapshot hash
70
- def reconstruct
71
- {
72
- version: "2.1.0",
73
- type: "swarm",
74
- snapshot_at: @events.last&.fetch(:timestamp, Time.now.utc.iso8601),
75
- swarm_sdk_version: SwarmSDK::VERSION,
76
- metadata: reconstruct_swarm_metadata,
77
- agents: reconstruct_all_agents,
78
- delegation_instances: reconstruct_all_delegations,
79
- scratchpad: reconstruct_scratchpad,
80
- read_tracking: reconstruct_read_tracking,
81
- memory_read_tracking: reconstruct_memory_read_tracking,
82
- plugin_states: reconstruct_plugin_states,
83
- }
84
- end
85
-
86
- private
87
-
88
- # Reconstruct swarm metadata
89
- #
90
- # @return [Hash] Swarm metadata
91
- def reconstruct_swarm_metadata
92
- first_event = @events.first || {}
93
-
94
- {
95
- id: first_event[:swarm_id],
96
- parent_id: first_event[:parent_swarm_id],
97
- first_message_sent: !@events.empty?,
98
- }
99
- end
100
-
101
- # Reconstruct all primary agents
102
- #
103
- # @return [Hash] { agent_name => { conversation:, context_state:, system_prompt: } }
104
- def reconstruct_all_agents
105
- # Get unique primary agent names (exclude delegations with @)
106
- agent_names = @events
107
- .map { |e| e[:agent] }
108
- .compact
109
- .uniq
110
- .reject { |name| name.to_s.include?("@") }
111
-
112
- agent_names.each_with_object({}) do |agent, hash|
113
- hash[agent.to_s] = {
114
- conversation: reconstruct_conversation(agent),
115
- context_state: reconstruct_context_state(agent),
116
- system_prompt: reconstruct_system_prompt(agent),
117
- }
118
- end
119
- end
120
-
121
- # Reconstruct all delegation instances
122
- #
123
- # @return [Hash] { "delegate@delegator" => { conversation:, context_state:, system_prompt: } }
124
- def reconstruct_all_delegations
125
- # Get unique delegation instance names (contain @)
126
- delegation_names = @events
127
- .map { |e| e[:agent] }
128
- .compact
129
- .uniq
130
- .select { |name| name.to_s.include?("@") }
131
-
132
- delegation_names.each_with_object({}) do |delegation, hash|
133
- hash[delegation.to_s] = {
134
- conversation: reconstruct_conversation(delegation),
135
- context_state: reconstruct_context_state(delegation),
136
- system_prompt: reconstruct_system_prompt(delegation),
137
- }
138
- end
139
- end
140
-
141
- # Reconstruct conversation for an agent
142
- #
143
- # @param agent [Symbol, String] Agent name
144
- # @return [Array<Hash>] Serialized RubyLLM::Message objects
145
- def reconstruct_conversation(agent)
146
- messages = SwarmSDK::EventsToMessages.reconstruct(@events, agent: agent)
147
- messages.map { |msg| serialize_message(msg) }
148
- end
149
-
150
- # Serialize a RubyLLM::Message to hash format
151
- #
152
- # Matches the format used by StateSnapshot.
153
- #
154
- # @param msg [RubyLLM::Message] Message to serialize
155
- # @return [Hash] Serialized message
156
- def serialize_message(msg)
157
- hash = { role: msg.role, content: msg.content }
158
-
159
- # Serialize tool calls if present
160
- # Must manually extract fields - RubyLLM::ToolCall#to_h doesn't reliably serialize id/name
161
- # msg.tool_calls is a Hash<String, ToolCall>, so we need .values
162
- if msg.tool_calls && !msg.tool_calls.empty?
163
- hash[:tool_calls] = msg.tool_calls.values.map do |tc|
164
- {
165
- id: tc.id,
166
- name: tc.name,
167
- arguments: tc.arguments,
168
- }
169
- end
170
- end
171
-
172
- # Add optional fields
173
- hash[:tool_call_id] = msg.tool_call_id if msg.tool_call_id
174
- hash[:input_tokens] = msg.input_tokens if msg.input_tokens
175
- hash[:output_tokens] = msg.output_tokens if msg.output_tokens
176
- hash[:model_id] = msg.model_id if msg.model_id
177
-
178
- hash
179
- end
180
-
181
- # Reconstruct context state for an agent
182
- #
183
- # @param agent [Symbol, String] Agent name
184
- # @return [Hash] Context state
185
- def reconstruct_context_state(agent)
186
- {
187
- warning_thresholds_hit: reconstruct_warning_thresholds(agent),
188
- compression_applied: reconstruct_compression_applied(agent),
189
- last_todowrite_message_index: reconstruct_todowrite_index(agent),
190
- active_skill_path: reconstruct_active_skill_path(agent),
191
- }
192
- end
193
-
194
- # Reconstruct which warning thresholds were hit
195
- #
196
- # @param agent [Symbol, String] Agent name
197
- # @return [Array<Integer>] Sorted array of thresholds that were hit
198
- def reconstruct_warning_thresholds(agent)
199
- @events
200
- .select { |e| e[:type] == "context_threshold_hit" && normalize_agent(e[:agent]) == normalize_agent(agent) }
201
- .map { |e| e[:threshold] }
202
- .uniq
203
- .sort
204
- end
205
-
206
- # Reconstruct compression state
207
- #
208
- # @param agent [Symbol, String] Agent name
209
- # @return [Boolean, nil] true if compression was applied, nil otherwise
210
- def reconstruct_compression_applied(agent)
211
- compression_event = @events
212
- .select { |e| e[:type] == "compression_completed" && normalize_agent(e[:agent]) == normalize_agent(agent) }
213
- .last
214
-
215
- # NOTE: StateSnapshot stores nil (not false) when no compression applied
216
- compression_event ? true : nil
217
- end
218
-
219
- # Reconstruct last TodoWrite message index
220
- #
221
- # Finds the last TodoWrite tool call and determines which message index it corresponds to.
222
- #
223
- # @param agent [Symbol, String] Agent name
224
- # @return [Integer, nil] Message index or nil if no TodoWrite calls
225
- def reconstruct_todowrite_index(agent)
226
- # Find last TodoWrite tool call
227
- last_todowrite_call = @events
228
- .select { |e| e[:type] == "tool_call" && e[:tool] == "TodoWrite" && normalize_agent(e[:agent]) == normalize_agent(agent) }
229
- .sort_by { |e| parse_timestamp(e[:timestamp]) }
230
- .last
231
-
232
- return unless last_todowrite_call
233
-
234
- # Reconstruct messages to find index
235
- messages = SwarmSDK::EventsToMessages.reconstruct(@events, agent: agent)
236
-
237
- # Find the message containing this tool_call_id
238
- messages.each_with_index do |msg, idx|
239
- if msg.role == :assistant && msg.tool_calls
240
- return idx if msg.tool_calls.key?(last_todowrite_call[:tool_call_id])
241
- end
242
- end
243
-
244
- nil
245
- end
246
-
247
- # Reconstruct active skill path
248
- #
249
- # @param agent [Symbol, String] Agent name
250
- # @return [String, nil] Skill path or nil if no skill loaded
251
- def reconstruct_active_skill_path(agent)
252
- last_load_skill = @events
253
- .select { |e| e[:type] == "tool_call" && e[:tool] == "LoadSkill" && normalize_agent(e[:agent]) == normalize_agent(agent) }
254
- .sort_by { |e| parse_timestamp(e[:timestamp]) }
255
- .last
256
-
257
- return unless last_load_skill
258
-
259
- # Extract skill path from arguments (handle both symbol and string keys)
260
- args = last_load_skill[:arguments]
261
- args[:file_path] || args["file_path"]
262
- end
263
-
264
- # Reconstruct system prompt from the last agent_start event
265
- #
266
- # Uses the LAST agent_start event for this agent, which represents the most
267
- # recent system prompt that was active. This handles cases where the swarm
268
- # was restarted with updated configuration.
269
- #
270
- # @param agent [Symbol, String] Agent name
271
- # @return [String, nil] System prompt or nil if no agent_start event found
272
- def reconstruct_system_prompt(agent)
273
- last_agent_start = @events
274
- .select { |e| e[:type] == "agent_start" && normalize_agent(e[:agent]) == normalize_agent(agent) }
275
- .sort_by { |e| parse_timestamp(e[:timestamp]) }
276
- .last
277
-
278
- return unless last_agent_start
279
-
280
- # Handle both symbol and string keys from JSON
281
- last_agent_start[:system_prompt] || last_agent_start["system_prompt"]
282
- end
283
-
284
- # Reconstruct scratchpad contents
285
- #
286
- # Replays all ScratchpadWrite tool calls in chronological order.
287
- # Later writes to the same path overwrite earlier ones.
288
- #
289
- # @return [Hash] { path => { content:, title:, updated_at:, size: } }
290
- def reconstruct_scratchpad
291
- scratchpad = {}
292
-
293
- @events
294
- .select { |e| e[:type] == "tool_call" && e[:tool] == "ScratchpadWrite" }
295
- .sort_by { |e| parse_timestamp(e[:timestamp]) }
296
- .each do |event|
297
- args = event[:arguments]
298
-
299
- # Handle both symbol and string keys
300
- path = args[:file_path] || args["file_path"]
301
- content = args[:content] || args["content"]
302
- title = args[:title] || args["title"]
303
-
304
- next unless path && content
305
-
306
- scratchpad[path] = {
307
- content: content,
308
- title: title,
309
- updated_at: event[:timestamp],
310
- size: content.bytesize,
311
- }
312
- end
313
-
314
- scratchpad
315
- end
316
-
317
- # Reconstruct read tracking (file digests)
318
- #
319
- # Extracts digests from Read tool_result metadata.
320
- # Later reads to the same file update the digest.
321
- #
322
- # @return [Hash] { agent_name => { file_path => digest } }
323
- def reconstruct_read_tracking
324
- tracking = {}
325
-
326
- @events
327
- .select { |e| e[:type] == "tool_result" && e[:tool] == "Read" }
328
- .each do |event|
329
- agent = event[:agent].to_s # Use string key to match StateSnapshot format
330
- digest = event.dig(:metadata, :read_digest)
331
- path = event.dig(:metadata, :read_path)
332
-
333
- next unless digest && path
334
-
335
- tracking[agent] ||= {}
336
- tracking[agent][path] = digest
337
- end
338
-
339
- tracking
340
- end
341
-
342
- # Reconstruct memory read tracking (entry digests)
343
- #
344
- # Extracts digests from MemoryRead tool_result metadata.
345
- # Later reads to the same entry update the digest.
346
- #
347
- # @return [Hash] { agent_name => { entry_path => digest } }
348
- def reconstruct_memory_read_tracking
349
- tracking = {}
350
-
351
- @events
352
- .select { |e| e[:type] == "tool_result" && e[:tool] == "MemoryRead" }
353
- .each do |event|
354
- agent = event[:agent].to_s # Use string key to match StateSnapshot format
355
- digest = event.dig(:metadata, :read_digest)
356
- path = event.dig(:metadata, :read_path)
357
-
358
- next unless digest && path
359
-
360
- tracking[agent] ||= {}
361
- tracking[agent][path] = digest
362
- end
363
-
364
- tracking
365
- end
366
-
367
- # Reconstruct plugin states
368
- #
369
- # Plugin states cannot be fully reconstructed from events alone as they
370
- # contain internal plugin data. Returns empty hash for compatibility.
371
- #
372
- # @return [Hash] Empty plugin states hash
373
- def reconstruct_plugin_states
374
- {}
375
- end
376
-
377
- # Parse timestamp string to Time object
378
- #
379
- # @param timestamp [String, nil] ISO 8601 timestamp
380
- # @return [Time] Parsed time or epoch if nil/invalid
381
- def parse_timestamp(timestamp)
382
- return Time.at(0) unless timestamp
383
-
384
- Time.parse(timestamp)
385
- rescue ArgumentError
386
- Time.at(0)
387
- end
388
-
389
- # Normalize agent name for comparison
390
- #
391
- # @param agent [Symbol, String, nil] Agent name
392
- # @return [Symbol] Normalized agent name
393
- def normalize_agent(agent)
394
- agent.to_s.to_sym
395
- end
396
- end
397
- end