Class: SwarmSDK::Agent::Definition

Inherits:
Object
  • Object
show all
Defined in:
lib/swarm_sdk/agent/definition.rb

Overview

Agent definition encapsulates agent configuration and builds system prompts

This class is responsible for:

  • Parsing and validating agent configuration
  • Building the full system prompt (base + custom)
  • Handling tool permissions
  • Managing hooks (both DSL Ruby blocks and YAML shell commands)

Examples:

definition = Agent::Definition.new(:backend, {
  description: "Backend API developer",
  model: "gpt-5",
  tools: [:Read, :Write, :Bash],
  system_prompt: "You build APIs"
})

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, config = {}) ⇒ Definition

Returns a new instance of Definition.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/swarm_sdk/agent/definition.rb', line 47

def initialize(name, config = {})
  @name = name.to_sym

  # Validate name doesn't contain '@' (reserved for delegation instances)
  if @name.to_s.include?("@")
    raise ConfigurationError,
      "Agent names cannot contain '@' character (reserved for delegation instance naming). " \
        "Agent: #{@name}"
  end

  # BREAKING CHANGE: Hard error for plural form
  if config[:directories]
    raise ConfigurationError,
      "The 'directories' (plural) configuration is no longer supported in SwarmSDK 1.0+.\n\n" \
        "Change 'directories:' to 'directory:' (singular).\n\n" \
        "If you need access to multiple directories, use permissions:\n\n  " \
        "directory: 'backend/'\n  " \
        "permissions do\n    " \
        "tool(:Read).allow_paths('../shared/**')\n  " \
        "end"
  end

  @description = config[:description]
  @model = config[:model] || SwarmSDK.config.default_model
  @provider = config[:provider] || SwarmSDK.config.default_provider
  @base_url = config[:base_url]
  @api_version = config[:api_version]
  @context_window = config[:context_window] # Explicit context window override
  @parameters = config[:parameters] || {}
  @headers = Utils.stringify_keys(config[:headers] || {})
  @timeout = config[:timeout] || SwarmSDK.config.agent_request_timeout
  @bypass_permissions = config[:bypass_permissions] || false
  @max_concurrent_tools = config[:max_concurrent_tools]
  # Always assume model exists - SwarmSDK validates models separately using models.json
  # This prevents RubyLLM from trying to validate models in its registry
  @assume_model_exists = true

  # disable_default_tools can be:
  # - nil/not set: include all default tools (default behavior)
  # - true: disable ALL default tools
  # - Array of symbols: disable specific tools (e.g., [:Think, :TodoWrite])
  @disable_default_tools = config[:disable_default_tools]

  # coding_agent defaults to false if not specified
  # When true, includes the base system prompt for coding tasks
  # When false, uses only the custom system prompt (no base prompt)
  @coding_agent = config.key?(:coding_agent) ? config[:coding_agent] : false

  # Parse directory first so it can be used in system prompt rendering
  @directory = parse_directory(config[:directory])

  # Extract plugin configurations (generic bucket for all plugin-specific keys)
  # This allows plugins to store their config without SDK knowing about them
  @plugin_configs = extract_plugin_configs(config)

  # Delegation isolation mode (default: false = isolated instances per delegation)
  @shared_across_delegations = config[:shared_across_delegations] || false

  # Build system prompt after directory and memory are set
  @system_prompt = build_full_system_prompt(config[:system_prompt])

  # Parse tools with permissions support
  @default_permissions = config[:default_permissions] || {}
  @agent_permissions = config[:permissions] || {}
  @tools = parse_tools_with_permissions(
    config[:tools],
    @default_permissions,
    @agent_permissions,
  )

  # Inject default write restrictions for security
  @tools = inject_default_write_permissions(@tools)

  @delegates_to = Array(config[:delegates_to] || []).map(&:to_sym).uniq
  @mcp_servers = Array(config[:mcp_servers] || [])

  # Parse hooks configuration
  # Handles both DSL (HookDefinition objects) and YAML (raw hash) formats
  @hooks = parse_hooks(config[:hooks])

  validate!
end

Instance Attribute Details

#agent_permissionsObject (readonly)

Returns the value of attribute agent_permissions.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def agent_permissions
  @agent_permissions
end

#api_versionObject (readonly)

Returns the value of attribute api_version.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def api_version
  @api_version
end

#assume_model_existsObject (readonly)

Returns the value of attribute assume_model_exists.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def assume_model_exists
  @assume_model_exists
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def base_url
  @base_url
end

#bypass_permissionsObject

Returns the value of attribute bypass_permissions.



45
46
47
# File 'lib/swarm_sdk/agent/definition.rb', line 45

def bypass_permissions
  @bypass_permissions
end

#coding_agentObject (readonly)

Returns the value of attribute coding_agent.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def coding_agent
  @coding_agent
end

#context_windowObject (readonly)

Returns the value of attribute context_window.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def context_window
  @context_window
end

#default_permissionsObject (readonly)

Returns the value of attribute default_permissions.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def default_permissions
  @default_permissions
end

#delegates_toObject (readonly)

