swarm_memory 2.1.4 → 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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. data/lib/swarm_memory.rb +7 -2
  4. metadata +6 -185
  5. data/lib/claude_swarm/base_executor.rb +0 -133
  6. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  7. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  8. data/lib/claude_swarm/cli.rb +0 -697
  9. data/lib/claude_swarm/commands/ps.rb +0 -215
  10. data/lib/claude_swarm/commands/show.rb +0 -139
  11. data/lib/claude_swarm/configuration.rb +0 -373
  12. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  13. data/lib/claude_swarm/json_handler.rb +0 -91
  14. data/lib/claude_swarm/mcp_generator.rb +0 -243
  15. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  16. data/lib/claude_swarm/openai/executor.rb +0 -256
  17. data/lib/claude_swarm/openai/responses.rb +0 -319
  18. data/lib/claude_swarm/orchestrator.rb +0 -878
  19. data/lib/claude_swarm/process_tracker.rb +0 -78
  20. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  21. data/lib/claude_swarm/session_path.rb +0 -42
  22. data/lib/claude_swarm/settings_generator.rb +0 -77
  23. data/lib/claude_swarm/system_utils.rb +0 -46
  24. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  25. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  27. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  28. data/lib/claude_swarm/version.rb +0 -5
  29. data/lib/claude_swarm/worktree_manager.rb +0 -475
  30. data/lib/claude_swarm/yaml_loader.rb +0 -22
  31. data/lib/claude_swarm.rb +0 -67
  32. data/lib/swarm_cli/cli.rb +0 -201
  33. data/lib/swarm_cli/command_registry.rb +0 -61
  34. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  35. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  36. data/lib/swarm_cli/commands/migrate.rb +0 -55
  37. data/lib/swarm_cli/commands/run.rb +0 -173
  38. data/lib/swarm_cli/config_loader.rb +0 -98
  39. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  40. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  41. data/lib/swarm_cli/interactive_repl.rb +0 -924
  42. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  43. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  44. data/lib/swarm_cli/migrate_options.rb +0 -54
  45. data/lib/swarm_cli/migrator.rb +0 -132
  46. data/lib/swarm_cli/options.rb +0 -151
  47. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  48. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  49. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  50. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  51. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  52. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  53. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  54. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  55. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  56. data/lib/swarm_cli/ui/icons.rb +0 -36
  57. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  58. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  59. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  60. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  61. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  62. data/lib/swarm_cli/version.rb +0 -5
  63. data/lib/swarm_cli.rb +0 -46
  64. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  65. data/lib/swarm_sdk/agent/builder.rb +0 -552
  66. data/lib/swarm_sdk/agent/chat.rb +0 -774
  67. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  68. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  69. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  70. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  71. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  72. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  73. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  75. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  76. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  77. data/lib/swarm_sdk/agent/context.rb +0 -116
  78. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  79. data/lib/swarm_sdk/agent/definition.rb +0 -477
  80. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  81. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  82. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  83. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  84. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  85. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  86. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  87. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  88. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  89. data/lib/swarm_sdk/configuration.rb +0 -135
  90. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  91. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  92. data/lib/swarm_sdk/context_compactor.rb +0 -335
  93. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  94. data/lib/swarm_sdk/context_management/context.rb +0 -328
  95. data/lib/swarm_sdk/defaults.rb +0 -196
  96. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  97. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  98. data/lib/swarm_sdk/hooks/context.rb +0 -197
  99. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  100. data/lib/swarm_sdk/hooks/error.rb +0 -29
  101. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  102. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  103. data/lib/swarm_sdk/hooks/result.rb +0 -150
  104. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  105. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  106. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  107. data/lib/swarm_sdk/log_collector.rb +0 -227
  108. data/lib/swarm_sdk/log_stream.rb +0 -127
  109. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  110. data/lib/swarm_sdk/model_aliases.json +0 -8
  111. data/lib/swarm_sdk/models.json +0 -1
  112. data/lib/swarm_sdk/models.rb +0 -120
  113. data/lib/swarm_sdk/node_context.rb +0 -245
  114. data/lib/swarm_sdk/observer/builder.rb +0 -81
  115. data/lib/swarm_sdk/observer/config.rb +0 -45
  116. data/lib/swarm_sdk/observer/manager.rb +0 -236
  117. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  118. data/lib/swarm_sdk/permissions/config.rb +0 -239
  119. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  120. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  121. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  122. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  123. data/lib/swarm_sdk/plugin.rb +0 -309
  124. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  125. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  126. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  127. data/lib/swarm_sdk/restore_result.rb +0 -65
  128. data/lib/swarm_sdk/result.rb +0 -123
  129. data/lib/swarm_sdk/snapshot.rb +0 -156
  130. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  131. data/lib/swarm_sdk/state_restorer.rb +0 -476
  132. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  133. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  134. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  135. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  136. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  137. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  138. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  139. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  140. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  141. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  142. data/lib/swarm_sdk/swarm.rb +0 -717
  143. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  144. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  145. data/lib/swarm_sdk/tools/bash.rb +0 -282
  146. data/lib/swarm_sdk/tools/clock.rb +0 -44
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  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 -163
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  160. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  161. data/lib/swarm_sdk/tools/read.rb +0 -261
  162. data/lib/swarm_sdk/tools/registry.rb +0 -205
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  166. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  167. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  168. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  169. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  170. data/lib/swarm_sdk/tools/think.rb +0 -98
  171. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  172. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  173. data/lib/swarm_sdk/tools/write.rb +0 -112
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  182. data/lib/swarm_sdk/workflow.rb +0 -554
  183. data/lib/swarm_sdk.rb +0 -524
  184. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -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