Module: AgentHarness

Defined in:
lib/agent_harness.rb,
lib/agent_harness/skill.rb,
lib/agent_harness/errors.rb,
lib/agent_harness/skills.rb,
lib/agent_harness/version.rb,
lib/agent_harness/response.rb,
lib/agent_harness/extensions.rb,
lib/agent_harness/mcp_server.rb,
lib/agent_harness/conversation.rb,
lib/agent_harness/providers/pi.rb,
lib/agent_harness/configuration.rb,
lib/agent_harness/token_tracker.rb,
lib/agent_harness/authentication.rb,
lib/agent_harness/error_taxonomy.rb,
lib/agent_harness/providers/base.rb,
lib/agent_harness/text_transport.rb,
lib/agent_harness/providers/aider.rb,
lib/agent_harness/providers/codex.rb,
lib/agent_harness/command_executor.rb,
lib/agent_harness/provider_runtime.rb,
lib/agent_harness/providers/cursor.rb,
lib/agent_harness/providers/gemini.rb,
lib/agent_harness/sub_agent_config.rb,
lib/agent_harness/mcp_config_loader.rb,
lib/agent_harness/providers/adapter.rb,
lib/agent_harness/providers/kilocode.rb,
lib/agent_harness/providers/opencode.rb,
lib/agent_harness/providers/registry.rb,
lib/agent_harness/providers/anthropic.rb,
lib/agent_harness/sub_agent_translator.rb,
lib/agent_harness/execution_preparation.rb,
lib/agent_harness/mcp_config_translator.rb,
lib/agent_harness/orchestration/metrics.rb,
lib/agent_harness/provider_health_check.rb,
lib/agent_harness/sub_agent_file_loader.rb,
lib/agent_harness/providers/mistral_vibe.rb,
lib/agent_harness/docker_command_executor.rb,
lib/agent_harness/orchestration/conductor.rb,
lib/agent_harness/providers/github_copilot.rb,
lib/agent_harness/orchestration/rate_limiter.rb,
lib/agent_harness/openai_compatible_transport.rb,
lib/agent_harness/orchestration/health_monitor.rb,
lib/agent_harness/orchestration/circuit_breaker.rb,
lib/agent_harness/providers/token_usage_parsing.rb,
lib/agent_harness/orchestration/provider_manager.rb,
lib/agent_harness/providers/mcp_config_file_support.rb,
lib/agent_harness/providers/rate_limit_reset_parsing.rb

Overview

AgentHarness provides a unified interface for CLI-based AI coding agents.

It offers:

  • Unified interface for multiple AI coding agents (Claude Code, Cursor, Gemini CLI, etc.)

  • Full orchestration layer with provider switching, circuit breakers, and health monitoring

  • Flexible configuration via YAML, Ruby DSL, or environment variables

  • Dynamic provider registration for custom provider support

  • Token usage tracking for cost and limit calculations

Examples:

Basic usage

AgentHarness.send_message("Write a hello world function", provider: :claude)

With configuration

AgentHarness.configure do |config|
  config.logger = Logger.new(STDOUT)
  config.default_provider = :cursor
end

Direct provider access

provider = AgentHarness.provider(:claude)
provider.send_message(prompt: "Hello")

Defined Under Namespace

Modules: Authentication, ErrorTaxonomy, Extensions, McpConfigTranslator, Orchestration, Providers, Skills, SubAgentTranslator Classes: AuthMismatchError, AuthenticationError, CallbackRegistry, CircuitBreakerConfig, CircuitOpenError, CommandExecutionError, CommandExecutor, Configuration, ConfigurationError, Conversation, DockerCommandExecutor, Error, ExecutionPreparation, ExtensionCompatibilityError, HealthCheckConfig, IdleTimeoutError, InvalidDurationError, McpConfigLoader, McpConfigurationError, McpServer, McpTransportUnsupportedError, McpUnsupportedError, NoProvidersAvailableError, OpenAICompatibleTransport, OrchestrationConfig, ProviderConfig, ProviderError, ProviderHealthCheck, ProviderNotFoundError, ProviderRuntime, ProviderUnavailableError, RateLimitConfig, RateLimitError, Response, RetryConfig, Skill, SubAgentConfig, SubAgentFileLoader, TextTransport, TimeoutError, TokenTracker, ToolDefinition, ToolRegistry, UnsupportedAuthFlowError

Constant Summary collapse

VERSION =
"0.18.0"

Class Method Summary collapse

Class Method Details

.auth_capabilities(provider_name) ⇒ Hash

Get authentication flow capabilities for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (Hash)

    capabilities with :auth_type, :auth_url, :refresh keys

Raises:



277
278
279
# File 'lib/agent_harness.rb', line 277

