swarm_sdk 2.7.14 → 3.0.0.alpha2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -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