Module: AgentHarness::Providers::Adapter::ClassMethods

Defined in:
lib/agent_harness/providers/adapter.rb

Overview

Class methods that all providers must implement

Constant Summary collapse

SUPPORTED_OAUTH_AUTH_STATUS_PROVIDERS =
%i[anthropic claude].freeze
IMMUTABLE_METADATA_OVERRIDE_KEYS =
%i[provider canonical_provider aliases binary_name].freeze

Instance Method Summary collapse

Instance Method Details

#available?Boolean

Check if provider CLI is available on the system

Returns:

  • (Boolean)

    true if the CLI is installed and accessible

Raises:

  • (NotImplementedError)


94
95
96
# File 'lib/agent_harness/providers/adapter.rb', line 94

def available?
  raise NotImplementedError, "#{self} must implement .available?"
end

#binary_nameString

CLI binary name

Returns:

  • (String)

    the name of the CLI binary

Raises:

  • (NotImplementedError)


101
102
103
# File 'lib/agent_harness/providers/adapter.rb', line 101

def binary_name
  raise NotImplementedError, "#{self} must implement .binary_name"
end

#discover_modelsArray<Hash>

Discover available models

Returns:

  • (Array<Hash>)

    list of available models



133
134
135
# File 'lib/agent_harness/providers/adapter.rb', line 133

def discover_models
  []
end

#firewall_requirementsHash

Required domains for firewall configuration

Returns:

  • (Hash)

    with :domains and :ip_ranges arrays



119
120
121
# File 'lib/agent_harness/providers/adapter.rb', line 119

def firewall_requirements
  {domains: [], ip_ranges: []}
end

#install_command(version: nil) ⇒ String, ...

Shell command for installing the provider CLI.

Parameters:

  • version (String, Symbol, nil) (defaults to: nil)

    optional install target/version

Returns:

  • (String, Array<String>, nil)

    shell command or argv, or nil when the provider does not expose an install contract



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/agent_harness/providers/adapter.rb', line 615

def install_command(version: nil)
   = (version: version)
  command = &.dig(:source, :command)
  return command if command

  contract = installation_contract
  return nil unless contract

  return contract[:install_command] unless version

  versioned_contract = versioned_installation_contract(version)
  if versioned_contract&.key?(:install_command)
    return versioned_contract[:install_command]
  end

  package_name = contract[:package_name]
  unless package_name
    raise ArgumentError, "installation_contract must define :package_name when overriding version"
  end

  requirement = contract[:version_requirement]
  if requirement
    requirement_args = Array(requirement).map do |entry|
      entry.is_a?(Array) ? "#{entry[0]} #{entry[1]}" : entry
    end
    parsed_requirement = Gem::Requirement.new(*requirement_args)
    parsed_version = begin
      Gem::Version.new(version)
    rescue ArgumentError
      raise ArgumentError,
        "Unsupported #{provider_name} CLI version #{version.inspect}; " \
        "supported versions must satisfy #{parsed_requirement}"
    end
    unless parsed_requirement.satisfied_by?(parsed_version)
      raise ArgumentError,
        "Unsupported #{provider_name} CLI version #{version.inspect}; " \
        "supported versions must satisfy #{parsed_requirement}"
    end
  end

  version_format = contract.fetch(:version_format, "%{package_name}@%{version}")
  package_with_version = format(version_format, package_name: package_name, version: version)

  Array(contract[:install_command_prefix]) + [package_with_version]
end

#install_contract(version: nil) ⇒ Hash?

Installation contract for the provider CLI.

Downstream applications can use this metadata to install a provider’s supported CLI without hardcoding package names, install flags, or version pins outside AgentHarness.

Returns:

  • (Hash, nil)

    installation metadata or nil when not provided



112
113
114
# File 'lib/agent_harness/providers/adapter.rb', line 112

def install_contract(version: nil)
  nil
end

#install_metadata(version: nil) ⇒ Hash?

First-class install metadata for the provider CLI.

Downstream applications can use this metadata to build provider images without hardcoding provider-specific install URLs, expected binary paths, or supported install targets.

This is separate from .installation_contract, which serves package-driven CLIs. Providers that need richer install metadata (e.g. shell-script installers, checksums, artifact URLs) should override this method.

Parameters:

  • version (String, Symbol, nil) (defaults to: nil)

    optional install target/version

Returns:

  • (Hash, nil)

    provider install metadata, or nil when the provider does not expose a first-class install contract



151
152
153
# File 'lib/agent_harness/providers/adapter.rb', line 151

def (version: nil)
  nil
end

#installation_contract(**options) ⇒ Hash?

Installation contract for package-driven provider CLIs.

Downstream apps can use this metadata to provision the provider CLI without hardcoding package names, versions, or binary expectations outside agent-harness.

Returns:

  • (Hash, nil)

    install metadata, or nil when no package-based installation contract is defined for the provider



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/agent_harness/providers/adapter.rb', line 163

def installation_contract(**options)
  return install_contract unless options.key?(:version)

  # Check if install_contract accepts the version: keyword before
  # forwarding it; legacy providers may override install_contract
  # without that parameter, which would raise ArgumentError.
  params = method(:install_contract).parameters
  accepts_version = params.any? do |type, name|
    # Only treat an explicit `version:` keyword as proof the method
    # handles version selection.  A bare `**options` keyrest does not
    # count — providers may add it for forward-compatibility without
    # actually acting on the version value.
    [:key, :keyreq].include?(type) && name == :version
  end

  if accepts_version
    install_contract(version: options[:version])
  else
    install_contract
  end