def auth_capabilities(provider_name)
  Authentication.auth_capabilities(provider_name)
end

.auth_status(provider_name) ⇒ Hash

Get detailed authentication status for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (Hash)

    status with :valid, :expires_at, :error keys



269
270
271
# File 'lib/agent_harness.rb', line 269

def auth_status(provider_name)
  Authentication.auth_status(provider_name)
end

.auth_url(provider_name) ⇒ String

Generate an OAuth URL for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (String)

    the OAuth authorization URL

Raises:



293
294
295
# File 'lib/agent_harness.rb', line 293

def auth_url(provider_name)
  Authentication.auth_url(provider_name)
end

.auth_url_supported?(provider_name) ⇒ Boolean

Check whether OAuth URL generation is supported for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (Boolean)

    true if auth_url can be called for the provider

Raises:



285
286
287
# File 'lib/agent_harness.rb', line 285

def auth_url_supported?(provider_name)
  Authentication.auth_url_supported?(provider_name)
end

.auth_valid?(provider_name) ⇒ Boolean

Check if authentication is valid for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (Boolean)

    true if auth is valid



262
263
264
# File 'lib/agent_harness.rb', line 262

def auth_valid?(provider_name)
  Authentication.auth_valid?(provider_name)
end

.build_config(name, **options) ⇒ ProviderConfig

Build a new ProviderConfig with defaults for the given provider

Parameters:

  • name (Symbol, String)

    the provider name

  • options (Hash)

    optional attribute overrides to merge

Returns:



167
168
169
170
171
# File 'lib/agent_harness.rb', line 167

def build_config(name, **options)
  config = ProviderConfig.new(name)
  config.merge!(options) unless options.empty?
  config
end

.check_provider(provider_name, timeout: nil, executor: nil, provider_runtime: nil) ⇒ Hash

Check health of a single provider

Parameters:

  • provider_name (Symbol)

    the provider name

  • timeout (Integer, nil) (defaults to: nil)

    timeout in seconds (nil lets ProviderHealthCheck apply its validated default)

Returns:

  • (Hash)

    health status with :name, :status, :message, :latency_ms



337
338
339
340
341
342
343
# File 'lib/agent_harness.rb', line 337

def check_provider(provider_name, timeout: nil, executor: nil, provider_runtime: nil)
  options = {}
  options[:timeout] = timeout unless timeout.nil?
  options[:executor] = executor unless executor.nil?
  options[:provider_runtime] = provider_runtime unless provider_runtime.nil?
  ProviderHealthCheck.check(provider_name, **options)
end

.check_providers(timeout: nil, executor: nil, provider_runtime: nil) ⇒ Array<Hash>

Check health of all configured providers.

Validates each enabled provider through registration, CLI availability, authentication, provider health status, and config validation checks.

Parameters:

  • timeout (Integer) (defaults to: nil)

    timeout in seconds for each check (defaults to configured value)

Returns:

  • (Array<Hash>)

    health status for each provider

Raises:

  • (ArgumentError)

    if provider_runtime is supplied; runtime overrides are only supported by ‘check_provider` to avoid leaking one provider’s execution context into every other health check



324
325
326
327
328
329
330
331
# File 'lib/agent_harness.rb', line 324

def check_providers(timeout: nil, executor: nil, provider_runtime: nil)
  raise ArgumentError, "provider_runtime is only supported for single-provider health checks" unless provider_runtime.nil?

  options = {}
  options[:timeout] = timeout unless timeout.nil?
  options[:executor] = executor unless executor.nil?
  ProviderHealthCheck.check_all(**options)
end

.conductorOrchestration::Conductor

Returns the global conductor for orchestrated requests

Returns:



67
68
69
# File 'lib/agent_harness.rb', line 67

def conductor
  @conductor ||= Orchestration::Conductor.new(config: configuration)
end

.configurationConfiguration

Returns the global configuration instance

Returns:



33
34
35
# File 'lib/agent_harness.rb', line 33

def configuration
  @configuration ||= Configuration.new
end

.configure {|Configuration| ... } ⇒ void

This method returns an undefined value.

Configure AgentHarness with a block

Yields:



40
41
42
# File 'lib/agent_harness.rb', line 40

def configure
  yield(configuration) if block_given?
end

.discover_extensions(directory) ⇒ Array<Extensions::Base>

Discover and register all extensions found in a directory.

Parameters:

  • directory (String)

    directory to scan

Returns:



102
103
104
# File 'lib/agent_harness.rb', line 102

def discover_extensions(directory)
  configuration.discover_extensions(directory)
end

.extension(reference) ⇒ Extensions::Base

Resolve a canonical extension definition by name or inline object.

Parameters:

Returns:



85
86
87
# File 'lib/agent_harness.rb', line 85

def extension(reference)
  configuration.resolve_extension(reference)
end

.extension_compatibility(provider:, extensions:) ⇒ Array<Extensions::CompatibilityReport>

Build a compatibility report for extensions against a provider.

Parameters:

Returns:



111
112
113
114
115
116
117
118
119
120
# File 'lib/agent_harness.rb', line 111

def extension_compatibility(provider:, extensions:)
  provider_instance = provider.is_a?(Providers::Base) ? provider : self.provider(provider)

  Array(extensions).map do |extension_ref|
    Extensions::Compatibility.report(
      provider: provider_instance,
      extension: extension(extension_ref)
    )
  end
end

.install_contract(name) ⇒ Hash

Get install contract metadata for a provider

Parameters:

  • name (Symbol, String)

    the provider name

Returns:

  • (Hash)

    install contract metadata

Raises:



177
178
179
# File 'lib/agent_harness.rb', line 177

def install_contract(name)
  Providers::Registry.instance.install_contract(name)
end

.installation_contract(provider_name, **options) ⇒ Hash?

Get installation metadata for a provider CLI.

Parameters:

  • provider_name (Symbol, String)

    the provider name

  • options (Hash)

    optional target selection (for example, ‘version:`)