Returns the value of attribute delegates_to.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def delegates_to
  @delegates_to
end

#descriptionObject (readonly)

Returns the value of attribute description.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def description
  @description
end

#directoryObject (readonly)

Returns the value of attribute directory.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def directory
  @directory
end

#disable_default_toolsObject (readonly)

Returns the value of attribute disable_default_tools.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def disable_default_tools
  @disable_default_tools
end

#headersObject (readonly)

Returns the value of attribute headers.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def headers
  @headers
end

#hooksObject (readonly)

Returns the value of attribute hooks.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def hooks
  @hooks
end

#max_concurrent_toolsObject

Returns the value of attribute max_concurrent_tools.



45
46
47
# File 'lib/swarm_sdk/agent/definition.rb', line 45

def max_concurrent_tools
  @max_concurrent_tools
end

#mcp_serversObject (readonly)

Returns the value of attribute mcp_servers.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def mcp_servers
  @mcp_servers
end

#modelObject (readonly)

Returns the value of attribute model.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def model
  @model
end

#nameObject (readonly)

Returns the value of attribute name.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def name
  @name
end

#parametersObject (readonly)

Returns the value of attribute parameters.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def parameters
  @parameters
end

#plugin_configsObject (readonly)

Returns the value of attribute plugin_configs.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def plugin_configs
  @plugin_configs
end

#providerObject (readonly)

Returns the value of attribute provider.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def provider
  @provider
end

#shared_across_delegationsObject (readonly)

Returns the value of attribute shared_across_delegations.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def shared_across_delegations
  @shared_across_delegations
end

#system_promptObject (readonly)

Returns the value of attribute system_prompt.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def system_prompt
  @system_prompt
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def timeout
  @timeout
end

#toolsObject (readonly)

Returns the value of attribute tools.



21
22
23
# File 'lib/swarm_sdk/agent/definition.rb', line 21

def tools
  @tools
end

Instance Method Details

#plugin_config(plugin_name) ⇒ Object?

Get plugin-specific configuration

Plugins store their configuration in the generic plugin_configs hash. This allows SDK to remain plugin-agnostic while plugins can store arbitrary configuration.

Examples:

agent_definition.plugin_config(:memory)
# => { directory: "tmp/memory", mode: :researcher }

Parameters:

  • plugin_name (Symbol)

    Plugin name (e.g., :memory)

Returns:

  • (Object, nil)

    Plugin configuration or nil if not present



142
143
144
# File 'lib/swarm_sdk/agent/definition.rb', line 142

def plugin_config(plugin_name)
  @plugin_configs[plugin_name.to_sym] || @plugin_configs[plugin_name.to_s]
end

#to_hObject



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/swarm_sdk/agent/definition.rb', line 146

def to_h
  # Core SDK configuration (always serialized)
  base_config = {
    name: @name,
    description: @description,
    model: SwarmSDK::Models.resolve_alias(@model), # Resolve model aliases
    context_window: @context_window,
    directory: @directory,
    tools: @tools,
    delegates_to: @delegates_to,
    system_prompt: @system_prompt,
    provider: @provider,
    base_url: @base_url,
    api_version: @api_version,
    mcp_servers: @mcp_servers,
    parameters: @parameters,
    headers: @headers,
    timeout: @timeout,
    bypass_permissions: @bypass_permissions,
    disable_default_tools: @disable_default_tools,
    coding_agent: @coding_agent,
    assume_model_exists: @assume_model_exists,
    max_concurrent_tools: @max_concurrent_tools,
    hooks: @hooks,
    shared_across_delegations: @shared_across_delegations,
    # Permissions are core SDK functionality (not plugin-specific)
    default_permissions: @default_permissions,
    permissions: @agent_permissions,
  }.compact

  # Allow plugins to contribute their config for serialization
  # This enables plugin features (memory, skills, etc.) to be preserved
  # when cloning agents without SwarmSDK knowing about plugin-specific fields
  plugin_configs = SwarmSDK::PluginRegistry.all.map do |plugin|
    plugin.serialize_config(agent_definition: self)
  end

  # Merge plugin configs into base config
  # Later plugins override earlier ones if they have conflicting keys
  plugin_configs.reduce(base_config) { |acc, config| acc.merge(config) }
end

#validateArray<Hash>

Validate agent configuration and return warnings (non-fatal issues)

Unlike validate! which raises exceptions for critical errors, this method returns an array of warning hashes for non-fatal issues like:

  • Model not found in registry (informs user, suggests alternatives)
  • Context tracking unavailable (useful even with assume_model_exists)

Note: Validation ALWAYS runs, even with assume_model_exists: true or base_url set. The purpose is to inform the user about potential issues and suggest corrections, not to block execution.

Returns:

  • (Array<Hash>)

    Array of warning hashes



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/swarm_sdk/agent/definition.rb', line 200

def validate
  warnings = []

  # Always validate model (even with assume_model_exists)
  # Warnings inform user about typos and context tracking limitations
  model_warning = validate_model
  warnings << model_warning if model_warning

  # Future: could add tool validation, delegate validation, etc.

  warnings
end