swarm_sdk 2.7.13 → 3.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
- data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
- data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
- data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
- data/lib/swarm_sdk/v3/agent.rb +1165 -0
- data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
- data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
- data/lib/swarm_sdk/v3/configuration.rb +490 -0
- data/lib/swarm_sdk/v3/debug_log.rb +86 -0
- data/lib/swarm_sdk/v3/event_stream.rb +130 -0
- data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
- data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
- data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
- data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
- data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
- data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
- data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
- data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
- data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
- data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
- data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
- data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
- data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
- data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
- data/lib/swarm_sdk/v3/memory/card.rb +206 -0
- data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
- data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
- data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
- data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
- data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
- data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
- data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
- data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
- data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
- data/lib/swarm_sdk/v3/memory/store.rb +489 -0
- data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
- data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
- data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
- data/lib/swarm_sdk/v3/tools/base.rb +80 -0
- data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
- data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
- data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
- data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
- data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
- data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
- data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
- data/lib/swarm_sdk/v3/tools/read.rb +181 -0
- data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
- data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
- data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
- data/lib/swarm_sdk/v3/tools/think.rb +88 -0
- data/lib/swarm_sdk/v3/tools/write.rb +87 -0
- data/lib/swarm_sdk/v3.rb +145 -0
- metadata +84 -148
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
- data/lib/swarm_sdk/agent/builder.rb +0 -680
- data/lib/swarm_sdk/agent/chat.rb +0 -1432
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
- 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 -85
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
- 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 -134
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
- data/lib/swarm_sdk/agent/context.rb +0 -115
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -581
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
- data/lib/swarm_sdk/agent_registry.rb +0 -146
- data/lib/swarm_sdk/builders/base_builder.rb +0 -553
- 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/config.rb +0 -367
- data/lib/swarm_sdk/configuration/parser.rb +0 -397
- data/lib/swarm_sdk/configuration/translator.rb +0 -283
- data/lib/swarm_sdk/configuration.rb +0 -165
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
- 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/custom_tool_registry.rb +0 -226
- data/lib/swarm_sdk/defaults.rb +0 -251
- 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 -256
- 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 -44002
- data/lib/swarm_sdk/models.rb +0 -161
- 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 -212
- 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 -648
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -195
- data/lib/swarm_sdk/swarm/builder.rb +0 -256
- data/lib/swarm_sdk/swarm/executor.rb +0 -290
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
- data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
- data/lib/swarm_sdk/swarm.rb +0 -843
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/base.rb +0 -63
- data/lib/swarm_sdk/tools/bash.rb +0 -280
- data/lib/swarm_sdk/tools/clock.rb +0 -46
- data/lib/swarm_sdk/tools/delegate.rb +0 -389
- 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 -167
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
- 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 -273
- 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 -100
- data/lib/swarm_sdk/tools/todo_write.rb +0 -237
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
- data/lib/swarm_sdk/tools/write.rb +0 -112
- data/lib/swarm_sdk/transcript_builder.rb +0 -278
- 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 -95
- data/lib/swarm_sdk/workflow/builder.rb +0 -227
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
- data/lib/swarm_sdk/workflow.rb +0 -589
- data/lib/swarm_sdk.rb +0 -718
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
module Tools
|
|
5
|
-
# Write tool for writing content to files
|
|
6
|
-
#
|
|
7
|
-
# Creates new files or overwrites existing files.
|
|
8
|
-
# Enforces read-before-write rule for existing files.
|
|
9
|
-
# Includes validation and usage guidelines via system reminders.
|
|
10
|
-
class Write < Base
|
|
11
|
-
include PathResolver
|
|
12
|
-
|
|
13
|
-
# Factory pattern: declare what parameters this tool needs for instantiation
|
|
14
|
-
class << self
|
|
15
|
-
def creation_requirements
|
|
16
|
-
[:agent_name, :directory]
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
description <<~DESC
|
|
21
|
-
Writes a file to the local filesystem.
|
|
22
|
-
This tool will overwrite the existing file if there is one at the provided path.
|
|
23
|
-
If this is an existing file, you MUST use the Read tool first to read the file's contents.
|
|
24
|
-
This tool will fail if you did not read the file first.
|
|
25
|
-
ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
|
|
26
|
-
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
|
|
27
|
-
Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
|
|
28
|
-
|
|
29
|
-
IMPORTANT - Path Handling:
|
|
30
|
-
- Relative paths (e.g., "tmp/file.txt", "src/main.rb") are resolved relative to your agent's working directory
|
|
31
|
-
- Absolute paths (e.g., "/tmp/file.txt", "/etc/passwd") are treated as system absolute paths
|
|
32
|
-
- When the user says "tmp/file.txt" they mean the tmp directory in your working directory, NOT /tmp
|
|
33
|
-
- Only use absolute paths (starting with /) when explicitly referring to system-level paths
|
|
34
|
-
DESC
|
|
35
|
-
|
|
36
|
-
param :file_path,
|
|
37
|
-
type: "string",
|
|
38
|
-
desc: "Path to the file. Use relative paths (e.g., 'tmp/file.txt') for files in your working directory, or absolute paths (e.g., '/etc/passwd') for system files.",
|
|
39
|
-
required: true
|
|
40
|
-
|
|
41
|
-
param :content,
|
|
42
|
-
type: "string",
|
|
43
|
-
desc: "The content to write to the file",
|
|
44
|
-
required: true
|
|
45
|
-
|
|
46
|
-
# Initialize the Write tool for a specific agent
|
|
47
|
-
#
|
|
48
|
-
# @param agent_name [Symbol, String] The agent identifier
|
|
49
|
-
# @param directory [String] Agent's working directory
|
|
50
|
-
def initialize(agent_name:, directory:)
|
|
51
|
-
super()
|
|
52
|
-
initialize_agent_context(agent_name: agent_name, directory: directory)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Override name to return simple "Write" instead of full class path
|
|
56
|
-
def name
|
|
57
|
-
"Write"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def execute(file_path:, content:)
|
|
61
|
-
# Validate inputs
|
|
62
|
-
return validation_error("file_path is required") if file_path.nil? || file_path.to_s.strip.empty?
|
|
63
|
-
return validation_error("content is required") if content.nil?
|
|
64
|
-
|
|
65
|
-
# CRITICAL: Resolve path against agent directory
|
|
66
|
-
resolved_path = resolve_path(file_path)
|
|
67
|
-
|
|
68
|
-
# Check if file already exists (use resolved path)
|
|
69
|
-
file_exists = File.exist?(resolved_path)
|
|
70
|
-
|
|
71
|
-
# Enforce read-before-write for existing files (use resolved path)
|
|
72
|
-
if file_exists && !Stores::ReadTracker.file_read?(@agent_name, resolved_path)
|
|
73
|
-
return validation_error(
|
|
74
|
-
"Cannot write to existing file without reading it first. " \
|
|
75
|
-
"You must use the Read tool on '#{file_path}' before overwriting it. " \
|
|
76
|
-
"This ensures you have context about the file's current contents.",
|
|
77
|
-
)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Create parent directory if it doesn't exist (use resolved path)
|
|
81
|
-
parent_dir = File.dirname(resolved_path)
|
|
82
|
-
FileUtils.mkdir_p(parent_dir) unless File.directory?(parent_dir)
|
|
83
|
-
|
|
84
|
-
# Write the file (use resolved path)
|
|
85
|
-
File.write(resolved_path, content, encoding: "UTF-8")
|
|
86
|
-
|
|
87
|
-
# Build success message
|
|
88
|
-
byte_size = content.bytesize
|
|
89
|
-
line_count = content.lines.count
|
|
90
|
-
action = file_exists ? "overwrote" : "created"
|
|
91
|
-
|
|
92
|
-
message = "Successfully #{action} file: #{file_path} (#{line_count} lines, #{byte_size} bytes)"
|
|
93
|
-
|
|
94
|
-
# Add system reminder for overwritten files
|
|
95
|
-
if file_exists
|
|
96
|
-
reminder = "<system-reminder>You overwrote an existing file. Make sure this was intentional and that you read the file first if you needed to preserve any content.</system-reminder>"
|
|
97
|
-
"#{message}\n\n#{reminder}"
|
|
98
|
-
else
|
|
99
|
-
message
|
|
100
|
-
end
|
|
101
|
-
rescue Errno::EACCES
|
|
102
|
-
error("Permission denied: Cannot write to file '#{file_path}'")
|
|
103
|
-
rescue Errno::EISDIR
|
|
104
|
-
error("Path is a directory, not a file.")
|
|
105
|
-
rescue Errno::ENOENT => e
|
|
106
|
-
error("Failed to create parent directory: #{e.message}")
|
|
107
|
-
rescue StandardError => e
|
|
108
|
-
error("Unexpected error writing file: #{e.class.name} - #{e.message}")
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# Transforms raw log events into LLM-readable conversation transcripts
|
|
5
|
-
#
|
|
6
|
-
# TranscriptBuilder converts the structured event log from swarm execution
|
|
7
|
-
# into a human/LLM-readable format suitable for reflection, analysis, or
|
|
8
|
-
# memory creation.
|
|
9
|
-
#
|
|
10
|
-
# @example Basic usage
|
|
11
|
-
# transcript = TranscriptBuilder.build(result.logs)
|
|
12
|
-
# # => "USER: Help me with CORS\n\nAGENT [assistant]: ..."
|
|
13
|
-
#
|
|
14
|
-
# @example Filter by agents
|
|
15
|
-
# transcript = TranscriptBuilder.build(result.logs, agents: [:backend, :database])
|
|
16
|
-
#
|
|
17
|
-
# @example Custom options
|
|
18
|
-
# transcript = TranscriptBuilder.build(
|
|
19
|
-
# result.logs,
|
|
20
|
-
# include_tool_results: true,
|
|
21
|
-
# max_result_length: 1000,
|
|
22
|
-
# include_thinking: false
|
|
23
|
-
# )
|
|
24
|
-
class TranscriptBuilder
|
|
25
|
-
# Default maximum length for tool result content
|
|
26
|
-
DEFAULT_MAX_RESULT_LENGTH = 500
|
|
27
|
-
|
|
28
|
-
# Default maximum length for tool arguments
|
|
29
|
-
DEFAULT_MAX_ARGS_LENGTH = 200
|
|
30
|
-
|
|
31
|
-
class << self
|
|
32
|
-
# Build a transcript from log events
|
|
33
|
-
#
|
|
34
|
-
# @param logs [Array<Hash>] Array of log events from Result#logs
|
|
35
|
-
# @param agents [Array<Symbol>, nil] Filter to specific agents (nil = all)
|
|
36
|
-
# @param include_tool_results [Boolean] Include tool execution results (default: true)
|
|
37
|
-
# @param include_thinking [Boolean] Include agent_step content/thinking (default: false)
|
|
38
|
-
# @param max_result_length [Integer] Maximum characters for tool results
|
|
39
|
-
# @param max_args_length [Integer] Maximum characters for tool arguments
|
|
40
|
-
# @return [String] Formatted transcript
|
|
41
|
-
def build(logs, agents: nil, include_tool_results: true, include_thinking: false,
|
|
42
|
-
max_result_length: DEFAULT_MAX_RESULT_LENGTH, max_args_length: DEFAULT_MAX_ARGS_LENGTH)
|
|
43
|
-
new(
|
|
44
|
-
logs,
|
|
45
|
-
agents: agents,
|
|
46
|
-
include_tool_results: include_tool_results,
|
|
47
|
-
include_thinking: include_thinking,
|
|
48
|
-
max_result_length: max_result_length,
|
|
49
|
-
max_args_length: max_args_length,
|
|
50
|
-
).build
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Initialize a new TranscriptBuilder
|
|
55
|
-
#
|
|
56
|
-
# @param logs [Array<Hash>] Array of log events
|
|
57
|
-
# @param agents [Array<Symbol>, nil] Filter to specific agents
|
|
58
|
-
# @param include_tool_results [Boolean] Include tool execution results
|
|
59
|
-
# @param include_thinking [Boolean] Include agent_step content
|
|
60
|
-
# @param max_result_length [Integer] Maximum characters for tool results
|
|
61
|
-
# @param max_args_length [Integer] Maximum characters for tool arguments
|
|
62
|
-
def initialize(logs, agents: nil, include_tool_results: true, include_thinking: false,
|
|
63
|
-
max_result_length: DEFAULT_MAX_RESULT_LENGTH, max_args_length: DEFAULT_MAX_ARGS_LENGTH)
|
|
64
|
-
@logs = logs || []
|
|
65
|
-
@agents = normalize_agents(agents)
|
|
66
|
-
@include_tool_results = include_tool_results
|
|
67
|
-
@include_thinking = include_thinking
|
|
68
|
-
@max_result_length = max_result_length
|
|
69
|
-
@max_args_length = max_args_length
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Build the transcript
|
|
73
|
-
#
|
|
74
|
-
# @return [String] Formatted transcript
|
|
75
|
-
def build
|
|
76
|
-
@logs
|
|
77
|
-
.filter_map { |event| format_event(event) }
|
|
78
|
-
.join("\n\n")
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
private
|
|
82
|
-
|
|
83
|
-
# Normalize agent filter to array of symbols
|
|
84
|
-
#
|
|
85
|
-
# @param agents [Array, Symbol, String, nil] Agent filter input
|
|
86
|
-
# @return [Array<Symbol>, nil] Normalized agent list or nil for all
|
|
87
|
-
def normalize_agents(agents)
|
|
88
|
-
return if agents.nil? || (agents.is_a?(Array) && agents.empty?)
|
|
89
|
-
|
|
90
|
-
Array(agents).map(&:to_sym)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Check if event passes agent filter
|
|
94
|
-
#
|
|
95
|
-
# @param event [Hash] Log event
|
|
96
|
-
# @return [Boolean] True if event should be included
|
|
97
|
-
def passes_agent_filter?(event)
|
|
98
|
-
return true if @agents.nil?
|
|
99
|
-
|
|
100
|
-
agent = event[:agent] || event["agent"]
|
|
101
|
-
return true if agent.nil? # Include events without agent (like swarm_start)
|
|
102
|
-
|
|
103
|
-
@agents.include?(agent.to_sym)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Format a single event into transcript text
|
|
107
|
-
#
|
|
108
|
-
# @param event [Hash] Log event
|
|
109
|
-
# @return [String, nil] Formatted text or nil to skip
|
|
110
|
-
def format_event(event)
|
|
111
|
-
return unless passes_agent_filter?(event)
|
|
112
|
-
|
|
113
|
-
type = event[:type] || event["type"]
|
|
114
|
-
|
|
115
|
-
case type
|
|
116
|
-
when "user_prompt"
|
|
117
|
-
format_user_prompt(event)
|
|
118
|
-
when "agent_step"
|
|
119
|
-
format_agent_step(event)
|
|
120
|
-
when "agent_stop"
|
|
121
|
-
format_agent_stop(event)
|
|
122
|
-
when "tool_call"
|
|
123
|
-
format_tool_call(event)
|
|
124
|
-
when "tool_result"
|
|
125
|
-
format_tool_result(event)
|
|
126
|
-
when "pre_delegation", "delegation_start"
|
|
127
|
-
format_delegation_start(event)
|
|
128
|
-
when "post_delegation", "delegation_complete"
|
|
129
|
-
format_delegation_complete(event)
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Format user_prompt event
|
|
134
|
-
#
|
|
135
|
-
# @param event [Hash] Event data
|
|
136
|
-
# @return [String, nil] Formatted text
|
|
137
|
-
def format_user_prompt(event)
|
|
138
|
-
prompt = event[:prompt] || event["prompt"]
|
|
139
|
-
return if prompt.nil? || prompt.to_s.strip.empty?
|
|
140
|
-
|
|
141
|
-
agent = event[:agent] || event["agent"]
|
|
142
|
-
source = event[:source] || event["source"] || "user"
|
|
143
|
-
|
|
144
|
-
# Show source if it's a delegation or system message
|
|
145
|
-
prefix = case source.to_s
|
|
146
|
-
when "delegation"
|
|
147
|
-
"DELEGATION REQUEST"
|
|
148
|
-
when "system"
|
|
149
|
-
"SYSTEM"
|
|
150
|
-
else
|
|
151
|
-
"USER"
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
if agent && source.to_s != "user"
|
|
155
|
-
"#{prefix} → [#{agent}]: #{prompt}"
|
|
156
|
-
else
|
|
157
|
-
"#{prefix}: #{prompt}"
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Format agent_step event (intermediate response with tool calls)
|
|
162
|
-
#
|
|
163
|
-
# @param event [Hash] Event data
|
|
164
|
-
# @return [String, nil] Formatted text
|
|
165
|
-
def format_agent_step(event)
|
|
166
|
-
return unless @include_thinking
|
|
167
|
-
|
|
168
|
-
content = event[:content] || event["content"]
|
|
169
|
-
return if content.nil? || content.to_s.strip.empty?
|
|
170
|
-
|
|
171
|
-
agent = event[:agent] || event["agent"]
|
|
172
|
-
"AGENT [#{agent}] (thinking): #{content}"
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# Format agent_stop event (final response)
|
|
176
|
-
#
|
|
177
|
-
# @param event [Hash] Event data
|
|
178
|
-
# @return [String, nil] Formatted text
|
|
179
|
-
def format_agent_stop(event)
|
|
180
|
-
content = event[:content] || event["content"]
|
|
181
|
-
return if content.nil? || content.to_s.strip.empty?
|
|
182
|
-
|
|
183
|
-
agent = event[:agent] || event["agent"]
|
|
184
|
-
"AGENT [#{agent}]: #{content}"
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Format tool_call event
|
|
188
|
-
#
|
|
189
|
-
# @param event [Hash] Event data
|
|
190
|
-
# @return [String] Formatted text
|
|
191
|
-
def format_tool_call(event)
|
|
192
|
-
tool = event[:tool] || event["tool"] || event[:tool_name] || event["tool_name"]
|
|
193
|
-
agent = event[:agent] || event["agent"]
|
|
194
|
-
arguments = event[:arguments] || event["arguments"]
|
|
195
|
-
|
|
196
|
-
args_str = format_arguments(arguments)
|
|
197
|
-
"TOOL [#{agent}] → #{tool}(#{args_str})"
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# Format tool_result event
|
|
201
|
-
#
|
|
202
|
-
# @param event [Hash] Event data
|
|
203
|
-
# @return [String, nil] Formatted text
|
|
204
|
-
def format_tool_result(event)
|
|
205
|
-
return unless @include_tool_results
|
|
206
|
-
|
|
207
|
-
tool = event[:tool] || event["tool"] || event[:tool_name] || event["tool_name"]
|
|
208
|
-
result = event[:result] || event["result"]
|
|
209
|
-
# Use key existence check since false || nil would lose the false value
|
|
210
|
-
success = event.key?(:success) ? event[:success] : event["success"]
|
|
211
|
-
|
|
212
|
-
# Handle RubyLLM::ToolResult objects
|
|
213
|
-
result_content = if result.respond_to?(:content)
|
|
214
|
-
result.content
|
|
215
|
-
else
|
|
216
|
-
result.to_s
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
truncated = truncate(result_content, @max_result_length)
|
|
220
|
-
|
|
221
|
-
status = success == false ? " [FAILED]" : ""
|
|
222
|
-
"RESULT [#{tool}]#{status}: #{truncated}"
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Format delegation_start event
|
|
226
|
-
#
|
|
227
|
-
# @param event [Hash] Event data
|
|
228
|
-
# @return [String, nil] Formatted text
|
|
229
|
-
def format_delegation_start(event)
|
|
230
|
-
from = event[:from_agent] || event["from_agent"] || event[:agent] || event["agent"]
|
|
231
|
-
to = event[:to_agent] || event["to_agent"]
|
|
232
|
-
task = event[:task] || event["task"] || event[:message] || event["message"]
|
|
233
|
-
|
|
234
|
-
return unless to
|
|
235
|
-
|
|
236
|
-
task_preview = truncate(task.to_s, 200)
|
|
237
|
-
"DELEGATE: #{from} → #{to}: #{task_preview}"
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Format delegation_complete event
|
|
241
|
-
#
|
|
242
|
-
# @param event [Hash] Event data
|
|
243
|
-
# @return [String, nil] Formatted text
|
|
244
|
-
def format_delegation_complete(event)
|
|
245
|
-
from = event[:from_agent] || event["from_agent"] || event[:agent] || event["agent"]
|
|
246
|
-
to = event[:to_agent] || event["to_agent"]
|
|
247
|
-
|
|
248
|
-
return unless to
|
|
249
|
-
|
|
250
|
-
"DELEGATE COMPLETE: #{to} → #{from}"
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Format tool arguments for display
|
|
254
|
-
#
|
|
255
|
-
# @param arguments [Hash, String, nil] Tool arguments
|
|
256
|
-
# @return [String] Formatted arguments
|
|
257
|
-
def format_arguments(arguments)
|
|
258
|
-
return "{}" if arguments.nil?
|
|
259
|
-
|
|
260
|
-
str = arguments.is_a?(String) ? arguments : arguments.to_json
|
|
261
|
-
truncate(str, @max_args_length)
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Truncate text to maximum length with ellipsis
|
|
265
|
-
#
|
|
266
|
-
# @param text [String, nil] Text to truncate
|
|
267
|
-
# @param max [Integer] Maximum length
|
|
268
|
-
# @return [String] Truncated text
|
|
269
|
-
def truncate(text, max)
|
|
270
|
-
return "" if text.nil?
|
|
271
|
-
|
|
272
|
-
str = text.to_s
|
|
273
|
-
return str if str.length <= max
|
|
274
|
-
|
|
275
|
-
"#{str[0...max]}..."
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
end
|
data/lib/swarm_sdk/utils.rb
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# Shared utility methods for SwarmSDK
|
|
5
|
-
module Utils
|
|
6
|
-
class << self
|
|
7
|
-
# Recursively convert all hash keys to symbols
|
|
8
|
-
#
|
|
9
|
-
# Handles nested hashes and arrays containing hashes.
|
|
10
|
-
#
|
|
11
|
-
# @param obj [Object] Object to symbolize (Hash, Array, or other)
|
|
12
|
-
# @return [Object] Object with symbolized keys (if applicable)
|
|
13
|
-
#
|
|
14
|
-
# @example
|
|
15
|
-
# Utils.symbolize_keys({ "name" => "test", "config" => { "key" => "value" } })
|
|
16
|
-
# # => { name: "test", config: { key: "value" } }
|
|
17
|
-
def symbolize_keys(obj)
|
|
18
|
-
case obj
|
|
19
|
-
when Hash
|
|
20
|
-
obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
|
|
21
|
-
when Array
|
|
22
|
-
obj.map { |item| symbolize_keys(item) }
|
|
23
|
-
else
|
|
24
|
-
obj
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Recursively convert all hash keys to strings
|
|
29
|
-
#
|
|
30
|
-
# Handles nested hashes and arrays containing hashes.
|
|
31
|
-
#
|
|
32
|
-
# @param obj [Object] Object to stringify (Hash, Array, or other)
|
|
33
|
-
# @return [Object] Object with stringified keys (if applicable)
|
|
34
|
-
#
|
|
35
|
-
# @example
|
|
36
|
-
# Utils.stringify_keys({ name: "test", config: { key: "value" } })
|
|
37
|
-
# # => { "name" => "test", "config" => { "key" => "value" } }
|
|
38
|
-
def stringify_keys(obj)
|
|
39
|
-
case obj
|
|
40
|
-
when Hash
|
|
41
|
-
obj.transform_keys(&:to_s).transform_values { |v| stringify_keys(v) }
|
|
42
|
-
when Array
|
|
43
|
-
obj.map { |item| stringify_keys(item) }
|
|
44
|
-
else
|
|
45
|
-
obj
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Convert hash to YAML string
|
|
50
|
-
#
|
|
51
|
-
# Converts a Ruby hash to a YAML string. Useful for creating inline
|
|
52
|
-
# swarm definitions from hash configurations.
|
|
53
|
-
#
|
|
54
|
-
# @param hash [Hash] Hash to convert
|
|
55
|
-
# @return [String] YAML string representation
|
|
56
|
-
#
|
|
57
|
-
# @example
|
|
58
|
-
# config = { version: 2, swarm: { name: "Test" } }
|
|
59
|
-
# Utils.hash_to_yaml(config)
|
|
60
|
-
# # => "---\nversion: 2\nswarm:\n name: Test\n"
|
|
61
|
-
def hash_to_yaml(hash)
|
|
62
|
-
# Convert symbols to strings for valid YAML
|
|
63
|
-
stringified = stringify_keys(hash)
|
|
64
|
-
stringified.to_yaml
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
# Internal result object for validation phase during snapshot restore
|
|
5
|
-
#
|
|
6
|
-
# Used during restore to track which agents can be restored and which
|
|
7
|
-
# need to be skipped due to configuration mismatches.
|
|
8
|
-
#
|
|
9
|
-
# @api private
|
|
10
|
-
class ValidationResult
|
|
11
|
-
attr_reader :warnings,
|
|
12
|
-
:skipped_agents,
|
|
13
|
-
:restorable_agents,
|
|
14
|
-
:skipped_delegations,
|
|
15
|
-
:restorable_delegations
|
|
16
|
-
|
|
17
|
-
# Initialize validation result
|
|
18
|
-
#
|
|
19
|
-
# @param warnings [Array<Hash>] Warning messages with details
|
|
20
|
-
# @param skipped_agents [Array<Symbol>] Names of agents that can't be restored
|
|
21
|
-
# @param restorable_agents [Array<Symbol>] Names of agents that can be restored
|
|
22
|
-
# @param skipped_delegations [Array<String>] Names of delegations that can't be restored
|
|
23
|
-
# @param restorable_delegations [Array<String>] Names of delegations that can be restored
|
|
24
|
-
def initialize(warnings:, skipped_agents:, restorable_agents:,
|
|
25
|
-
skipped_delegations:, restorable_delegations:)
|
|
26
|
-
@warnings = warnings
|
|
27
|
-
@skipped_agents = skipped_agents
|
|
28
|
-
@restorable_agents = restorable_agents
|
|
29
|
-
@skipped_delegations = skipped_delegations
|
|
30
|
-
@restorable_delegations = restorable_delegations
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
data/lib/swarm_sdk/version.rb
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SwarmSDK
|
|
4
|
-
class Workflow
|
|
5
|
-
# AgentConfig provides fluent API for configuring agents within a node
|
|
6
|
-
#
|
|
7
|
-
# This class enables the chainable syntax:
|
|
8
|
-
# agent(:backend).delegates_to(:tester, :database)
|
|
9
|
-
# agent(:backend, reset_context: false) # Preserve context across nodes
|
|
10
|
-
# agent(:backend).tools(:Read, :Edit) # Override tools for this node
|
|
11
|
-
#
|
|
12
|
-
# @example Basic delegation
|
|
13
|
-
# agent(:backend).delegates_to(:tester)
|
|
14
|
-
#
|
|
15
|
-
# @example No delegation (solo agent)
|
|
16
|
-
# agent(:planner)
|
|
17
|
-
#
|
|
18
|
-
# @example Preserve agent context
|
|
19
|
-
# agent(:architect, reset_context: false)
|
|
20
|
-
#
|
|
21
|
-
# @example Override tools for this node
|
|
22
|
-
# agent(:backend).tools(:Read, :Think)
|
|
23
|
-
#
|
|
24
|
-
# @example Combine delegation and tool override
|
|
25
|
-
# agent(:backend).delegates_to(:tester).tools(:Read, :Edit, :Write)
|
|
26
|
-
class AgentConfig
|
|
27
|
-
attr_reader :agent_name
|
|
28
|
-
|
|
29
|
-
def initialize(agent_name, node_builder, reset_context: true)
|
|
30
|
-
@agent_name = agent_name
|
|
31
|
-
@node_builder = node_builder
|
|
32
|
-
@delegates_to = []
|
|
33
|
-
@reset_context = reset_context
|
|
34
|
-
@tools = nil # nil means use global agent definition tools
|
|
35
|
-
@finalized = false
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Set delegation targets for this agent
|
|
39
|
-
#
|
|
40
|
-
# Supports multiple formats for flexibility:
|
|
41
|
-
# - Array: delegates_to(:frontend, :backend)
|
|
42
|
-
# - Hash: delegates_to(frontend: "AskFrontend", backend: "GetBackend")
|
|
43
|
-
#
|
|
44
|
-
# @param agent_names_and_options [Array<Symbol, Hash>] Names and/or hash with custom tool names
|
|
45
|
-
# @return [self] For method chaining
|
|
46
|
-
def delegates_to(*agent_names_and_options)
|
|
47
|
-
# Parse delegation configs (same logic as Agent::Builder)
|
|
48
|
-
@delegates_to = []
|
|
49
|
-
agent_names_and_options.each do |item|
|
|
50
|
-
case item
|
|
51
|
-
when Symbol, String
|
|
52
|
-
@delegates_to << { agent: item.to_sym, tool_name: nil }
|
|
53
|
-
when Hash
|
|
54
|
-
item.each do |agent, tool_name|
|
|
55
|
-
@delegates_to << { agent: agent.to_sym, tool_name: tool_name }
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
update_registration
|
|
61
|
-
self
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Override tools for this agent in this node
|
|
65
|
-
#
|
|
66
|
-
# @param tool_names [Array<Symbol>] Tool names to use (overrides global agent definition)
|
|
67
|
-
# @return [self] For method chaining
|
|
68
|
-
#
|
|
69
|
-
# @example
|
|
70
|
-
# agent(:backend).tools(:Read, :Edit)
|
|
71
|
-
def tools(*tool_names)
|
|
72
|
-
@tools = tool_names.map(&:to_sym)
|
|
73
|
-
update_registration
|
|
74
|
-
self
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Update agent registration (called after each fluent method)
|
|
78
|
-
#
|
|
79
|
-
# Always updates the registration with current state.
|
|
80
|
-
# This allows chaining: .delegates_to(...).tools(...)
|
|
81
|
-
#
|
|
82
|
-
# @return [void]
|
|
83
|
-
def update_registration
|
|
84
|
-
@node_builder.register_agent(@agent_name, @delegates_to, @reset_context, @tools)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Finalize agent configuration (backward compatibility)
|
|
88
|
-
#
|
|
89
|
-
# @return [void]
|
|
90
|
-
def finalize
|
|
91
|
-
update_registration
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|