Returns:

  • (Hash, nil)

    installation contract

Raises:



205
206
207
# File 'lib/agent_harness.rb', line 205

def installation_contract(provider_name, **options)
  Providers::Registry.instance.installation_contract(provider_name, **options)
end

.installation_contractsHash<Symbol, Hash>

Get all provider installation contracts exposed by agent-harness.

Returns:

  • (Hash<Symbol, Hash>)

    installation contracts keyed by provider



211
212
213
# File 'lib/agent_harness.rb', line 211

def installation_contracts
  Providers::Registry.instance.installation_contracts
end

.load_extensions(path, adapter: nil) ⇒ Array<Extensions::Base>

Load one or more extensions from disk through an adapter.

Parameters:

  • path (String)

    extension file, directory, or package root

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

    optional explicit adapter

Returns:



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

def load_extensions(path, adapter: nil)
  configuration.load_extensions(path, adapter: adapter)
end

.loggerLogger?

Returns the global logger

Returns:

  • (Logger, nil)

    the configured logger



55
56
57
# File 'lib/agent_harness.rb', line 55

def logger
  configuration.logger
end

.provider(name) ⇒ Providers::Base

Get a provider instance

Parameters:

  • name (Symbol)

    the provider name

Returns:



142
143
144
# File 'lib/agent_harness.rb', line 142

def provider(name)
  conductor.provider_manager.get_provider(name)
end

.provider_class(name) ⇒ Class

Look up the provider class for a given name or alias

Parameters:

  • name (Symbol, String)

    the provider name or alias

Returns:

  • (Class)

    the provider class

Raises:



158
159
160
# File 'lib/agent_harness.rb', line 158

def provider_class(name)
  Providers::Registry.instance.get(name)
end

.provider_install_contract(provider_name, version: nil) ⇒ Hash?

Returns install metadata for a provider CLI when the provider exposes it.

Parameters:

  • provider_name (Symbol, String)

    the provider name

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

    optional explicit CLI version override

Returns:

  • (Hash, nil)

    installation metadata



186
187
188
# File 'lib/agent_harness.rb', line 186

def provider_install_contract(provider_name, version: nil)
  provider_installation_contract(provider_name, **(version ? {version: version} : {}))
end

.provider_installation_contract(name, **options) ⇒ Hash?

Get the installation contract for a provider CLI.

