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.
- checksums.yaml +4 -4
- data/lib/swarm_memory/version.rb +1 -1
- metadata +5 -184
- data/lib/claude_swarm/base_executor.rb +0 -133
- data/lib/claude_swarm/claude_code_executor.rb +0 -349
- data/lib/claude_swarm/claude_mcp_server.rb +0 -78
- data/lib/claude_swarm/cli.rb +0 -697
- data/lib/claude_swarm/commands/ps.rb +0 -215
- data/lib/claude_swarm/commands/show.rb +0 -139
- data/lib/claude_swarm/configuration.rb +0 -373
- data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
- data/lib/claude_swarm/json_handler.rb +0 -91
- data/lib/claude_swarm/mcp_generator.rb +0 -230
- data/lib/claude_swarm/openai/chat_completion.rb +0 -256
- data/lib/claude_swarm/openai/executor.rb +0 -256
- data/lib/claude_swarm/openai/responses.rb +0 -319
- data/lib/claude_swarm/orchestrator.rb +0 -878
- data/lib/claude_swarm/process_tracker.rb +0 -78
- data/lib/claude_swarm/session_cost_calculator.rb +0 -209
- data/lib/claude_swarm/session_path.rb +0 -42
- data/lib/claude_swarm/settings_generator.rb +0 -77
- data/lib/claude_swarm/system_utils.rb +0 -46
- data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
- data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
- data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
- data/lib/claude_swarm/tools/task_tool.rb +0 -63
- data/lib/claude_swarm/version.rb +0 -5
- data/lib/claude_swarm/worktree_manager.rb +0 -475
- data/lib/claude_swarm/yaml_loader.rb +0 -22
- data/lib/claude_swarm.rb +0 -67
- data/lib/swarm_cli/cli.rb +0 -201
- data/lib/swarm_cli/command_registry.rb +0 -61
- data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
- data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
- data/lib/swarm_cli/commands/migrate.rb +0 -55
- data/lib/swarm_cli/commands/run.rb +0 -173
- data/lib/swarm_cli/config_loader.rb +0 -98
- data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
- data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
- data/lib/swarm_cli/interactive_repl.rb +0 -924
- data/lib/swarm_cli/mcp_serve_options.rb +0 -44
- data/lib/swarm_cli/mcp_tools_options.rb +0 -59
- data/lib/swarm_cli/migrate_options.rb +0 -54
- data/lib/swarm_cli/migrator.rb +0 -132
- data/lib/swarm_cli/options.rb +0 -151
- data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
- data/lib/swarm_cli/ui/components/content_block.rb +0 -120
- data/lib/swarm_cli/ui/components/divider.rb +0 -57
- data/lib/swarm_cli/ui/components/panel.rb +0 -62
- data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
- data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
- data/lib/swarm_cli/ui/formatters/number.rb +0 -58
- data/lib/swarm_cli/ui/formatters/text.rb +0 -77
- data/lib/swarm_cli/ui/formatters/time.rb +0 -73
- data/lib/swarm_cli/ui/icons.rb +0 -36
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
- data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
- data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
- data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
- data/lib/swarm_cli/version.rb +0 -5
- data/lib/swarm_cli.rb +0 -46
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
- data/lib/swarm_sdk/agent/builder.rb +0 -552
- data/lib/swarm_sdk/agent/chat.rb +0 -774
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
- data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
- data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
- data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
- data/lib/swarm_sdk/agent/context.rb +0 -116
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -477
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- data/lib/swarm_sdk/builders/base_builder.rb +0 -409
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
- data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
- data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
- data/lib/swarm_sdk/concerns/validatable.rb +0 -55
- data/lib/swarm_sdk/configuration/parser.rb +0 -353
- data/lib/swarm_sdk/configuration/translator.rb +0 -255
- data/lib/swarm_sdk/configuration.rb +0 -135
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
- data/lib/swarm_sdk/context_compactor.rb +0 -335
- data/lib/swarm_sdk/context_management/builder.rb +0 -128
- data/lib/swarm_sdk/context_management/context.rb +0 -328
- data/lib/swarm_sdk/defaults.rb +0 -196
- data/lib/swarm_sdk/events_to_messages.rb +0 -199
- data/lib/swarm_sdk/hooks/adapter.rb +0 -359
- data/lib/swarm_sdk/hooks/context.rb +0 -197
- data/lib/swarm_sdk/hooks/definition.rb +0 -80
- data/lib/swarm_sdk/hooks/error.rb +0 -29
- data/lib/swarm_sdk/hooks/executor.rb +0 -146
- data/lib/swarm_sdk/hooks/registry.rb +0 -147
- data/lib/swarm_sdk/hooks/result.rb +0 -150
- data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
- data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
- data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
- data/lib/swarm_sdk/log_collector.rb +0 -227
- data/lib/swarm_sdk/log_stream.rb +0 -127
- data/lib/swarm_sdk/markdown_parser.rb +0 -75
- data/lib/swarm_sdk/model_aliases.json +0 -8
- data/lib/swarm_sdk/models.json +0 -1
- data/lib/swarm_sdk/models.rb +0 -120
- data/lib/swarm_sdk/node_context.rb +0 -245
- data/lib/swarm_sdk/observer/builder.rb +0 -81
- data/lib/swarm_sdk/observer/config.rb +0 -45
- data/lib/swarm_sdk/observer/manager.rb +0 -236
- data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
- data/lib/swarm_sdk/permissions/config.rb +0 -239
- data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
- data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
- data/lib/swarm_sdk/permissions/validator.rb +0 -173
- data/lib/swarm_sdk/permissions_builder.rb +0 -122
- data/lib/swarm_sdk/plugin.rb +0 -309
- data/lib/swarm_sdk/plugin_registry.rb +0 -101
- data/lib/swarm_sdk/proc_helpers.rb +0 -53
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
- data/lib/swarm_sdk/restore_result.rb +0 -65
- data/lib/swarm_sdk/result.rb +0 -123
- data/lib/swarm_sdk/snapshot.rb +0 -156
- data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
- data/lib/swarm_sdk/state_restorer.rb +0 -476
- data/lib/swarm_sdk/state_snapshot.rb +0 -334
- data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
- data/lib/swarm_sdk/swarm/builder.rb +0 -249
- data/lib/swarm_sdk/swarm/executor.rb +0 -213
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
- data/lib/swarm_sdk/swarm.rb +0 -717
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/bash.rb +0 -282
- data/lib/swarm_sdk/tools/clock.rb +0 -44
- data/lib/swarm_sdk/tools/delegate.rb +0 -267
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
- data/lib/swarm_sdk/tools/edit.rb +0 -145
- data/lib/swarm_sdk/tools/glob.rb +0 -166
- data/lib/swarm_sdk/tools/grep.rb +0 -235
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
- data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
- data/lib/swarm_sdk/tools/read.rb +0 -261
- data/lib/swarm_sdk/tools/registry.rb +0 -205
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
- data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
- data/lib/swarm_sdk/tools/think.rb +0 -98
- data/lib/swarm_sdk/tools/todo_write.rb +0 -235
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
- data/lib/swarm_sdk/tools/write.rb +0 -112
- data/lib/swarm_sdk/utils.rb +0 -68
- data/lib/swarm_sdk/validation_result.rb +0 -33
- data/lib/swarm_sdk/version.rb +0 -5
- data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
- data/lib/swarm_sdk/workflow/builder.rb +0 -143
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
- data/lib/swarm_sdk/workflow.rb +0 -554
- data/lib/swarm_sdk.rb +0 -524
|
@@ -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
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Hooks
|
|
5
|
-
# Translates YAML hooks configuration to Ruby hooks
|
|
6
|
-
#
|
|
7
|
-
# Adapter bridges the gap between declarative YAML hooks (shell commands)
|
|
8
|
-
# and SwarmSDK's internal hook system. It creates hooks that execute
|
|
9
|
-
# shell commands and translate exit codes to Result objects.
|
|
10
|
-
#
|
|
11
|
-
# ## YAML Hooks are YAML-Only
|
|
12
|
-
#
|
|
13
|
-
# Hooks are a **YAML-only feature** designed for users who want Claude Code-style
|
|
14
|
-
# shell command hooks. Users of the Ruby API should use hooks directly.
|
|
15
|
-
#
|
|
16
|
-
# ## Swarm-Level vs Agent-Level
|
|
17
|
-
#
|
|
18
|
-
# - **Swarm-level**: Only `swarm_start` and `swarm_stop` (lifecycle hooks)
|
|
19
|
-
# - **Agent-level**: All other events (per-agent or all_agents)
|
|
20
|
-
# - **all_agents**: Hooks applied as swarm defaults to all agents
|
|
21
|
-
#
|
|
22
|
-
# ## Event Naming
|
|
23
|
-
#
|
|
24
|
-
# Uses snake_case to match internal hook events directly (no translation):
|
|
25
|
-
# - `pre_tool_use` → :pre_tool_use
|
|
26
|
-
# - `swarm_start` → :swarm_start
|
|
27
|
-
# - etc.
|
|
28
|
-
#
|
|
29
|
-
# @example YAML configuration
|
|
30
|
-
# swarm:
|
|
31
|
-
# hooks:
|
|
32
|
-
# swarm_start:
|
|
33
|
-
# - hooks:
|
|
34
|
-
# - type: command
|
|
35
|
-
# command: "echo 'Starting swarm'"
|
|
36
|
-
#
|
|
37
|
-
# all_agents:
|
|
38
|
-
# hooks:
|
|
39
|
-
# pre_tool_use:
|
|
40
|
-
# - matcher: "Write|Edit"
|
|
41
|
-
# hooks:
|
|
42
|
-
# - type: command
|
|
43
|
-
# command: "rubocop --stdin"
|
|
44
|
-
#
|
|
45
|
-
# agents:
|
|
46
|
-
# backend:
|
|
47
|
-
# hooks:
|
|
48
|
-
# pre_tool_use:
|
|
49
|
-
# - matcher: "Bash"
|
|
50
|
-
# hooks:
|
|
51
|
-
# - type: command
|
|
52
|
-
# command: "python validate_bash.py"
|
|
53
|
-
class Adapter
|
|
54
|
-
# Swarm-level events (only these allowed at swarm.hooks level)
|
|
55
|
-
SWARM_LEVEL_EVENTS = [:swarm_start, :swarm_stop].freeze
|
|
56
|
-
|
|
57
|
-
# Agent-level events (allowed in all_agents.hooks and agent.hooks)
|
|
58
|
-
AGENT_LEVEL_EVENTS = [
|
|
59
|
-
:pre_tool_use,
|
|
60
|
-
:post_tool_use,
|
|
61
|
-
:user_prompt,
|
|
62
|
-
:agent_step,
|
|
63
|
-
:agent_stop,
|
|
64
|
-
:first_message,
|
|
65
|
-
:pre_delegation,
|
|
66
|
-
:post_delegation,
|
|
67
|
-
:context_warning,
|
|
68
|
-
].freeze
|
|
69
|
-
|
|
70
|
-
class << self
|
|
71
|
-
# Apply hooks from YAML configuration to swarm
|
|
72
|
-
#
|
|
73
|
-
# This is called automatically by Swarm.load after creating the swarm instance.
|
|
74
|
-
# It translates YAML hooks into hooks that execute shell commands.
|
|
75
|
-
#
|
|
76
|
-
# @param swarm [Swarm] Swarm instance to configure
|
|
77
|
-
# @param config [Configuration] Parsed YAML configuration
|
|
78
|
-
# @return [void]
|
|
79
|
-
def apply_hooks(swarm, config)
|
|
80
|
-
# 1. Apply swarm-level hooks (from swarm.hooks)
|
|
81
|
-
apply_swarm_hooks(swarm, config.swarm_hooks) if config.swarm_hooks&.any?
|
|
82
|
-
|
|
83
|
-
# 2. Apply all_agents hooks (as swarm defaults)
|
|
84
|
-
apply_all_agents_hooks(swarm, config.all_agents_hooks) if config.all_agents_hooks&.any?
|
|
85
|
-
|
|
86
|
-
# 3. Store agent hooks for later application (after agents are initialized)
|
|
87
|
-
store_agent_hooks(config)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Apply agent-specific hooks to an already-initialized agent
|
|
91
|
-
#
|
|
92
|
-
# This is called during agent initialization for each agent that has hooks configured.
|
|
93
|
-
#
|
|
94
|
-
# @param agent [AgentChat] Agent instance
|
|
95
|
-
# @param agent_name [Symbol] Agent name
|
|
96
|
-
# @param hooks_config [Hash] Hooks configuration from YAML
|
|
97
|
-
# @param swarm_name [String] Swarm name for environment variables
|
|
98
|
-
# @return [void]
|
|
99
|
-
def apply_agent_hooks(agent, agent_name, hooks_config, swarm_name)
|
|
100
|
-
return unless hooks_config&.any?
|
|
101
|
-
|
|
102
|
-
hooks_config.each do |event_name, hook_defs|
|
|
103
|
-
event_symbol = event_name.to_sym
|
|
104
|
-
validate_agent_event!(event_symbol)
|
|
105
|
-
|
|
106
|
-
# Each hook def can have optional matcher
|
|
107
|
-
Array(hook_defs).each do |hook_def|
|
|
108
|
-
matcher = hook_def[:matcher] || hook_def["matcher"]
|
|
109
|
-
hook = create_hook_callback(hook_def, event_symbol, agent_name, swarm_name)
|
|
110
|
-
agent.add_hook(event_symbol, matcher: matcher, &hook)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
private
|
|
116
|
-
|
|
117
|
-
# Apply swarm-level hooks (swarm_start, swarm_stop)
|
|
118
|
-
#
|
|
119
|
-
# @param swarm [Swarm] Swarm instance
|
|
120
|
-
# @param hooks_config [Hash] Hooks configuration
|
|
121
|
-
def apply_swarm_hooks(swarm, hooks_config)
|
|
122
|
-
hooks_config.each do |event_name, hook_defs|
|
|
123
|
-
event_symbol = event_name.to_sym
|
|
124
|
-
validate_swarm_event!(event_symbol)
|
|
125
|
-
|
|
126
|
-
# Each hook def is a direct hash with type, command, timeout
|
|
127
|
-
Array(hook_defs).each do |hook_def|
|
|
128
|
-
hook = create_swarm_hook_callback(hook_def, event_symbol, swarm.name)
|
|
129
|
-
swarm.add_default_callback(event_symbol, &hook)
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Apply all_agents hooks as swarm defaults
|
|
135
|
-
#
|
|
136
|
-
# @param swarm [Swarm] Swarm instance
|
|
137
|
-
# @param hooks_config [Hash] Hooks configuration
|
|
138
|
-
def apply_all_agents_hooks(swarm, hooks_config)
|
|
139
|
-
hooks_config.each do |event_name, hook_defs|
|
|
140
|
-
event_symbol = event_name.to_sym
|
|
141
|
-
validate_agent_event!(event_symbol)
|
|
142
|
-
|
|
143
|
-
# Each hook def can have optional matcher
|
|
144
|
-
Array(hook_defs).each do |hook_def|
|
|
145
|
-
matcher = hook_def[:matcher] || hook_def["matcher"]
|
|
146
|
-
hook = create_all_agents_hook_callback(hook_def, event_symbol, swarm.name)
|
|
147
|
-
swarm.add_default_callback(event_symbol, matcher: matcher, &hook)
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Store agent hooks in Configuration for later application
|
|
153
|
-
#
|
|
154
|
-
# @param config [Configuration] Configuration instance
|
|
155
|
-
def store_agent_hooks(config)
|
|
156
|
-
# Agent hooks are already stored in AgentDefinition
|
|
157
|
-
# They'll be applied during agent initialization
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Create a hook for agent-level hooks
|
|
161
|
-
#
|
|
162
|
-
# @param hook_def [Hash] Hook definition from YAML
|
|
163
|
-
# @param event_symbol [Symbol] Event type
|
|
164
|
-
# @param agent_name [Symbol, String] Agent name
|
|
165
|
-
# @param swarm_name [String] Swarm name
|
|
166
|
-
# @return [Proc] Hook callback
|
|
167
|
-
def create_hook_callback(hook_def, event_symbol, agent_name, swarm_name)
|
|
168
|
-
# Support both string and symbol keys (YAML may be symbolized)
|
|
169
|
-
command = hook_def[:command] || hook_def["command"]
|
|
170
|
-
timeout = hook_def[:timeout] || hook_def["timeout"] || ShellExecutor::DEFAULT_TIMEOUT
|
|
171
|
-
|
|
172
|
-
lambda do |context|
|
|
173
|
-
input_json = build_input_json(context, event_symbol, agent_name)
|
|
174
|
-
ShellExecutor.execute(
|
|
175
|
-
command: command,
|
|
176
|
-
input_json: input_json,
|
|
177
|
-
timeout: timeout,
|
|
178
|
-
agent_name: agent_name,
|
|
179
|
-
swarm_name: swarm_name,
|
|
180
|
-
event: event_symbol,
|
|
181
|
-
)
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Create a hook for all_agents hooks
|
|
186
|
-
#
|
|
187
|
-
# @param hook_def [Hash] Hook definition from YAML
|
|
188
|
-
# @param event_symbol [Symbol] Event type
|
|
189
|
-
# @param swarm_name [String] Swarm name
|
|
190
|
-
# @return [Proc] Hook callback
|
|
191
|
-
def create_all_agents_hook_callback(hook_def, event_symbol, swarm_name)
|
|
192
|
-
# Support both string and symbol keys (YAML may be symbolized)
|
|
193
|
-
command = hook_def[:command] || hook_def["command"]
|
|
194
|
-
timeout = hook_def[:timeout] || hook_def["timeout"] || ShellExecutor::DEFAULT_TIMEOUT
|
|
195
|
-
|
|
196
|
-
lambda do |context|
|
|
197
|
-
# Agent name comes from context
|
|
198
|
-
agent_name = context.agent_name
|
|
199
|
-
input_json = build_input_json(context, event_symbol, agent_name)
|
|
200
|
-
ShellExecutor.execute(
|
|
201
|
-
command: command,
|
|
202
|
-
input_json: input_json,
|
|
203
|
-
timeout: timeout,
|
|
204
|
-
agent_name: agent_name,
|
|
205
|
-
swarm_name: swarm_name,
|
|
206
|
-
event: event_symbol,
|
|
207
|
-
)
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Create a hook for swarm-level hooks
|
|
212
|
-
#
|
|
213
|
-
# @param hook_def [Hash] Hook definition from YAML
|
|
214
|
-
# @param event_symbol [Symbol] Event type
|
|
215
|
-
# @param swarm_name [String] Swarm name
|
|
216
|
-
# @return [Proc] Hook callback
|
|
217
|
-
def create_swarm_hook_callback(hook_def, event_symbol, swarm_name)
|
|
218
|
-
# Support both string and symbol keys (YAML may be symbolized)
|
|
219
|
-
command = hook_def[:command] || hook_def["command"]
|
|
220
|
-
timeout = hook_def[:timeout] || hook_def["timeout"] || ShellExecutor::DEFAULT_TIMEOUT
|
|
221
|
-
|
|
222
|
-
lambda do |context|
|
|
223
|
-
input_json = build_swarm_input_json(context, event_symbol, swarm_name)
|
|
224
|
-
ShellExecutor.execute(
|
|
225
|
-
command: command,
|
|
226
|
-
input_json: input_json,
|
|
227
|
-
timeout: timeout,
|
|
228
|
-
agent_name: nil,
|
|
229
|
-
swarm_name: swarm_name,
|
|
230
|
-
event: event_symbol,
|
|
231
|
-
)
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# Build JSON input for agent-level hook scripts
|
|
236
|
-
#
|
|
237
|
-
# @param context [Context] Hook context
|
|
238
|
-
# @param event_symbol [Symbol] Event type
|
|
239
|
-
# @param agent_name [Symbol, String] Agent name
|
|
240
|
-
# @return [Hash] JSON input for hook script
|
|
241
|
-
def build_input_json(context, event_symbol, agent_name)
|
|
242
|
-
base = {
|
|
243
|
-
event: event_symbol.to_s,
|
|
244
|
-
agent: agent_name.to_s,
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
# Add event-specific data
|
|
248
|
-
case event_symbol
|
|
249
|
-
when :pre_tool_use
|
|
250
|
-
base.merge(
|
|
251
|
-
tool: context.tool_call.name,
|
|
252
|
-
parameters: context.tool_call.parameters,
|
|
253
|
-
)
|
|
254
|
-
when :post_tool_use
|
|
255
|
-
# In post_tool_use, we only have tool_result, not tool_call
|
|
256
|
-
# Need to extract tool info from metadata or tool_result
|
|
257
|
-
base.merge(
|
|
258
|
-
result: context.tool_result.content,
|
|
259
|
-
success: context.tool_result.success?,
|
|
260
|
-
tool_call_id: context.tool_result.tool_call_id,
|
|
261
|
-
)
|
|
262
|
-
when :pre_delegation
|
|
263
|
-
base.merge(
|
|
264
|
-
delegation_target: context.delegation_target,
|
|
265
|
-
task: context.metadata[:task],
|
|
266
|
-
)
|
|
267
|
-
when :post_delegation
|
|
268
|
-
base.merge(
|
|
269
|
-
delegation_target: context.delegation_target,
|
|
270
|
-
task: context.metadata[:task],
|
|
271
|
-
result: context.delegation_result,
|
|
272
|
-
)
|
|
273
|
-
when :user_prompt
|
|
274
|
-
base.merge(
|
|
275
|
-
prompt: context.metadata[:prompt],
|
|
276
|
-
message_count: context.metadata[:message_count],
|
|
277
|
-
)
|
|
278
|
-
when :agent_step
|
|
279
|
-
base.merge(
|
|
280
|
-
content: context.metadata[:content],
|
|
281
|
-
tool_calls: context.metadata[:tool_calls],
|
|
282
|
-
finish_reason: context.metadata[:finish_reason],
|
|
283
|
-
usage: context.metadata[:usage],
|
|
284
|
-
)
|
|
285
|
-
when :agent_stop
|
|
286
|
-
base.merge(
|
|
287
|
-
content: context.metadata[:content],
|
|
288
|
-
finish_reason: context.metadata[:finish_reason],
|
|
289
|
-
usage: context.metadata[:usage],
|
|
290
|
-
)
|
|
291
|
-
when :first_message
|
|
292
|
-
base.merge(prompt: context.metadata[:prompt])
|
|
293
|
-
when :context_warning
|
|
294
|
-
base.merge(
|
|
295
|
-
threshold: context.metadata[:threshold],
|
|
296
|
-
percentage: context.metadata[:percentage],
|
|
297
|
-
tokens_used: context.metadata[:tokens_used],
|
|
298
|
-
tokens_remaining: context.metadata[:tokens_remaining],
|
|
299
|
-
)
|
|
300
|
-
else
|
|
301
|
-
base
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# Build JSON input for swarm-level hook scripts
|
|
306
|
-
#
|
|
307
|
-
# @param context [Context] Hook context
|
|
308
|
-
# @param event_symbol [Symbol] Event type
|
|
309
|
-
# @param swarm_name [String] Swarm name
|
|
310
|
-
# @return [Hash] JSON input for hook script
|
|
311
|
-
def build_swarm_input_json(context, event_symbol, swarm_name)
|
|
312
|
-
base = {
|
|
313
|
-
event: event_symbol.to_s,
|
|
314
|
-
swarm: swarm_name,
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
case event_symbol
|
|
318
|
-
when :swarm_start, :first_message
|
|
319
|
-
base.merge(prompt: context.metadata[:prompt])
|
|
320
|
-
when :swarm_stop
|
|
321
|
-
base.merge(
|
|
322
|
-
success: context.metadata[:success],
|
|
323
|
-
duration: context.metadata[:duration],
|
|
324
|
-
total_cost: context.metadata[:total_cost],
|
|
325
|
-
total_tokens: context.metadata[:total_tokens],
|
|
326
|
-
)
|
|
327
|
-
else
|
|
328
|
-
base
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# Validate swarm-level event
|
|
333
|
-
#
|
|
334
|
-
# @param event [Symbol] Event to validate
|
|
335
|
-
# @raise [ConfigurationError] if event invalid for swarm level
|
|
336
|
-
def validate_swarm_event!(event)
|
|
337
|
-
return if SWARM_LEVEL_EVENTS.include?(event)
|
|
338
|
-
|
|
339
|
-
raise ConfigurationError,
|
|
340
|
-
"Invalid swarm-level hook event: #{event}. " \
|
|
341
|
-
"Only #{SWARM_LEVEL_EVENTS.join(", ")} are allowed at swarm.hooks level. " \
|
|
342
|
-
"Use all_agents.hooks or agent.hooks for other events."
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# Validate agent-level event
|
|
346
|
-
#
|
|
347
|
-
# @param event [Symbol] Event to validate
|
|
348
|
-
# @raise [ConfigurationError] if event invalid for agent level
|
|
349
|
-
def validate_agent_event!(event)
|
|
350
|
-
return if AGENT_LEVEL_EVENTS.include?(event)
|
|
351
|
-
|
|
352
|
-
raise ConfigurationError,
|
|
353
|
-
"Invalid agent-level hook event: #{event}. " \
|
|
354
|
-
"Valid events: #{AGENT_LEVEL_EVENTS.join(", ")}"
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
end
|