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)


99
100
101
# File 'lib/agent_harness/providers/adapter.rb', line 99

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

#binary_nameString

CLI binary name

Returns:

  • (String)

    the name of the CLI binary

Raises:

  • (NotImplementedError)


106
107
108
# File 'lib/agent_harness/providers/adapter.rb', line 106

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

#discover_modelsArray<Hash>

Discover available models

Returns:

  • (Array<Hash>)

    list of available models



138
139
140
# File 'lib/agent_harness/providers/adapter.rb', line 138

def discover_models
  []
end

#firewall_requirementsHash

Required domains for firewall configuration

Returns:

  • (Hash)

    with :domains and :ip_ranges arrays



124
125
126
# File 'lib/agent_harness/providers/adapter.rb', line 124

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



656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
# File 'lib/agent_harness/providers/adapter.rb', line 656

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



117
118
119
# File 'lib/agent_harness/providers/adapter.rb', line 117

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



156
157
158
# File 'lib/agent_harness/providers/adapter.rb', line 156

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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/agent_harness/providers/adapter.rb', line 168

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



131
132
133
# File 'lib/agent_harness/providers/adapter.rb', line 131

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



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
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/agent_harness/providers/adapter.rb', line 204

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_token_counting: (
        provider,
        :supports_token_counting?,
        default: default_supports_token_counting
      ),
      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
      )
    },
    chat: (provider)
  }

  (, )
end

#provider_metadata_overridesHash

Optional provider-specific metadata overrides for provider_metadata.

Returns:

  • (Hash)


308
309
310
# File 'lib/agent_harness/providers/adapter.rb', line 308

def 
  {}
end

#provider_nameSymbol

Human-readable provider name

Returns:

  • (Symbol)

    unique identifier for this provider

Raises:

  • (NotImplementedError)


92
93
94
# File 'lib/agent_harness/providers/adapter.rb', line 92

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



708
709
710
# File 'lib/agent_harness/providers/adapter.rb', line 708

def smoke_test_contract
  nil
end