swarm_memory 2.1.5 → 2.1.6

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. metadata +5 -184
  4. data/lib/claude_swarm/base_executor.rb +0 -133
  5. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  6. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  7. data/lib/claude_swarm/cli.rb +0 -697
  8. data/lib/claude_swarm/commands/ps.rb +0 -215
  9. data/lib/claude_swarm/commands/show.rb +0 -139
  10. data/lib/claude_swarm/configuration.rb +0 -373
  11. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  12. data/lib/claude_swarm/json_handler.rb +0 -91
  13. data/lib/claude_swarm/mcp_generator.rb +0 -230
  14. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  15. data/lib/claude_swarm/openai/executor.rb +0 -256
  16. data/lib/claude_swarm/openai/responses.rb +0 -319
  17. data/lib/claude_swarm/orchestrator.rb +0 -878
  18. data/lib/claude_swarm/process_tracker.rb +0 -78
  19. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  20. data/lib/claude_swarm/session_path.rb +0 -42
  21. data/lib/claude_swarm/settings_generator.rb +0 -77
  22. data/lib/claude_swarm/system_utils.rb +0 -46
  23. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  24. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  25. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  27. data/lib/claude_swarm/version.rb +0 -5
  28. data/lib/claude_swarm/worktree_manager.rb +0 -475
  29. data/lib/claude_swarm/yaml_loader.rb +0 -22
  30. data/lib/claude_swarm.rb +0 -67
  31. data/lib/swarm_cli/cli.rb +0 -201
  32. data/lib/swarm_cli/command_registry.rb +0 -61
  33. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  34. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  35. data/lib/swarm_cli/commands/migrate.rb +0 -55
  36. data/lib/swarm_cli/commands/run.rb +0 -173
  37. data/lib/swarm_cli/config_loader.rb +0 -98
  38. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  39. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  40. data/lib/swarm_cli/interactive_repl.rb +0 -924
  41. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  42. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  43. data/lib/swarm_cli/migrate_options.rb +0 -54
  44. data/lib/swarm_cli/migrator.rb +0 -132
  45. data/lib/swarm_cli/options.rb +0 -151
  46. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  47. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  48. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  49. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  50. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  51. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  52. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  53. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  54. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  55. data/lib/swarm_cli/ui/icons.rb +0 -36
  56. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  57. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  58. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  59. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  60. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  61. data/lib/swarm_cli/version.rb +0 -5
  62. data/lib/swarm_cli.rb +0 -46
  63. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  64. data/lib/swarm_sdk/agent/builder.rb +0 -552
  65. data/lib/swarm_sdk/agent/chat.rb +0 -774
  66. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  67. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  68. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  69. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  70. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  71. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  72. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  73. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  75. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  76. data/lib/swarm_sdk/agent/context.rb +0 -116
  77. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  78. data/lib/swarm_sdk/agent/definition.rb +0 -477
  79. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  80. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  81. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  82. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  83. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  84. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  85. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  86. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  87. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  88. data/lib/swarm_sdk/configuration.rb +0 -135
  89. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  90. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  91. data/lib/swarm_sdk/context_compactor.rb +0 -335
  92. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  93. data/lib/swarm_sdk/context_management/context.rb +0 -328
  94. data/lib/swarm_sdk/defaults.rb +0 -196
  95. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  96. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  97. data/lib/swarm_sdk/hooks/context.rb +0 -197
  98. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  99. data/lib/swarm_sdk/hooks/error.rb +0 -29
  100. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  101. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  102. data/lib/swarm_sdk/hooks/result.rb +0 -150
  103. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  104. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  105. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  106. data/lib/swarm_sdk/log_collector.rb +0 -227
  107. data/lib/swarm_sdk/log_stream.rb +0 -127
  108. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  109. data/lib/swarm_sdk/model_aliases.json +0 -8
  110. data/lib/swarm_sdk/models.json +0 -1
  111. data/lib/swarm_sdk/models.rb +0 -120
  112. data/lib/swarm_sdk/node_context.rb +0 -245
  113. data/lib/swarm_sdk/observer/builder.rb +0 -81
  114. data/lib/swarm_sdk/observer/config.rb +0 -45
  115. data/lib/swarm_sdk/observer/manager.rb +0 -236
  116. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  117. data/lib/swarm_sdk/permissions/config.rb +0 -239
  118. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  119. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  120. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  121. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  122. data/lib/swarm_sdk/plugin.rb +0 -309
  123. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  124. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  125. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  126. data/lib/swarm_sdk/restore_result.rb +0 -65
  127. data/lib/swarm_sdk/result.rb +0 -123
  128. data/lib/swarm_sdk/snapshot.rb +0 -156
  129. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  130. data/lib/swarm_sdk/state_restorer.rb +0 -476
  131. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  132. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  133. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  134. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  135. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  136. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  141. data/lib/swarm_sdk/swarm.rb +0 -717
  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/bash.rb +0 -282
  145. data/lib/swarm_sdk/tools/clock.rb +0 -44
  146. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  147. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  148. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  149. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  150. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  151. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  152. data/lib/swarm_sdk/tools/edit.rb +0 -145
  153. data/lib/swarm_sdk/tools/glob.rb +0 -166
  154. data/lib/swarm_sdk/tools/grep.rb +0 -235
  155. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  156. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
  157. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -98
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/utils.rb +0 -68
  174. data/lib/swarm_sdk/validation_result.rb +0 -33
  175. data/lib/swarm_sdk/version.rb +0 -5
  176. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  177. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  178. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  179. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  180. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  181. data/lib/swarm_sdk/workflow.rb +0 -554
  182. data/lib/swarm_sdk.rb +0 -524
