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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +43 -22
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +6 -0
  4. data/lib/swarm_sdk/ruby_llm_patches/mcp_ssl_patch.rb +144 -0
  5. data/lib/swarm_sdk/ruby_llm_patches/tool_concurrency_patch.rb +3 -4
  6. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  7. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  8. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  9. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  10. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  11. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  12. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  13. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  14. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  15. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  16. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  17. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  18. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  19. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  20. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  24. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  25. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  26. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  27. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  28. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  29. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  30. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  31. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  32. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  33. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  34. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  35. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  36. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  37. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  38. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  39. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  40. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  41. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  42. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  43. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  45. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  46. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  47. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  48. data/lib/swarm_sdk/v3/tools/read.rb +181 -0
  49. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  50. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  51. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  52. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  53. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  54. data/lib/swarm_sdk/v3.rb +145 -0
  55. metadata +84 -148
  56. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  57. data/lib/swarm_sdk/agent/builder.rb +0 -680
  58. data/lib/swarm_sdk/agent/chat.rb +0 -1432
  59. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  60. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  61. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  62. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  63. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  64. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  65. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  66. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  67. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  68. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  69. data/lib/swarm_sdk/agent/context.rb +0 -115
  70. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  71. data/lib/swarm_sdk/agent/definition.rb +0 -581
  72. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  73. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  74. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  75. data/lib/swarm_sdk/agent_registry.rb +0 -146
  76. data/lib/swarm_sdk/builders/base_builder.rb +0 -553
  77. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  78. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  79. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  80. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  81. data/lib/swarm_sdk/config.rb +0 -367
  82. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  83. data/lib/swarm_sdk/configuration/translator.rb +0 -283
  84. data/lib/swarm_sdk/configuration.rb +0 -165
  85. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  86. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  87. data/lib/swarm_sdk/context_compactor.rb +0 -335
  88. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  89. data/lib/swarm_sdk/context_management/context.rb +0 -328
  90. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  91. data/lib/swarm_sdk/defaults.rb +0 -251
  92. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  93. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  94. data/lib/swarm_sdk/hooks/context.rb +0 -197
  95. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  96. data/lib/swarm_sdk/hooks/error.rb +0 -29
  97. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  98. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  99. data/lib/swarm_sdk/hooks/result.rb +0 -150
  100. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  101. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  102. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  103. data/lib/swarm_sdk/log_collector.rb +0 -227
  104. data/lib/swarm_sdk/log_stream.rb +0 -127
  105. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  106. data/lib/swarm_sdk/model_aliases.json +0 -8
  107. data/lib/swarm_sdk/models.json +0 -44002
  108. data/lib/swarm_sdk/models.rb +0 -161
  109. data/lib/swarm_sdk/node_context.rb +0 -245
  110. data/lib/swarm_sdk/observer/builder.rb +0 -81
  111. data/lib/swarm_sdk/observer/config.rb +0 -45
  112. data/lib/swarm_sdk/observer/manager.rb +0 -236
  113. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  114. data/lib/swarm_sdk/permissions/config.rb +0 -239
  115. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  116. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  117. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  118. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  119. data/lib/swarm_sdk/plugin.rb +0 -309
  120. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  121. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  122. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  123. data/lib/swarm_sdk/restore_result.rb +0 -65
  124. data/lib/swarm_sdk/result.rb +0 -212
  125. data/lib/swarm_sdk/snapshot.rb +0 -156
  126. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  127. data/lib/swarm_sdk/state_restorer.rb +0 -476
  128. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  129. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  130. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -195
  131. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  132. data/lib/swarm_sdk/swarm/executor.rb +0 -290
  133. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -151
  134. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  135. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -360
  136. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -270
  137. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  138. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  139. data/lib/swarm_sdk/swarm.rb +0 -843
  140. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  141. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  142. data/lib/swarm_sdk/tools/base.rb +0 -63
  143. data/lib/swarm_sdk/tools/bash.rb +0 -280
  144. data/lib/swarm_sdk/tools/clock.rb +0 -46
  145. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  146. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  147. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  148. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  149. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  150. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  151. data/lib/swarm_sdk/tools/edit.rb +0 -145
  152. data/lib/swarm_sdk/tools/glob.rb +0 -166
  153. data/lib/swarm_sdk/tools/grep.rb +0 -235
  154. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  155. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  156. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  157. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -100
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  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 -95
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  182. data/lib/swarm_sdk/workflow.rb +0 -589
  183. data/lib/swarm_sdk.rb +0 -718