Parameters:

  • name (Symbol, String)

    the provider name

  • options (Hash)

    optional target selection (for example, ‘version:`)

Returns:

  • (Hash, nil)

    provider installation contract for the requested target

Raises:



196
197
198
# File 'lib/agent_harness.rb', line 196

def provider_installation_contract(name, **options)
  Providers::Registry.instance.installation_contract(name, **options)
end

.provider_metadata(provider_name, refresh: false) ⇒ Hash

Get consolidated metadata for a provider.

Parameters:

  • provider_name (Symbol, String)

    the provider name or alias

  • refresh (Boolean) (defaults to: false)

    when true, refresh live runtime metadata such as CLI availability instead of reusing cached values

Returns:

  • (Hash)

    provider metadata

Raises:



222
223
224
# File 'lib/agent_harness.rb', line 222

def (provider_name, refresh: false)
  Providers::Registry.instance.(provider_name, refresh: refresh)
end

.provider_metadata_catalog(refresh: false) ⇒ Hash<Symbol, Hash>

Get consolidated metadata for all registered providers.

Parameters:

  • refresh (Boolean) (defaults to: false)

    when true, refresh live runtime metadata such as CLI availability instead of reusing cached values

Returns:

  • (Hash<Symbol, Hash>)

    provider metadata keyed by canonical provider



231
232
233
# File 'lib/agent_harness.rb', line 231

def (refresh: false)
  Providers::Registry.instance.(refresh: refresh)
end

.provider_smoke_test_contract(provider_name) ⇒ Hash?

Get smoke-test metadata for a provider CLI when the provider exposes it.

Parameters:

  • provider_name (Symbol, String)

    the provider name

Returns:

  • (Hash, nil)

    smoke-test contract



239
240
241
# File 'lib/agent_harness.rb', line 239

def provider_smoke_test_contract(provider_name)
  smoke_test_contract(provider_name)
end

.providersArray<Symbol>

List all registered provider names

Returns:

  • (Array<Symbol>)

    canonical provider names



149
150
151
# File 'lib/agent_harness.rb', line 149

def providers
  Providers::Registry.instance.all
end

.refresh_auth(provider_name, token: nil) ⇒ Hash

Refresh authentication credentials for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

  • token (String, nil) (defaults to: nil)

    OAuth token to store

Returns:

  • (Hash)

    result with :success key

Raises:



310
311
312
# File 'lib/agent_harness.rb', line 310

def refresh_auth(provider_name, token: nil)
  Authentication.refresh_auth(provider_name, token: token)
end

.refresh_auth_supported?(provider_name) ⇒ Boolean

Check whether credential refresh is supported for a provider

Parameters:

  • provider_name (Symbol)

    the provider name

Returns:

  • (Boolean)

    true if refresh_auth can be called for the provider

Raises:



301
302
303
# File 'lib/agent_harness.rb', line 301

def refresh_auth_supported?(provider_name)
  Authentication.refresh_auth_supported?(provider_name)
end

.reset!void

This method returns an undefined value.

Reset configuration to defaults (useful for testing)



46
47
48
49
50
51
# File 'lib/agent_harness.rb', line 46

def reset!
  @configuration = nil
  @conductor = nil
  @token_tracker = nil
  Skills.reset! if defined?(Skills)
end

.send_message(prompt, provider: nil, executor: nil, **options) ⇒ Response

Send a message using the orchestration layer

Parameters:

  • prompt (String)

    the prompt to send

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

    optional provider override

  • executor (CommandExecutor, nil) (defaults to: nil)

    per-request executor override

  • options (Hash)

    additional options

Returns:

  • (Response)

    the response from the provider



77
78
79
# File 'lib/agent_harness.rb', line 77

def send_message(prompt, provider: nil, executor: nil, **options)
  conductor.send_message(prompt, provider: provider, executor: executor, **options)
end

.smoke_test_contract(provider_name) ⇒ Hash?

Get smoke-test metadata for a provider CLI.

Parameters:

  • provider_name (Symbol, String)

    the provider name

Returns:

  • (Hash, nil)

    smoke-test contract

Raises:



247
248
249
250
251
# File 'lib/agent_harness.rb', line 247

def smoke_test_contract(provider_name)
  # Explicitly raise if provider is not registered to match documentation
  raise ConfigurationError, "Unknown provider: #{provider_name}" unless Providers::Registry.instance.registered?(provider_name)
  Providers::Registry.instance.smoke_test_contract(provider_name)
end

.smoke_test_contractsHash<Symbol, Hash>

Get all provider smoke-test contracts exposed by agent-harness.

Returns:

  • (Hash<Symbol, Hash>)

    smoke-test contracts keyed by provider



255
256
257
# File 'lib/agent_harness.rb', line 255

def smoke_test_contracts
  Providers::Registry.instance.smoke_test_contracts
end

.sub_agent(reference) ⇒ SubAgentConfig

Resolve a canonical sub-agent definition by name or inline payload.

Parameters:

Returns:



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

def sub_agent(reference)
  configuration.resolve_sub_agent(reference)
end

.token_trackerTokenTracker

Returns the global token tracker

Returns:



61
62
63
# File 'lib/agent_harness.rb', line 61

def token_tracker
  @token_tracker ||= TokenTracker.new
end

.translate_sub_agent(reference, provider:) ⇒ Hash

Translate a canonical sub-agent definition into a provider-specific format.

Parameters:

  • reference (Symbol, String, Hash, SubAgentConfig)

    sub-agent reference

  • provider (Symbol, String)

    target provider

Returns:

  • (Hash)

    provider-specific sub-agent definition



135
136
137
# File 'lib/agent_harness.rb', line 135

def translate_sub_agent(reference, provider:)
  SubAgentTranslator.for_provider(provider, sub_agent(reference))
end