@@ -1,335 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # ContextCompactor implements intelligent conversation history compression
5
- #
6
- # The Hybrid Production Strategy combines three compression techniques:
7
- # 1. Tool result pruning - Aggressively truncate tool outputs (80% of tokens!)
8
- # 2. Checkpoint creation - LLM-generated summaries of conversation chunks
9
- # 3. Sliding window - Keep recent messages in full detail
10
- #
11
- # ## Usage
12
- #
13
- # # From Agent::Chat
14
- # metrics = chat.compact_context
15
- #
16
- # # With options
17
- # metrics = chat.compact_context(
18
- # tool_result_max_length: 500,
19
- # checkpoint_threshold: 50,
20
- # sliding_window_size: 20,
21
- # summarization_model: "claude-3-haiku-20240307"
22
- # )
23
- #
24
- # ## Metrics
25
- #
26
- # Returns a Metrics object with compression stats:
27
- # - original_message_count / compressed_message_count
28
- # - original_tokens / compressed_tokens
29
- # - compression_ratio (e.g., 0.15 = 15% of original)
30
- # - messages_removed / messages_summarized
31
- # - time_taken
32
- #
33
- class ContextCompactor
34
- # Default configuration
35
- DEFAULT_OPTIONS = {
36
- tool_result_max_length: 500, # Truncate tool results to N chars
37
- checkpoint_threshold: 50, # Create checkpoint after N messages
38
- sliding_window_size: 20, # Keep last N messages in full
39
- summarization_model: "claude-3-haiku-20240307", # Fast model for summaries
40
- preserve_system_messages: true, # Always keep system messages
41
- preserve_error_messages: true, # Always keep error messages
42
- }.freeze
43
-
44
- # Initialize compactor for a chat instance
45
- #
46
- # @param chat [Agent::Chat] The chat instance to compact
47
- # @param options [Hash] Configuration options (see DEFAULT_OPTIONS)
48
- def initialize(chat, options = {})
49
- @chat = chat
50
- @options = DEFAULT_OPTIONS.merge(options)
51
- @agent_name = chat.provider.respond_to?(:agent_name) ? chat.provider.agent_name : :unknown
52
- end
53
-
54
- # Compact the conversation history using hybrid production strategy
55
- #
56
- # Returns metrics about the compression operation.
57
- #
58
- # @return [ContextCompactor::Metrics] Compression metrics
59
- def compact
60
- start_time = Time.now
61
- original_messages = @chat.messages
62
-
63
- # Emit compression_started event
64
- LogStream.emit(
65
- type: "compression_started",
66
- agent: @agent_name,
67
- message_count: original_messages.size,
68
- estimated_tokens: TokenCounter.estimate_messages(original_messages),
69
- )
70
-
71
- # Step 1: Prune tool results
72
- pruned = prune_tool_results(original_messages)
73
-
74
- # Step 2: Create checkpoint if needed
75
- checkpointed = create_checkpoint_if_needed(pruned)
76
-
77
- # Step 3: Apply sliding window
78
- final_messages = apply_sliding_window(checkpointed)
79
-
80
- # Replace messages in chat
81
- replace_messages(final_messages)
82
-
83
- # Calculate metrics
84
- time_taken = Time.now - start_time
85
- metrics = ContextCompactor::Metrics.new(
86
- original_messages: original_messages,
87
- compressed_messages: final_messages,
88
- time_taken: time_taken,
89
- )
90
-
91
- # Emit compression_completed event
92
- LogStream.emit(
93
- type: "compression_completed",
94
- agent: @agent_name,
95
- original_message_count: metrics.original_message_count,
96
- compressed_message_count: metrics.compressed_message_count,
97
- original_tokens: metrics.original_tokens,
98
- compressed_tokens: metrics.compressed_tokens,
99
- compression_ratio: metrics.compression_ratio,
100
- messages_removed: metrics.messages_removed,
101
- messages_summarized: metrics.messages_summarized,
102
- time_taken: metrics.time_taken.round(3),
103
- )
104
-
105
- metrics
106
- end
107
-
108
- private
109
-
110
- # Step 1: Prune tool results to reduce token count
111
- #
112
- # Tool results often contain 80%+ of conversation tokens.
113
- # We truncate them aggressively while preserving errors.
114
- #
115
- # @param messages [Array<RubyLLM::Message>] Original messages
116
- # @return [Array<RubyLLM::Message>] Messages with pruned tool results
117
- def prune_tool_results(messages)
118
- max_length = @options[:tool_result_max_length]
119
-
120
- messages.map do |msg|
121
- # Only prune tool result messages
122
- next msg unless msg.role == :tool
123
-
124
- # Preserve error messages
125
- if @options[:preserve_error_messages] && msg.is_error
126
- next msg
127
- end
128
-
129
- # Truncate long tool results
130
- if msg.content.is_a?(String) && msg.content.length > max_length
131
- truncated_content = msg.content[0...max_length] + "\n\n[... truncated by context compaction ...]"
132
-
133
- # Create new message with truncated content
134
- # We can't modify messages in place, so we create a new one
135
- RubyLLM::Message.new(
136
- role: :tool,
137
- content: truncated_content,
138
- tool_call_id: msg.tool_call_id,
139
- )
140
- else
141
- msg
142
- end
143
- end
144
- end
145
-
146
- # Step 2: Create checkpoint if conversation is long enough
147
- #
148
- # Checkpoints are LLM-generated summaries that preserve context
149
- # while drastically reducing token count. We keep recent messages
150
- # in full detail and checkpoint older conversation.
151
- #
152
- # @param messages [Array<RubyLLM::Message>] Pruned messages
153
- # @return [Array<RubyLLM::Message>] Messages with checkpoint
154
- def create_checkpoint_if_needed(messages)
155
- threshold = @options[:checkpoint_threshold]
156
- window_size = @options[:sliding_window_size]
157
-
158
- # Only checkpoint if we have enough messages
159
- return messages if messages.size <= threshold
160
-
161
- # Separate system messages, old messages, and recent messages
162
- system_messages = messages.select { |m| m.role == :system }
163
- non_system_messages = messages.reject { |m| m.role == :system }
164
-
165
- # Keep recent messages, checkpoint the rest
166
- recent_messages = non_system_messages.last(window_size)
167
- old_messages = non_system_messages[0...-window_size]
168
-
169
- # Create checkpoint summary of old messages
170
- checkpoint_message = create_checkpoint_summary(old_messages)
171
-
172
- # Reconstruct: system messages + checkpoint + recent messages
173
- system_messages + [checkpoint_message] + recent_messages
174
- end
175
-
176
- # Step 3: Apply sliding window to keep conversation size bounded
177
- #
178
- # After checkpointing, we still apply a sliding window to ensure
179
- # the conversation doesn't grow unbounded.
180
- #
181
- # @param messages [Array<RubyLLM::Message>] Checkpointed messages
182
- # @return [Array<RubyLLM::Message>] Final messages
183
- def apply_sliding_window(messages)
184
- window_size = @options[:sliding_window_size]
185
-
186
- # Separate system messages from others
187
- system_messages = messages.select { |m| m.role == :system }
188
- non_system_messages = messages.reject { |m| m.role == :system }
189
-
190
- # Keep only the sliding window of non-system messages
191
- recent_messages = non_system_messages.last(window_size)
192
-
193
- # Always include system messages
194
- system_messages + recent_messages
195
- end
196
-
197
- # Create a checkpoint summary using an LLM
198
- #
199
- # Uses a fast model (Haiku) to generate a concise summary of
200
- # the conversation chunk that preserves critical context.
201
- #
202
- # @param messages [Array<RubyLLM::Message>] Messages to summarize
203
- # @return [RubyLLM::Message] Checkpoint message
204
- def create_checkpoint_summary(messages)
205
- # Extract key information for summarization
206
- user_messages = messages.select { |m| m.role == :user }.map(&:content).compact
207
- assistant_messages = messages.select { |m| m.role == :assistant }.map(&:content).compact
208
- tool_calls = messages.select { |m| m.role == :assistant && m.tool_calls&.any? }
209
-
210
- # Build summarization prompt
211
- prompt = build_summarization_prompt(
212
- user_messages: user_messages,
213
- assistant_messages: assistant_messages,
214
- tool_calls: tool_calls,
215
- message_count: messages.size,
216
- )
217
-
218
- # Generate summary using fast model
219
- summary = generate_summary(prompt)
220
-
221
- # Create checkpoint message
222
- checkpoint_content = <<~CHECKPOINT
223
- [CONVERSATION CHECKPOINT - #{Time.now.utc.iso8601}]
224
-
225
- #{summary}
226
-
227
- --- Continuing conversation from this point ---
228
- CHECKPOINT
229
-
230
- RubyLLM::Message.new(
231
- role: :system,
232
- content: checkpoint_content,
233
- )
234
- end
235
-
236
- # Build the summarization prompt for the LLM
237
- #
238
- # @param user_messages [Array<String>] User message contents
239
- # @param assistant_messages [Array<String>] Assistant message contents
240
- # @param tool_calls [Array<RubyLLM::Message>] Messages with tool calls
241
- # @param message_count [Integer] Total messages being summarized
242
- # @return [String] Summarization prompt
243
- def build_summarization_prompt(user_messages:, assistant_messages:, tool_calls:, message_count:)
244
- # Format tool calls for context
245
- tools_used = tool_calls.flat_map do |msg|
246
- msg.tool_calls.map { |_id, tc| tc.name }
247
- end.uniq
248
-
249
- # Get last few user messages for context
250
- recent_user_messages = user_messages.last(5).join("\n---\n")
251
-
252
- <<~PROMPT
253
- You are a conversation summarization specialist. Create a concise summary of this conversation
254
- that preserves all critical information needed for the assistant to continue working effectively.
255
-
256
- CONVERSATION STATS:
257
- - Total messages: #{message_count}
258
- - User messages: #{user_messages.size}
259
- - Assistant responses: #{assistant_messages.size}
260
- - Tools used: #{tools_used.join(", ")}
261
-
262
- RECENT USER REQUESTS (last 5):
263
- #{recent_user_messages}
264
-
265
- INSTRUCTIONS:
266
- Create a structured summary with these sections:
267
-
268
- ## Summary
269
- Brief overview of what has been discussed and accomplished (2-3 sentences)
270
-
271
- ## Key Facts Discovered
272
- - List important facts, findings, or observations
273
- - Include file paths, variable names, configurations discussed
274
- - Note any errors or issues encountered
275
-
276
- ## Decisions Made
277
- - List key decisions or approaches agreed upon
278
- - Include rationale if relevant
279
-
280
- ## Current State
281
- - What is the current state of the work?
282
- - What files or systems have been modified?
283
- - What is working / what needs work?
284
-
285
- ## Tools & Actions Completed
286
- - Summarize major tool calls and their outcomes
287
- - Focus on successful operations and their results
288
-
289
- Be concise but comprehensive. Preserve all information the assistant will need to continue
290
- the conversation seamlessly. Use bullet points for clarity.
291
- PROMPT
292
- end
293
-
294
- # Generate summary using a fast LLM model
295
- #
296
- # @param prompt [String] Summarization prompt
297
- # @return [String] Generated summary
298
- def generate_summary(prompt)
299
- # Create a temporary chat for summarization
300
- summary_chat = RubyLLM::Chat.new(
301
- model: @options[:summarization_model],
302
- context: @chat.provider.client.context, # Use same context (API keys, etc.)
303
- )
304
-
305
- summary_chat.with_instructions("You are a precise conversation summarization assistant.")
306
-
307
- response = summary_chat.ask(prompt)
308
- response.content
309
- rescue StandardError => e
310
- # If summarization fails, create a simple fallback summary
311
- LogStream.emit_error(e, source: "context_compactor", context: "generate_summary", agent: @agent_name)
312
- RubyLLM.logger.debug("ContextCompactor: Summarization failed: #{e.message}")
313
-
314
- <<~FALLBACK
315
- ## Summary
316
- Previous conversation involved multiple exchanges. Conversation compacted due to context limits.
317
-
318
- ## Note
319
- Summarization failed - continuing with reduced context. If critical information was lost,
320
- please ask the user to provide it again.
321
- FALLBACK
322
- end
323
-
324
- # Replace messages in the chat
325
- #
326
- # Delegates to the Chat's replace_messages method which provides
327
- # a safe abstraction over the internal message array.
328
- #
329
- # @param new_messages [Array<RubyLLM::Message>] New message array
330
- # @return [void]
331
- def replace_messages(new_messages)
332
- @chat.replace_messages(new_messages)
333
- end
334
- end
335
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module ContextManagement
5
- # DSL for defining context management handlers
6
- #
7
- # This builder provides a clean, idiomatic way to register handlers for
8
- # context warning thresholds. Handlers receive a rich context object
9
- # with message manipulation methods.
10
- #
11
- # @example Basic usage
12
- # context_management do
13
- # on :warning_60 do |ctx|
14
- # ctx.compress_tool_results(keep_recent: 10)
15
- # end
16
- #
17
- # on :warning_80 do |ctx|
18
- # ctx.prune_old_messages(keep_recent: 20)
19
- # end
20
- # end
21
- #
22
- # @example Progressive compression
23
- # context_management do
24
- # on :warning_60 do |ctx|
25
- # ctx.compress_tool_results(keep_recent: 15, truncate_to: 500)
26
- # end
27
- #
28
- # on :warning_80 do |ctx|
29
- # ctx.prune_old_messages(keep_recent: 30)
30
- # ctx.compress_tool_results(keep_recent: 5, truncate_to: 200)
31
- # end
32
- #
33
- # on :warning_90 do |ctx|
34
- # ctx.log_action("emergency_pruning", tokens_remaining: ctx.tokens_remaining)
35
- # ctx.prune_old_messages(keep_recent: 15)
36
- # end
37
- # end
38
- class Builder
39
- # Map semantic event names to threshold percentages
40
- EVENT_MAP = {
41
- warning_60: 60,
42
- warning_80: 80,
43
- warning_90: 90,
44
- }.freeze
45
-
46
- def initialize
47
- @handlers = {} # { threshold => block }
48
- end
49
-
50
- # Register a handler for a context warning threshold
51
- #
52
- # Handlers take full responsibility for managing context at their threshold.
53
- # When a handler is registered for a threshold, automatic compression is disabled
54
- # for that threshold.
55
- #
56
- # @param event [Symbol] Event name (:warning_60, :warning_80, :warning_90)
57
- # @yield [ContextManagement::Context] Context with message manipulation methods
58
- # @return [void]
59
- #
60
- # @raise [ArgumentError] If event is unknown or block is missing
61
- #
62
- # @example Compress tool results at 60%
63
- # on :warning_60 do |ctx|
64
- # ctx.compress_tool_results(keep_recent: 10)
65
- # end
66
- #
67
- # @example Custom logic at 80%
68
- # on :warning_80 do |ctx|
69
- # if ctx.usage_percentage > 85
70
- # ctx.prune_old_messages(keep_recent: 10)
71
- # else
72
- # ctx.summarize_old_exchanges(older_than: 20)
73
- # end
74
- # end
75
- #
76
- # @example Log and prune at 90%
77
- # on :warning_90 do |ctx|
78
- # ctx.log_action("critical_threshold", remaining: ctx.tokens_remaining)
79
- # ctx.prune_old_messages(keep_recent: 10)
80
- # end
81
- def on(event, &block)
82
- threshold = EVENT_MAP[event]
83
- raise ArgumentError, "Unknown event: #{event}. Valid events: #{EVENT_MAP.keys.join(", ")}" unless threshold
84
- raise ArgumentError, "Block required for #{event}" unless block
85
-
86
- @handlers[threshold] = block
87
- end
88
-
89
- # Build hook definitions from handlers
90
- #
91
- # Creates Hooks::Definition objects that wrap user blocks to provide
92
- # rich context objects instead of raw Hooks::Context. Each handler
93
- # becomes a hook for the :context_warning event.
94
- #
95
- # @return [Array<Hooks::Definition>] Hook definitions for :context_warning event
96
- def build
97
- @handlers.map do |threshold, user_block|
98
- # Create a hook that filters by threshold and wraps context
99
- Hooks::Definition.new(
100
- event: :context_warning,
101
- matcher: nil, # No tool matching needed
102
- priority: 0,
103
- proc: create_threshold_matcher(threshold, user_block),
104
- )
105
- end
106
- end
107
-
108
- private
109
-
110
- # Create a proc that matches threshold and wraps context
111
- #
112
- # @param target_threshold [Integer] Threshold to match (60, 80, 90)
113
- # @param user_block [Proc] User's handler block
114
- # @return [Proc] Hook proc
115
- def create_threshold_matcher(target_threshold, user_block)
116
- proc do |hooks_context|
117
- # Only execute for matching threshold
118
- current_threshold = hooks_context.metadata[:threshold]
119
- next unless current_threshold == target_threshold
120
-
121
- # Wrap in rich context object
122
- rich_context = Context.new(hooks_context)
123
- user_block.call(rich_context)
124
- end
125
- end
126
- end
127
- end
128
- end