end

#instruction_file_pathsArray<Hash>

Paths to instruction files (e.g., CLAUDE.md, .cursorrules)

Returns:

  • (Array<Hash>)

    instruction file configurations



126
127
128
# File 'lib/agent_harness/providers/adapter.rb', line 126

def instruction_file_paths
  []
end

#provider_metadata(aliases: [], refresh: false, requested_name: provider_name, canonical_name: provider_name) ⇒ Hash

Stable provider metadata for downstream configuration and policy UIs.

This contract consolidates provider identifier aliases, auth/runtime details, installability, and health-check characteristics so apps do not need to maintain their own partial mirrors of adapter behavior.

Parameters:

  • aliases (Array<Symbol, String>) (defaults to: [])

    alternate identifiers registered for this provider

  • requested_name (Symbol, String) (defaults to: provider_name)

    provider identifier originally requested by the caller; used to prefer alias-keyed config when metadata construction is config-sensitive

  • canonical_name (Symbol, String) (defaults to: provider_name)

    canonical registry identifier for this provider; used for the public stable metadata contract

Returns:

  • (Hash)

    provider metadata



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/agent_harness/providers/adapter.rb', line 199

def (aliases: [], refresh: false, requested_name: provider_name, canonical_name: provider_name)
  normalized_aliases = (aliases, canonical_name: canonical_name)
  requested_provider_name = requested_name.to_sym
  canonical_provider_name = canonical_name.to_sym
  provider = (
    requested_name: requested_provider_name,
    canonical_name: canonical_provider_name
  )
  configuration = (
    default_configuration_schema,
    (provider, :configuration_schema, default: {})
  )
  execution = (
    default_execution_semantics,
    (provider, :execution_semantics, default: {})
  )
  installation = Adapter.(
    installation_contract,
    provider_name: canonical_provider_name,
    binary_name: binary_name
  )
  supported_auth_modes = Array(configuration[:auth_modes]).map(&:to_sym)
  supports_registry_checks = !provider.nil?
  auth_check_supported = auth_status_available?(
    provider,
    requested_name: requested_provider_name,
    canonical_name: canonical_provider_name,
    refresh: refresh
  )
  provider_status_check = supports_registry_checks && overrides_instance_method?(:health_status)
  configuration_validation = supports_registry_checks && overrides_instance_method?(:validate_config)
  lightweight_checks = supports_registry_checks && !provider_status_check && !configuration_validation

   = {
    provider: canonical_provider_name,
    canonical_provider: canonical_provider_name,
    aliases: normalized_aliases,
    display_name: provider_display_name(provider, canonical_name: canonical_provider_name),
    binary_name: binary_name,
    auth: {
      default_mode: (provider, supported_modes: supported_auth_modes),
      supported_modes: supported_auth_modes,
      service: nil,
      api_family: nil
    },
    runtime: {
      interface: :cli,
      requires_cli: true,
      available: (refresh: refresh),
      installable: !installation.nil?,
      installation: installation,
      prompt_delivery: execution[:prompt_delivery],
      output_format: execution[:output_format],
      sandbox_aware: execution[:sandbox_aware],
      uses_subcommand: execution[:uses_subcommand],
      supports_mcp: (provider, :supports_mcp?, default: default_supports_mcp),
      supported_mcp_transports: (
        provider,
        :supported_mcp_transports,
        default: default_supported_mcp_transports
      ),
      supports_sessions: (
        provider,
        :supports_sessions?,
        default: default_supports_sessions
      ),
      supports_dangerous_mode: (
        provider,
        :supports_dangerous_mode?,
        default: default_supports_dangerous_mode
      )
    },
    configuration: configuration,
    capabilities: (
      default_capabilities,
      (provider, :capabilities, default: {})
    ),
    health_check: {
      supports_registry_checks: supports_registry_checks,
      auth_check_supported: auth_check_supported,
      provider_status: provider_status_check,
      configuration_validation: configuration_validation,
      lightweight: lightweight_checks
    },
    identity: {
      bot_usernames: provider_bot_usernames(
        canonical_name: canonical_provider_name,
        aliases: normalized_aliases
      )
    }
  }

  (, )
end

#provider_metadata_overridesHash

Optional provider-specific metadata overrides for provider_metadata.

Returns:

  • (Hash)


297
298
299
# File 'lib/agent_harness/providers/adapter.rb', line 297

def 
  {}
end

#provider_nameSymbol

Human-readable provider name

Returns:

  • (Symbol)

    unique identifier for this provider

Raises:

  • (NotImplementedError)


87
88
89
# File 'lib/agent_harness/providers/adapter.rb', line 87

def provider_name
  raise NotImplementedError, "#{self} must implement .provider_name"
end

#smoke_test_contractHash?

Canonical smoke-test contract for this provider.

CLI-backed providers should expose a minimal real-execution prompt so downstream apps can reuse a stable provider-owned health check.

Returns:

  • (Hash, nil)

    smoke-test metadata or nil when not provided



667
668
669
# File 'lib/agent_harness/providers/adapter.rb', line 667

def smoke_test_contract
  nil
end