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)


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

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

#binary_nameString

CLI binary name

Returns:

  • (String)

    the name of the CLI binary

Raises:

  • (NotImplementedError)


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

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

#default_compatible_model_idString?

Optional default model the runner can recommend as a fallback when a requested model is unsupported or unknown. Providers override this when they own a stable default model for smoke tests or downstream tier fallback.

Returns:

  • (String, nil)


773
774
775
# File 'lib/agent_harness/providers/adapter.rb', line 773

def default_compatible_model_id
  nil
end

#discover_modelsArray<Hash>

Discover available models

Returns:

  • (Array<Hash>)

    list of available models



164
165
166
# File 'lib/agent_harness/providers/adapter.rb', line 164

def discover_models
  []
end

#firewall_requirementsHash

Required domains for firewall configuration

Returns:

  • (Hash)

    with :domains and :ip_ranges arrays



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

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



682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
# File 'lib/agent_harness/providers/adapter.rb', line 682

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



143
144
145
# File 'lib/agent_harness/providers/adapter.rb', line 143

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



182
183
184
# File 'lib/agent_harness/providers/adapter.rb', line 182

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



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/agent_harness/providers/adapter.rb', line 194

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



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

def instruction_file_paths
  []
end

#model_compatibility(model_id:, auth_mode: nil, cli_version: nil) ⇒ AgentHarness::ModelCompatibility::Result

Runner/model compatibility contract.

Returns a structured ModelCompatibility::Result describing whether this runner can execute model_id under the given auth_mode and installed cli_version. Providers that own CLI-gated or entitlement-gated models should override this to expose stable, structured facts so downstream orchestrators do not have to parse error strings or rediscover provider knowledge.

The default implementation returns an explicit :unknown result rather than collapsing to "supported" — callers are expected to handle unknown outcomes explicitly.

Parameters:

  • model_id (String, Symbol)

    the requested model identifier

  • auth_mode (Symbol, nil) (defaults to: nil)

    caller's auth mode (e.g. :api_key, :subscription)

  • cli_version (String, Gem::Version, nil) (defaults to: nil)

    installed CLI version, when known

Returns:



757
758
759
760
761
762
763
764
765
# File 'lib/agent_harness/providers/adapter.rb', line 757

def model_compatibility(model_id:, auth_mode: nil, cli_version: nil)
  AgentHarness::ModelCompatibility.unknown_result(
    runner: provider_name,
    model_id: model_id,
    auth_mode: auth_mode,
    cli_version: normalize_cli_version_for_compatibility(cli_version),
    fallback_model_id: default_compatible_model_id
  )
end

#normalize_cli_version_for_compatibility(value) ⇒ Object

Helper for #model_compatibility implementations: coerce a CLI version string into a Gem::Version when possible, returning the raw value otherwise. Keeps Result#cli_version stable across callers that pass either form.



781
782
783
784
785
786
787
788
789
790
791
792
793
# File 'lib/agent_harness/providers/adapter.rb', line 781

def normalize_cli_version_for_compatibility(value)
  return nil if value.nil?
  return value if value.is_a?(Gem::Version)

  str = value.respond_to?(:strip) ? value.strip : value.to_s
  return nil if str.empty?

  begin
    Gem::Version.new(str)
  rescue ArgumentError
    str
  end
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



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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/agent_harness/providers/adapter.rb', line 230

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)


334
335
336
# File 'lib/agent_harness/providers/adapter.rb', line 334

def 
  {}
end

#provider_nameSymbol

Human-readable provider name

Returns:

  • (Symbol)

    unique identifier for this provider

Raises:

  • (NotImplementedError)


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

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



734
735
736
# File 'lib/agent_harness/providers/adapter.rb', line 734

def smoke_test_contract
  nil
end