@@ -1,145 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Loader for creating swarm instances from multiple sources
5
- #
6
- # SwarmLoader loads swarm configurations from:
7
- # - Files: .rb (DSL) or .yml (YAML)
8
- # - YAML strings: Direct YAML content
9
- # - DSL blocks: Inline Ruby blocks
10
- #
11
- # All loaded swarms get hierarchical swarm_id and parent_swarm_id.
12
- #
13
- # ## Features
14
- # - Supports Ruby DSL (.rb files or blocks)
15
- # - Supports YAML (.yml/.yaml files or strings)
16
- # - Sets hierarchical swarm_id based on parent + registration name
17
- # - Isolates loading in separate context
18
- # - Proper error handling for missing/invalid sources
19
- #
20
- # ## Examples
21
- #
22
- # # From file
23
- # swarm = SwarmLoader.load_from_file(
24
- # "./swarms/code_review.rb",
25
- # swarm_id: "main/code_review",
26
- # parent_swarm_id: "main"
27
- # )
28
- #
29
- # # From YAML string
30
- # swarm = SwarmLoader.load_from_yaml_string(
31
- # "version: 2\nswarm:\n name: Test\n...",
32
- # swarm_id: "main/testing",
33
- # parent_swarm_id: "main"
34
- # )
35
- #
36
- # # From block
37
- # swarm = SwarmLoader.load_from_block(
38
- # proc { id "team"; name "Team"; agent :dev { ... } },
39
- # swarm_id: "main/team",
40
- # parent_swarm_id: "main"
41
- # )
42
- #
43
- class SwarmLoader
44
- class << self
45
- # Load a swarm from a file (.rb or .yml)
46
- #
47
- # @param file_path [String] Path to swarm file
48
- # @param swarm_id [String] Hierarchical swarm ID to assign
49
- # @param parent_swarm_id [String] Parent swarm ID
50
- # @return [Swarm] Loaded swarm instance with overridden IDs
51
- # @raise [ConfigurationError] If file not found or unsupported type
52
- def load_from_file(file_path, swarm_id:, parent_swarm_id:)
53
- path = Pathname.new(file_path).expand_path
54
-
55
- raise ConfigurationError, "Swarm file not found: #{path}" unless path.exist?
56
-
57
- # Determine file type and load
58
- case path.extname
59
- when ".rb"
60
- load_from_ruby_file(path, swarm_id, parent_swarm_id)
61
- when ".yml", ".yaml"
62
- load_from_yaml_file(path, swarm_id, parent_swarm_id)
63
- else
64
- raise ConfigurationError, "Unsupported swarm file type: #{path.extname}. Use .rb, .yml, or .yaml"
65
- end
66
- end
67
-
68
- # Load a swarm from YAML string
69
- #
70
- # @param yaml_content [String] YAML configuration content
71
- # @param swarm_id [String] Hierarchical swarm ID to assign
72
- # @param parent_swarm_id [String] Parent swarm ID
73
- # @return [Swarm] Loaded swarm instance with overridden IDs
74
- # @raise [ConfigurationError] If YAML is invalid
75
- def load_from_yaml_string(yaml_content, swarm_id:, parent_swarm_id:)
76
- # Use Configuration to parse YAML string
77
- config = Configuration.new(yaml_content, base_dir: Dir.pwd)
78
- config.load_and_validate
79
- swarm = config.to_swarm
80
-
81
- # Override swarm_id and parent_swarm_id
82
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
83
-
84
- swarm
85
- end
86
-
87
- # Load a swarm from DSL block
88
- #
89
- # @param block [Proc] Block containing SwarmSDK DSL
90
- # @param swarm_id [String] Hierarchical swarm ID to assign
91
- # @param parent_swarm_id [String] Parent swarm ID
92
- # @return [Swarm] Loaded swarm instance with overridden IDs
93
- def load_from_block(block, swarm_id:, parent_swarm_id:)
94
- # Execute block in Builder context
95
- builder = Swarm::Builder.new
96
- builder.instance_eval(&block)
97
- swarm = builder.build_swarm
98
-
99
- # Override swarm_id and parent_swarm_id
100
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
101
-
102
- swarm
103
- end
104
-
105
- private
106
-
107
- # Load swarm from Ruby DSL file
108
- #
109
- # @param path [Pathname] Path to .rb file
110
- # @param swarm_id [String] Swarm ID to assign
111
- # @param parent_swarm_id [String] Parent swarm ID
112
- # @return [Swarm] Loaded swarm with overridden IDs
113
- def load_from_ruby_file(path, swarm_id, parent_swarm_id)
114
- content = File.read(path)
115
-
116
- # Execute DSL in isolated context
117
- # The DSL should return a swarm via SwarmSDK.build { ... }
118
- swarm = eval(content, binding, path.to_s) # rubocop:disable Security/Eval
119
-
120
- # Override swarm_id and parent_swarm_id
121
- # These must be set after build to ensure hierarchical structure
122
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
123
-
124
- swarm
125
- end
126
-
127
- # Load swarm from YAML file
128
- #
129
- # @param path [Pathname] Path to .yml file
130
- # @param swarm_id [String] Swarm ID to assign
131
- # @param parent_swarm_id [String] Parent swarm ID
132
- # @return [Swarm] Loaded swarm with overridden IDs
133
- def load_from_yaml_file(path, swarm_id, parent_swarm_id)
134
- # Use Configuration to load and convert YAML to swarm
135
- config = Configuration.load_file(path.to_s)
136
- swarm = config.to_swarm
137
-
138
- # Override swarm_id and parent_swarm_id
139
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
140
-
141
- swarm
142
- end
143
- end
144
- end
145
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Registry for managing sub-swarms in composable swarms
5
- #
6
- # SwarmRegistry handles lazy loading, caching, and lifecycle management
7
- # of child swarms registered via the `swarms` DSL block.
8
- #
9
- # ## Features
10
- # - Lazy loading: Sub-swarms are only loaded when first accessed
11
- # - Caching: Loaded swarms are cached for reuse
12
- # - Hierarchical IDs: Sub-swarms get IDs based on parent + registration name
13
- # - Context control: keep_context determines if swarm state persists
14
- # - Lifecycle management: Cleanup cascades through all sub-swarms
15
- #
16
- # ## Example
17
- #
18
- # registry = SwarmRegistry.new(parent_swarm_id: "main_app")
19
- # registry.register("code_review", file: "./swarms/code_review.rb", keep_context: true)
20
- #
21
- # # Lazy load on first access
22
- # swarm = registry.load_swarm("code_review")
23
- # # => Swarm with swarm_id = "main_app/code_review"
24
- #
25
- # # Reset if keep_context: false
26
- # registry.reset_if_needed("code_review")
27
- #
28
- class SwarmRegistry
29
- # Initialize a new swarm registry
30
- #
31
- # @param parent_swarm_id [String] ID of the parent swarm
32
- def initialize(parent_swarm_id:)
33
- @parent_swarm_id = parent_swarm_id
34
- @registered_swarms = {}
35
- # Format: { "code_review" => { file: "...", keep_context: true, instance: nil } }
36
- end
37
-
38
- # Register a sub-swarm for lazy loading
39
- #
40
- # @param name [String] Registration name for the swarm
41
- # @param source [Hash] Source specification with :type and :value
42
- # - { type: :file, value: "./path/to/swarm.rb" }
43
- # - { type: :yaml, value: "version: 2\n..." }
44
- # - { type: :block, value: Proc }
45
- # @param keep_context [Boolean] Whether to preserve conversation state between calls (default: true)
46
- # @return [void]
47
- # @raise [ArgumentError] If swarm with same name already registered
48
- def register(name, source:, keep_context: true)
49
- raise ArgumentError, "Swarm '#{name}' already registered" if @registered_swarms.key?(name)
50
-
51
- @registered_swarms[name] = {
52
- source: source,
53
- keep_context: keep_context,
54
- instance: nil, # Lazy load
55
- }
56
- end
57
-
58
- # Check if a swarm is registered
59
- #
60
- # @param name [String] Swarm registration name
61
- # @return [Boolean] True if swarm is registered
62
- def registered?(name)
63
- @registered_swarms.key?(name)
64
- end
65
-
66
- # Load a registered swarm (lazy load + cache)
67
- #
68
- # Loads the swarm from its source (file, yaml, or block) on first access, then caches it.
69
- # Sets hierarchical swarm_id based on parent_swarm_id + registration name.
70
- #
71
- # @param name [String] Swarm registration name
72
- # @return [Swarm] Loaded swarm instance
73
- # @raise [ConfigurationError] If swarm not registered
74
- def load_swarm(name)
75
- entry = @registered_swarms[name]
76
- raise ConfigurationError, "Swarm '#{name}' not registered" unless entry
77
-
78
- # Return cached instance if exists
79
- return entry[:instance] if entry[:instance]
80
-
81
- # Load from appropriate source
82
- swarm_id = "#{@parent_swarm_id}/#{name}" # Hierarchical
83
- source = entry[:source]
84
-
85
- swarm = case source[:type]
86
- when :file
87
- SwarmLoader.load_from_file(
88
- source[:value],
89
- swarm_id: swarm_id,
90
- parent_swarm_id: @parent_swarm_id,
91
- )
92
- when :yaml
93
- SwarmLoader.load_from_yaml_string(
94
- source[:value],
95
- swarm_id: swarm_id,
96
- parent_swarm_id: @parent_swarm_id,
97
- )
98
- when :block
99
- SwarmLoader.load_from_block(
100
- source[:value],
101
- swarm_id: swarm_id,
102
- parent_swarm_id: @parent_swarm_id,
103
- )
104
- else
105
- raise ConfigurationError, "Unknown source type: #{source[:type]}"
106
- end
107
-
108
- entry[:instance] = swarm
109
- swarm
110
- end
111
-
112
- # Reset swarm context if keep_context: false
113
- #
114
- # @param name [String] Swarm registration name
115
- # @return [void]
116
- def reset_if_needed(name)
117
- entry = @registered_swarms[name]
118
- return if entry[:keep_context]
119
-
120
- entry[:instance]&.reset_context!
121
- end
122
-
123
- # Cleanup all registered swarms
124
- #
125
- # Stops all loaded swarm instances and clears the registry.
126
- # Should be called when parent swarm is done.
127
- #
128
- # @return [void]
129
- def shutdown_all
130
- @registered_swarms.each_value do |entry|
131
- entry[:instance]&.cleanup
132
- end
133
- @registered_swarms.clear
134
- end
135
- end
136
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Tools
5
- # Base class for all SwarmSDK tools
6
- #
7
- # Provides:
8
- # - Declarative removability control
9
- # - Common tool functionality
10
- # - Standard initialization patterns
11
- #
12
- # ## Removability
13
- #
14
- # Tools can be marked as non-removable to ensure they're always available:
15
- #
16
- # class Think < Base
17
- # removable false
18
- # end
19
- #
20
- # Non-removable tools are included even when skills specify a limited toolset.
21
- #
22
- # @example Removable tool (default)
23
- # class Read < Base
24
- # # removable true # Default, can omit
25
- # end
26
- #
27
- # @example Non-removable tool
28
- # class Think < Base
29
- # removable false # Always available
30
- # end
31
- class Base < RubyLLM::Tool
32
- class << self
33
- # Whether this tool can be deactivated by LoadSkill
34
- #
35
- # Non-removable tools are ALWAYS active regardless of skill toolset.
36
- # Use for essential tools that agents should never lose.
37
- #
38
- # @return [Boolean] True if removable (default: true)
39
- def removable?
40
- @removable.nil? ? true : @removable
41
- end
42
-
43
- # Mark tool as removable or non-removable
44
- #
45
- # @param value [Boolean] Whether tool can be removed
46
- # @return [void]
47
- #
48
- # @example Make tool always available
49
- # removable false
50
- def removable(value)
51
- @removable = value
52
- end
53
- end
54
-
55
- # Instance method for checking removability
56
- #
57
- # @return [Boolean]
58
- def removable?
59
- self.class.removable?
60
- end
61
- end
62
- end
63
- end
@@ -1,280 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Tools
5
- # Bash tool for executing shell commands
6
- #
7
- # Executes commands in a persistent shell session with timeout support.
8
- # Provides comprehensive guidance on proper usage patterns.
9
- class Bash < Base
10
- # Factory pattern: declare what parameters this tool needs for instantiation
11
- class << self
12
- def creation_requirements
13
- [:directory]
14
- end
15
- end
16
-
17
- def initialize(directory:)
18
- super()
19
- @directory = File.expand_path(directory)
20
- end
21
-
22
- def name
23
- "Bash"
24
- end
25
-
26
- description <<~DESC
27
- Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
28
-
29
- IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
30
-
31
- Before executing the command, please follow these steps:
32
-
33
- 1. Directory Verification:
34
- - If the command will create new directories or files, first use `ls` to verify the parent directory exists and is the correct location
35
- - For example, before running "mkdir foo/bar", first use `ls foo` to check that "foo" exists and is the intended parent directory
36
-
37
- 2. Command Execution:
38
- - Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt")
39
- - Examples of proper quoting:
40
- - cd "/Users/name/My Documents" (correct)
41
- - cd /Users/name/My Documents (incorrect - will fail)
42
- - python "/path/with spaces/script.py" (correct)
43
- - python /path/with spaces/script.py (incorrect - will fail)
44
- - After ensuring proper quoting, execute the command.
45
- - Capture the output of the command.
46
-
47
- Usage notes:
48
- - The command argument is required.
49
- - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).
50
- - It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
51
- - If the output exceeds 30000 characters, output will be truncated before being returned to you.
52
- - Avoid using Bash with the `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
53
- - File search: Use Glob (NOT find or ls)
54
- - Content search: Use Grep (NOT grep or rg)
55
- - Read files: Use Read (NOT cat/head/tail)
56
- - Edit files: Use Edit (NOT sed/awk)
57
- - Write files: Use Write (NOT echo >/cat <<EOF)
58
- - Communication: Output text directly (NOT echo/printf)
59
- - When issuing multiple commands:
60
- - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
61
- - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together (e.g., `git add . && git commit -m "message" && git push`). For instance, if one operation must complete before another starts (like mkdir before cp, Write before Bash for git operations, or git add before git commit), run these operations sequentially instead.
62
- - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
63
- - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
64
- - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it.
65
- <good-example>
66
- pytest /foo/bar/tests
67
- </good-example>
68
- <bad-example>
69
- cd /foo/bar && pytest tests
70
- </bad-example>
71
- DESC
72
-
73
- param :command,
74
- type: "string",
75
- desc: "The command to execute",
76
- required: true
77
-
78
- param :description,
79
- type: "string",
80
- desc: "Clear, concise description of what this command does in 5-10 words, in active voice. Examples:\nInput: ls\nOutput: List files in current directory\n\nInput: git status\nOutput: Show working tree status\n\nInput: npm install\nOutput: Install package dependencies\n\nInput: mkdir foo\nOutput: Create directory 'foo'",
81
- required: false
82
-
83
- param :timeout,
84
- type: "number",
85
- desc: "Optional timeout in milliseconds (max 600000)",
86
- required: false
87
-
88
- # NOTE: Timeout and output limits now accessed via SwarmSDK.config
89
-
90
- # Commands that are ALWAYS blocked for safety reasons
91
- # These cannot be overridden by permissions configuration
92
- ALWAYS_BLOCKED_COMMANDS = [
93
- %r{^rm\s+-rf\s+/$}, # rm -rf / - delete root filesystem
94
- ].freeze
95
-
96
- def execute(command:, description: nil, timeout: nil)
97
- # Validate inputs
98
- return validation_error("command is required") if command.nil? || command.empty?
99
-
100
- # Check against always-blocked commands
101
- blocked_pattern = ALWAYS_BLOCKED_COMMANDS.find { |pattern| pattern.match?(command) }
102
- if blocked_pattern
103
- return blocked_command_error(command, blocked_pattern)
104
- end
105
-
106
- # Validate and set timeout
107
- timeout_ms = timeout || SwarmSDK.config.bash_command_timeout
108
- timeout_ms = [timeout_ms, SwarmSDK.config.bash_command_max_timeout].min
109
- timeout_seconds = timeout_ms / 1000.0
110
-
111
- # Execute command with timeout
112
- stdout = +""
113
- stderr = +""
114
- exit_status = nil
115
-
116
- begin
117
- require "open3"
118
- require "timeout"
119
-
120
- Timeout.timeout(timeout_seconds) do
121
- # CRITICAL: Change to agent's directory for subprocess
122
- # This is SAFE because Open3.popen3 creates a subprocess
123
- # The subprocess inherits the directory, but the parent fiber is unaffected
124
- Dir.chdir(@directory) do
125
- Open3.popen3(command) do |stdin, out, err, wait_thr|
126
- stdin.close # Close stdin since we don't send input
127
-
128
- # Read stdout and stderr
129
- stdout = out.read || ""
130
- stderr = err.read || ""
131
- exit_status = wait_thr.value.exitstatus
132
- end
133
- end
134
- end
135
- rescue Timeout::Error
136
- return format_timeout_error(command, timeout_seconds)
137
- rescue Errno::ENOENT => e
138
- return error("Command not found or executable not in PATH: #{e.message}")
139
- rescue Errno::EACCES
140
- return error("Permission denied: Cannot execute command '#{command}'")
141
- rescue StandardError => e
142
- return error("Failed to execute command: #{e.class.name} - #{e.message}")
143
- end
144
-
145
- # Build output
146
- output = format_command_output(command, description, stdout, stderr, exit_status)
147
-
148
- # Truncate if too long
149
- max_output = SwarmSDK.config.output_character_limit
150
- if output.length > max_output
151
- truncated = output[0...max_output]
152
- truncated += "\n\n<system-reminder>Output truncated at #{max_output} characters. The full output was #{output.length} characters.</system-reminder>"
153
- output = truncated
154
- end
155
-
156
- # Add usage reminders for certain patterns
157
- output = add_usage_reminders(output, command)
158
-
159
- output
160
- rescue StandardError => e
161
- error("Unexpected error executing command: #{e.class.name} - #{e.message}")
162
- end
163
-
164
- private
165
-
166
- def validation_error(message)
167
- "<tool_use_error>InputValidationError: #{message}</tool_use_error>"
168
- end
169
-
170
- def error(message)
171
- "Error: #{message}"
172
- end
173
-
174
- def blocked_command_error(command, pattern)
175
- <<~ERROR
176
- Error: Command blocked for safety reasons.
177
- Command: #{command}
178
- Pattern: #{pattern.source}
179
-
180
- <system-reminder>
181
- SECURITY BLOCK: This command is permanently blocked for safety reasons and cannot be executed.
182
-
183
- This is a built-in safety feature of the Bash tool that cannot be overridden by any configuration.
184
- The command matches a pattern that could cause catastrophic system damage.
185
-
186
- DO NOT attempt to:
187
- - Modify the command slightly to bypass this check
188
- - Ask the user to allow this command
189
- - Work around this restriction in any way
190
-
191
- If you need to perform a similar operation safely, consider:
192
- - Using a more specific path instead of system-wide operations
193
- - Using dedicated tools for file operations
194
- - Asking the user for guidance on a safer approach
195
-
196
- This is an UNRECOVERABLE error. You must inform the user that this command cannot be executed for safety reasons.
197
- </system-reminder>
198
- ERROR
199
- end
200
-
201
- def format_timeout_error(command, timeout_seconds)
202
- <<~ERROR
203
- Error: Command timed out after #{timeout_seconds} seconds.
204
- Command: #{command}
205
-
206
- <system-reminder>The command exceeded the timeout limit. Consider:
207
- 1. Breaking the command into smaller steps
208
- 2. Increasing the timeout parameter
209
- 3. Running long-running commands in the background if supported
210
- </system-reminder>
211
- ERROR
212
- end
213
-
214
- def format_command_output(command, description, stdout, stderr, exit_status)
215
- parts = []
216
-
217
- # Add description if provided
218
- parts << "Running: #{description}" if description
219
-
220
- # Add command
221
- parts << "$ #{command}"
222
- parts << ""
223
-
224
- # Add exit status
225
- parts << "Exit code: #{exit_status}"
226
-
227
- # Add stdout if present
228
- if stdout && !stdout.empty?
229
- parts << ""
230
- parts << "STDOUT:"
231
- parts << stdout.chomp
232
- end
233
-
234
- # Add stderr if present
235
- if stderr && !stderr.empty?
236
- parts << ""
237
- parts << "STDERR:"
238
- parts << stderr.chomp
239
- end
240
-
241
- # Add warning for non-zero exit
242
- if exit_status != 0
243
- parts << ""
244
- parts << "<system-reminder>Command exited with non-zero status (#{exit_status}). Check STDERR for error details.</system-reminder>"
245
- end
246
-
247
- parts.join("\n")
248
- end
249
-
250
- def add_usage_reminders(output, command)
251
- reminders = []
252
-
253
- # Detect file operation commands that should use dedicated tools
254
- if command.match?(/\b(cat|head|tail|less|more)\s+/)
255
- reminders << "You used a command to read a file. Consider using the Read tool instead for better formatting and error handling."
256
- end
257
-
258
- if command.match?(/\b(grep|rg|ag)\s+/)
259
- reminders << "You used grep/ripgrep to search files. Consider using the Grep tool instead for structured results."
260
- end
261
-
262
- if command.match?(/\b(find|locate)\s+/)
263
- reminders << "You used find to locate files. Consider using the Glob tool instead for pattern-based file matching."
264
- end
265
-
266
- if command.match?(/\b(sed|awk)\s+/) && !command.include?("|")
267
- reminders << "You used sed/awk for file editing. Consider using the Edit tool instead for safer, tracked file modifications."
268
- end
269
-
270
- if command.match?(/\becho\s+.*>\s*/) || command.match?(/\bcat\s*<</)
271
- reminders << "You used echo/cat with redirection to write a file. Consider using the Write tool instead for proper file creation."
272
- end
273
-
274
- return output if reminders.empty?
275
-
276
- output + "\n\n<system-reminder>\n#{reminders.join("\n\n")}\n</system-reminder>"
277
- end
278
- end
279
- end
280
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Tools
5
- # Clock tool provides current date and time information
6
- #
7
- # Returns current temporal information in a consistent format.
8
- # Agents use this when they need to know what day/time it is.
9
- class Clock < Base
10
- removable false # Clock is always available
11
-
12
- description <<~DESC
13
- Get current date and time.
14
-
15
- Returns:
16
- - Current date (YYYY-MM-DD format)
17
- - Current time (HH:MM:SS format)
18
- - Day of week (Monday, Tuesday, etc.)
19
- - ISO 8601 timestamp (full datetime)
20
-
21
- Use this when you need to know what day it is, what time it is,
22
- or to store temporal information (e.g., "As of 2025-10-20...").
23
-
24
- No parameters needed - just call Clock() to get complete temporal information.
25
- DESC
26
-
27
- # No parameters needed
28
-
29
- # Override name to return simple "Clock"
30
- def name
31
- "Clock"
32
- end
33
-
34
- def execute
35
- now = Time.now
36
-
37
- <<~RESULT.chomp
38
- Current date: #{now.strftime("%Y-%m-%d")}
39
- Current time: #{now.strftime("%H:%M:%S")}
40
- Day of week: #{now.strftime("%A")}
41
- ISO 8601: #{now.iso8601}
42
- RESULT
43
- end
44
- end
45
- end
46
- end