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,121 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Permissions
5
- # ErrorFormatter generates user-friendly error messages for permission violations
6
- class ErrorFormatter
7
- class << self
8
- # Generate a permission denied error message
9
- #
10
- # @param path [String] The path that was denied
11
- # @param allowed_patterns [Array<String>] List of allowed path patterns
12
- # @param denied_patterns [Array<String>] List of denied path patterns
13
- # @param matching_pattern [String, nil] The specific pattern that blocked this path
14
- # @param tool_name [String] Name of the tool that was denied
15
- # @return [String] Formatted error message with system reminder
16
- def permission_denied(path:, allowed_patterns:, denied_patterns: [], matching_pattern: nil, tool_name:)
17
- operation_verb = case tool_name.to_s
18
- when "Read" then "read"
19
- when "Write" then "write to"
20
- when "Edit", "MultiEdit" then "edit"
21
- when "Glob" then "access directory"
22
- when "Grep" then "search in"
23
- else "access"
24
- end
25
-
26
- # Build policy explanation
27
- policy_info = if matching_pattern && matching_pattern != "(not in allowed list)"
28
- # Show the specific denied pattern that blocked this path
29
- "Blocked by policy: #{matching_pattern}"
30
- elsif matching_pattern == "(not in allowed list)" && allowed_patterns.any?
31
- # Show allowed patterns when path doesn't match any
32
- patterns = allowed_patterns.map { |p| " - #{p}" }.join("\n")
33
- "Path not in allowed list. Allowed paths:\n#{patterns}"
34
- elsif denied_patterns.any?
35
- # Show denied patterns
36
- patterns = denied_patterns.map { |p| " - #{p}" }.join("\n")
37
- "Denied paths:\n#{patterns}"
38
- elsif allowed_patterns.any?
39
- # Show allowed patterns
40
- patterns = allowed_patterns.map { |p| " - #{p}" }.join("\n")
41
- "Allowed paths (not matched):\n#{patterns}"
42
- else
43
- "No access policy configured"
44
- end
45
-
46
- reminder = <<~REMINDER
47
-
48
- <system-reminder>
49
- PERMISSION DENIED: You do not have permission to #{operation_verb} '#{path}'.
50
-
51
- #{policy_info}
52
-
53
- This is an UNRECOVERABLE error set by user policy. You MUST stop trying to access files matching this pattern.
54
-
55
- Policy explanation:
56
- - This policy blocks ALL files matching the pattern, not just this specific file
57
- - Do not attempt to access other files matching this pattern - they will also be denied
58
- - Do not try to work around this restriction by using different tool arguments
59
- - The user has explicitly denied access to these resources via security policy
60
-
61
- You should inform the user that you cannot proceed due to permission restrictions on this file pattern.
62
- </system-reminder>
63
- REMINDER
64
-
65
- "Permission denied: Cannot #{operation_verb} '#{path}'#{reminder}"
66
- end
67
-
68
- # Generate a command permission denied error message
69
- #
70
- # @param command [String] The command that was denied
71
- # @param allowed_patterns [Array<Regexp>] List of allowed command regex patterns
72
- # @param denied_patterns [Array<Regexp>] List of denied command regex patterns
73
- # @param matching_pattern [String, nil] The specific pattern that blocked this command
74
- # @param tool_name [String] Name of the tool (typically "bash")
75
- # @return [String] Formatted error message with system reminder
76
- def command_permission_denied(command:, allowed_patterns:, denied_patterns: [], matching_pattern: nil, tool_name:)
77
- # Build policy explanation
78
- policy_info = if matching_pattern && matching_pattern != "(not in allowed list)"
79
- # Show the specific denied pattern that blocked this command
80
- "Blocked by policy: #{matching_pattern}"
81
- elsif matching_pattern == "(not in allowed list)" && allowed_patterns.any?
82
- # Show allowed patterns when command doesn't match any
83
- patterns = allowed_patterns.map { |p| " - #{p.source}" }.join("\n")
84
- "Command not in allowed list. Allowed command patterns:\n#{patterns}"
85
- elsif denied_patterns.any?
86
- # Show denied patterns
87
- patterns = denied_patterns.map { |p| " - #{p.source}" }.join("\n")
88
- "Denied command patterns:\n#{patterns}"
89
- elsif allowed_patterns.any?
90
- # Show allowed patterns
91
- patterns = allowed_patterns.map { |p| " - #{p.source}" }.join("\n")
92
- "Allowed command patterns (not matched):\n#{patterns}"
93
- else
94
- "No command policy configured"
95
- end
96
-
97
- reminder = <<~REMINDER
98
-
99
- <system-reminder>
100
- PERMISSION DENIED: You do not have permission to execute command '#{command}'.
101
-
102
- #{policy_info}
103
-
104
- This is an UNRECOVERABLE error set by user policy. You MUST stop trying to execute commands matching this pattern.
105
-
106
- Policy explanation:
107
- - This policy blocks ALL commands matching the pattern, not just this specific command
108
- - Do not attempt to execute other commands matching this pattern - they will also be denied
109
- - Do not try to work around this restriction by modifying the command slightly
110
- - The user has explicitly denied access to these commands via security policy
111
-
112
- You should inform the user that you cannot proceed due to permission restrictions on this command.
113
- </system-reminder>
114
- REMINDER
115
-
116
- "Permission denied: Cannot execute command '#{command}'#{reminder}"
117
- end
118
- end
119
- end
120
- end
121
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Permissions
5
- # PathMatcher handles glob pattern matching for file paths
6
- #
7
- # Supports gitignore-style glob patterns with:
8
- # - Standard globs: *, **, ?, [abc], {a,b}
9
- # - Recursive matching: **/* matches all nested files
10
- # - Negation: !pattern to explicitly deny
11
- #
12
- # Examples:
13
- # PathMatcher.matches?("tmp/**/*", "tmp/foo/bar.rb") # => true
14
- # PathMatcher.matches?("*.log", "debug.log") # => true
15
- # PathMatcher.matches?("src/**/*.{rb,js}", "src/a/b.rb") # => true
16
- class PathMatcher
17
- class << self
18
- # Check if a path matches a glob pattern
19
- #
20
- # @param pattern [String] Glob pattern to match against
21
- # @param path [String] File path to check
22
- # @return [Boolean] True if path matches pattern
23
- def matches?(pattern, path)
24
- # Remove leading ! for negation patterns (handled by caller)
25
- pattern = pattern.delete_prefix("!")
26
-
27
- # Use File.fnmatch with pathname and extglob flags
28
- # FNM_PATHNAME: ** matches directories recursively
29
- # FNM_EXTGLOB: Support {a,b} patterns
30
- File.fnmatch(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,173 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Permissions
5
- # Validator decorates tools to enforce permission checks before execution
6
- #
7
- # Uses the Decorator pattern (via SimpleDelegator) to wrap tool instances
8
- # and validate file paths and commands before allowing tool execution.
9
- #
10
- # Example:
11
- # write_tool = Tools::Write.new
12
- # permissions = Config.new(
13
- # {
14
- # allowed_paths: ["tmp/**/*"],
15
- # allowed_commands: ["^git (status|diff)$"]
16
- # },
17
- # base_directories: ["."]
18
- # )
19
- # validated_tool = Validator.new(write_tool, permissions)
20
- #
21
- # # This will be denied:
22
- # validated_tool.call({"file_path" => "/etc/passwd", "content" => "..."})
23
- class Validator < SimpleDelegator
24
- # Initialize validator decorator
25
- #
26
- # @param tool [RubyLLM::Tool] Tool instance to wrap
27
- # @param permissions_config [Config] Permission configuration
28
- def initialize(tool, permissions_config)
29
- super(tool)
30
- @permissions = permissions_config
31
- @tool = tool
32
- end
33
-
34
- # Intercept RubyLLM's call method to validate permissions
35
- #
36
- # RubyLLM calls tool.call(args) where args have string keys.
37
- # We must override call (not execute) because SimpleDelegator doesn't
38
- # automatically intercept methods defined in the superclass.
39
- #
40
- # @param args [Hash] Tool arguments with string keys
41
- # @return [String] Tool result or permission denied message
42
- def call(args)
43
- # Validate Bash commands if this is the Bash tool
44
- if bash_tool?
45
- command = args["command"]
46
- if command && !@permissions.command_allowed?(command)
47
- # Find the specific pattern that blocks this command
48
- matching_pattern = @permissions.find_blocking_command_pattern(command)
49
-
50
- return ErrorFormatter.command_permission_denied(
51
- command: command,
52
- allowed_patterns: @permissions.allowed_commands,
53
- denied_patterns: @permissions.denied_commands,
54
- matching_pattern: matching_pattern,
55
- tool_name: @tool.name,
56
- )
57
- end
58
- end
59
-
60
- # Extract paths from arguments (handles both string and symbol keys)
61
- paths = extract_paths_from_args(args)
62
-
63
- # Determine if this is a directory search tool (Glob/Grep)
64
- directory_search = directory_search_tool?
65
-
66
- # Validate each path
67
- paths.each do |path|
68
- next if @permissions.allowed?(path, directory_search: directory_search)
69
-
70
- # Show absolute path in error message for clarity
71
- absolute_path = @permissions.to_absolute(path)
72
-
73
- # Find the specific pattern that blocks this path
74
- matching_pattern = @permissions.find_blocking_pattern(path, directory_search: directory_search)
75
-
76
- return ErrorFormatter.permission_denied(
77
- path: absolute_path,
78
- allowed_patterns: @permissions.allowed_patterns,
79
- denied_patterns: @permissions.denied_patterns,
80
- matching_pattern: matching_pattern,
81
- tool_name: @tool.name,
82
- )
83
- end
84
-
85
- # All permissions validated, call wrapped tool
86
- __getobj__.call(args)
87
- end
88
-
89
- private
90
-
91
- # Check if the tool is the Bash tool
92
- #
93
- # @return [Boolean] True if tool is Bash
94
- def bash_tool?
95
- @tool.name.to_s == "Bash"
96
- end
97
-
98
- # Check if the tool is a directory search tool (Glob or Grep)
99
- #
100
- # @return [Boolean] True if tool searches directories
101
- def directory_search_tool?
102
- tool_name = @tool.name.to_s
103
- tool_name == "Glob" || tool_name == "Grep"
104
- end
105
-
106
- # Extract file paths from tool arguments
107
- #
108
- # RubyLLM always passes arguments with string keys to call().
109
- #
110
- # Different tools have different parameter structures:
111
- # - Write/Edit/Read: file_path parameter
112
- # - MultiEdit: edits array with file_path in each edit
113
- # - Glob/Grep: path parameter (directory to search)
114
- # - Glob: pattern parameter may contain directory (e.g., "lib/**/*.rb")
115
- # - Bash: command parameter (validated separately via command_allowed?)
116
- #
117
- # @param args [Hash] Tool arguments with string keys
118
- # @return [Array<String>] List of file paths to validate
119
- def extract_paths_from_args(args)
120
- paths = []
121
-
122
- # Single file path parameter (Write, Edit, Read)
123
- paths << args["file_path"] if args["file_path"]
124
-
125
- # Path parameter (Glob, Grep)
126
- paths << args["path"] if args["path"]
127
-
128
- # Glob pattern may contain directory prefix (e.g., "lib/**/*.rb")
129
- # Extract the base directory from the pattern for validation
130
- # Note: Only do this for Glob, not Grep (Grep pattern is a regex, not a path)
131
- if @tool.name.to_s == "Glob"
132
- pattern = args["pattern"]
133
- if pattern && !pattern.start_with?("/")
134
- # Extract first directory component from relative patterns
135
- base_dir = extract_base_directory(pattern)
136
- paths << base_dir if base_dir
137
- end
138
- end
139
-
140
- # MultiEdit has array of edits
141
- edits = args["edits"]
142
- edits&.each do |edit|
143
- paths << edit["file_path"] if edit.is_a?(Hash) && edit["file_path"]
144
- end
145
-
146
- paths.compact.uniq
147
- end
148
-
149
- # Extract base directory from a glob pattern
150
- #
151
- # Examples:
152
- # "lib/**/*.rb" => "lib"
153
- # "src/main.rb" => "src"
154
- # "**/*.rb" => nil (no specific directory)
155
- # "*.rb" => nil (current directory)
156
- #
157
- # @param pattern [String] Glob pattern
158
- # @return [String, nil] Base directory or nil
159
- def extract_base_directory(pattern)
160
- return if pattern.nil? || pattern.empty?
161
-
162
- # Split on / and take first component
163
- parts = pattern.split("/")
164
- first_part = parts.first
165
-
166
- # Skip if pattern starts with wildcard (means current directory)
167
- return if first_part.include?("*") || first_part.include?("?")
168
-
169
- first_part
170
- end
171
- end
172
- end
173
- end
@@ -1,122 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # DSL builder for tool permissions configuration
5
- #
6
- # Provides fluent API for configuring tool permissions using underscore syntax:
7
- #
8
- # @example Basic usage
9
- # permissions do
10
- # tool(:Write).allow_paths "tmp/**/*"
11
- # tool(:Write).deny_paths "tmp/secrets/**"
12
- # tool(:Read).deny_paths "lib/**/*"
13
- # end
14
- #
15
- # @example Bash commands
16
- # permissions do
17
- # tool(:Bash).allow_commands "^git (status|diff|log)$"
18
- # tool(:Bash).deny_commands "^rm -rf"
19
- # end
20
- #
21
- class PermissionsBuilder
22
- def initialize
23
- @permissions = {}
24
- end
25
-
26
- class << self
27
- # Build permissions from block
28
- #
29
- # @yield Block for configuring permissions
30
- # @return [Hash] Permissions configuration
31
- def build(&block)
32
- builder = new
33
- builder.instance_eval(&block)
34
- builder.to_h
35
- end
36
- end
37
-
38
- # Convert to hash format expected by AgentDefinition
39
- #
40
- # @return [Hash] Permissions config
41
- def to_h
42
- @permissions
43
- end
44
-
45
- # Get a tool permissions proxy for configuring a specific tool
46
- #
47
- # @param tool_name [Symbol, String] Tool name
48
- # @return [ToolPermissionsProxy] Proxy for configuring this tool
49
- #
50
- # @example
51
- # tool(:Write).allow_paths "tmp/**/*"
52
- # tool(:Bash).deny_commands "^rm -rf"
53
- def tool(tool_name)
54
- ToolPermissionsProxy.new(tool_name, @permissions)
55
- end
56
- end
57
-
58
- # Proxy for configuring permissions on a specific tool
59
- #
60
- # @example
61
- # tool(:Write).allow_paths "tmp/**/*"
62
- # tool(:Write).deny_paths "tmp/secrets/**"
63
- # tool(:Bash).allow_commands "^git status$"
64
- #
65
- class ToolPermissionsProxy
66
- def initialize(tool_name, permissions_hash)
67
- @tool_name = tool_name.to_sym
68
- @permissions = permissions_hash
69
- end
70
-
71
- # Add allowed path patterns
72
- #
73
- # @param patterns [Array<String>] Glob patterns for allowed paths
74
- # @return [self]
75
- def allow_paths(*patterns)
76
- ensure_tool_config
77
- @permissions[@tool_name][:allowed_paths] ||= []
78
- @permissions[@tool_name][:allowed_paths].concat(patterns.flatten)
79
- self
80
- end
81
-
82
- # Add denied path patterns
83
- #
84
- # @param patterns [Array<String>] Glob patterns for denied paths
85
- # @return [self]
86
- def deny_paths(*patterns)
87
- ensure_tool_config
88
- @permissions[@tool_name][:denied_paths] ||= []
89
- @permissions[@tool_name][:denied_paths].concat(patterns.flatten)
90
- self
91
- end
92
-
93
- # Add allowed command patterns (Bash tool only)
94
- #
95
- # @param patterns [Array<String>] Regex patterns for allowed commands
96
- # @return [self]
97
- def allow_commands(*patterns)
98
- ensure_tool_config
99
- @permissions[@tool_name][:allowed_commands] ||= []
100
- @permissions[@tool_name][:allowed_commands].concat(patterns.flatten)
101
- self
102
- end
103
-
104
- # Add denied command patterns (Bash tool only)
105
- #
106
- # @param patterns [Array<String>] Regex patterns for denied commands
107
- # @return [self]
108
- def deny_commands(*patterns)
109
- ensure_tool_config
110
- @permissions[@tool_name][:denied_commands] ||= []
111
- @permissions[@tool_name][:denied_commands].concat(patterns.flatten)
112
- self
113
- end
114
-
115
- private
116
-
117
- # Ensure tool entry exists in permissions hash
118
- def ensure_tool_config
119
- @permissions[@tool_name] ||= {}
120
- end
121
- end
122
- end