agent-harness 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +8 -0
- data/lib/agent_harness/provider_runtime.rb +115 -0
- data/lib/agent_harness/providers/adapter.rb +21 -0
- data/lib/agent_harness/providers/aider.rb +17 -0
- data/lib/agent_harness/providers/anthropic.rb +17 -0
- data/lib/agent_harness/providers/base.rb +38 -2
- data/lib/agent_harness/providers/codex.rb +23 -0
- data/lib/agent_harness/providers/cursor.rb +31 -2
- data/lib/agent_harness/providers/gemini.rb +19 -0
- data/lib/agent_harness/providers/github_copilot.rb +8 -0
- data/lib/agent_harness/providers/opencode.rb +23 -0
- data/lib/agent_harness/version.rb +1 -1
- data/lib/agent_harness.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 946d7c425aff8c96536bc30def7e0abdba7ff7d82020f4941674a8c93be63526
|
|
4
|
+
data.tar.gz: ffc9d707f89ab60bf9cc59b4e5ccbcd2570a3aaa07e97c5a10bfb731f5dc07a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c7b0dcef83c7a31be09a87884211a8ba03c0c93fb845e666423cd17eb70a358dd122db7e57ce04271fa5e114013adbc0007394211286c23b03cfa6a2a600c68f
|
|
7
|
+
data.tar.gz: a8f14eb24039afd0a93ec1eb064b59ebbe6d03aafd61ab1859c64729d2253140afebacc5c8ee761578337c671c074425784c33676808534cdf7b8ad809a2df32
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.6](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.5...agent-harness/v0.5.6) (2026-03-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* 53: Expose provider configuration capabilities for app-driven provider setup UIs ([#57](https://github.com/viamin/agent-harness/issues/57)) ([6aa6a02](https://github.com/viamin/agent-harness/commit/6aa6a02da14feefcad8761302d5fa8b5642a57fe))
|
|
9
|
+
* 54: Add per-request provider runtime overrides for CLI-backed providers ([#55](https://github.com/viamin/agent-harness/issues/55)) ([407467a](https://github.com/viamin/agent-harness/commit/407467a6965a01494e2c4590680b2bb9ddac6dce))
|
|
10
|
+
|
|
3
11
|
## [0.5.5](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.4...agent-harness/v0.5.5) (2026-03-29)
|
|
4
12
|
|
|
5
13
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AgentHarness
|
|
4
|
+
# Normalized runtime configuration for per-request provider overrides.
|
|
5
|
+
#
|
|
6
|
+
# ProviderRuntime lets callers pass a single, provider-agnostic payload
|
|
7
|
+
# into +send_message+ that each provider materializes into CLI args, env
|
|
8
|
+
# vars, or config files as needed.
|
|
9
|
+
#
|
|
10
|
+
# @example Routing OpenCode through OpenRouter with a specific model
|
|
11
|
+
# runtime = AgentHarness::ProviderRuntime.new(
|
|
12
|
+
# model: "anthropic/claude-opus-4.1",
|
|
13
|
+
# base_url: "https://openrouter.ai/api/v1",
|
|
14
|
+
# api_provider: "openrouter",
|
|
15
|
+
# env: { "OPENROUTER_API_KEY" => "sk-..." }
|
|
16
|
+
# )
|
|
17
|
+
# provider.send_message(prompt: "Hello", provider_runtime: runtime)
|
|
18
|
+
#
|
|
19
|
+
# @example Passing a Hash (auto-coerced by Base#send_message)
|
|
20
|
+
# provider.send_message(
|
|
21
|
+
# prompt: "Hello",
|
|
22
|
+
# provider_runtime: {
|
|
23
|
+
# model: "openai/gpt-5.3-codex",
|
|
24
|
+
# base_url: "https://openrouter.ai/api/v1"
|
|
25
|
+
# }
|
|
26
|
+
# )
|
|
27
|
+
class ProviderRuntime
|
|
28
|
+
attr_reader :model, :base_url, :api_provider, :env, :flags, :metadata
|
|
29
|
+
|
|
30
|
+
# @param model [String, nil] model identifier override
|
|
31
|
+
# @param base_url [String, nil] upstream API base URL override
|
|
32
|
+
# @param api_provider [String, nil] API-compatible backend name
|
|
33
|
+
# @param env [Hash<String,String>] extra environment variables for the subprocess
|
|
34
|
+
# @param flags [Array<String>] extra CLI flags to append
|
|
35
|
+
# @param metadata [Hash] arbitrary provider-specific data
|
|
36
|
+
def initialize(model: nil, base_url: nil, api_provider: nil, env: {}, flags: [], metadata: {})
|
|
37
|
+
@model = model
|
|
38
|
+
@base_url = base_url
|
|
39
|
+
@api_provider = api_provider
|
|
40
|
+
|
|
41
|
+
env_hash = env || {}
|
|
42
|
+
unless env_hash.is_a?(Hash)
|
|
43
|
+
raise ArgumentError, "env must be a Hash (got #{env_hash.class})"
|
|
44
|
+
end
|
|
45
|
+
normalized_env = env_hash.each_with_object({}) do |(key, value), acc|
|
|
46
|
+
string_key = key.to_s
|
|
47
|
+
unless value.is_a?(String)
|
|
48
|
+
raise ArgumentError, "env value for #{string_key.inspect} must be a String (got #{value.class})"
|
|
49
|
+
end
|
|
50
|
+
acc[string_key] = value
|
|
51
|
+
end
|
|
52
|
+
@env = normalized_env.freeze
|
|
53
|
+
|
|
54
|
+
normalized_flags = flags || []
|
|
55
|
+
unless normalized_flags.is_a?(Array)
|
|
56
|
+
raise ArgumentError, "flags must be an Array (got #{normalized_flags.class})"
|
|
57
|
+
end
|
|
58
|
+
normalized_flags = normalized_flags.dup
|
|
59
|
+
normalized_flags.each_with_index do |flag, index|
|
|
60
|
+
unless flag.is_a?(String)
|
|
61
|
+
raise ArgumentError,
|
|
62
|
+
"flags must be an Array of Strings; invalid element at index #{index}: #{flag.inspect} (#{flag.class})"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
@flags = normalized_flags.freeze
|
|
66
|
+
|
|
67
|
+
metadata_hash = metadata || {}
|
|
68
|
+
unless metadata_hash.is_a?(Hash)
|
|
69
|
+
raise ArgumentError, "metadata must be a Hash (got #{metadata_hash.class})"
|
|
70
|
+
end
|
|
71
|
+
@metadata = metadata_hash.dup.freeze
|
|
72
|
+
|
|
73
|
+
freeze
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Build a ProviderRuntime from a Hash.
|
|
77
|
+
#
|
|
78
|
+
# @param hash [Hash] runtime attributes
|
|
79
|
+
# @return [ProviderRuntime]
|
|
80
|
+
def self.from_hash(hash)
|
|
81
|
+
raise ArgumentError, "expected a Hash, got #{hash.class}" unless hash.is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
new(
|
|
84
|
+
model: hash[:model] || hash["model"],
|
|
85
|
+
base_url: hash[:base_url] || hash["base_url"],
|
|
86
|
+
api_provider: hash[:api_provider] || hash["api_provider"],
|
|
87
|
+
env: hash[:env] || hash["env"] || {},
|
|
88
|
+
flags: hash[:flags] || hash["flags"] || [],
|
|
89
|
+
metadata: hash[:metadata] || hash["metadata"] || {}
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Coerce a value into a ProviderRuntime.
|
|
94
|
+
#
|
|
95
|
+
# @param value [ProviderRuntime, Hash, nil] input
|
|
96
|
+
# @return [ProviderRuntime, nil]
|
|
97
|
+
def self.wrap(value)
|
|
98
|
+
case value
|
|
99
|
+
when ProviderRuntime then value
|
|
100
|
+
when Hash then from_hash(value)
|
|
101
|
+
when nil then nil
|
|
102
|
+
else
|
|
103
|
+
raise ArgumentError, "Cannot coerce #{value.class} into ProviderRuntime"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Whether any meaningful overrides are present.
|
|
108
|
+
#
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def empty?
|
|
111
|
+
model.nil? && base_url.nil? && api_provider.nil? &&
|
|
112
|
+
env.empty? && flags.empty? && metadata.empty?
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -75,11 +75,32 @@ module AgentHarness
|
|
|
75
75
|
# @option options [Integer] :timeout timeout in seconds
|
|
76
76
|
# @option options [String] :session session identifier
|
|
77
77
|
# @option options [Boolean] :dangerous_mode skip permission checks
|
|
78
|
+
# @option options [ProviderRuntime, Hash, nil] :provider_runtime per-request
|
|
79
|
+
# runtime overrides (model, base_url, api_provider, env, flags, metadata).
|
|
80
|
+
# For providers that delegate to Providers::Base#send_message, a plain Hash
|
|
81
|
+
# is automatically coerced into a ProviderRuntime. Providers that override
|
|
82
|
+
# #send_message directly are responsible for handling this option.
|
|
78
83
|
# @return [Response] response object with output and metadata
|
|
79
84
|
def send_message(prompt:, **options)
|
|
80
85
|
raise NotImplementedError, "#{self.class} must implement #send_message"
|
|
81
86
|
end
|
|
82
87
|
|
|
88
|
+
# Provider configuration schema for app-driven setup UIs
|
|
89
|
+
#
|
|
90
|
+
# Returns metadata describing the configurable fields, supported
|
|
91
|
+
# authentication modes, and backend compatibility for this provider.
|
|
92
|
+
# Applications use this to build generic provider-entry forms without
|
|
93
|
+
# hardcoding provider-specific knowledge.
|
|
94
|
+
#
|
|
95
|
+
# @return [Hash] with :fields, :auth_modes, :openai_compatible keys
|
|
96
|
+
def configuration_schema
|
|
97
|
+
{
|
|
98
|
+
fields: [],
|
|
99
|
+
auth_modes: [auth_type],
|
|
100
|
+
openai_compatible: false
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
|
|
83
104
|
# Provider capabilities
|
|
84
105
|
#
|
|
85
106
|
# @return [Hash] capability flags
|
|
@@ -59,6 +59,23 @@ module AgentHarness
|
|
|
59
59
|
"Aider"
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
def configuration_schema
|
|
63
|
+
{
|
|
64
|
+
fields: [
|
|
65
|
+
{
|
|
66
|
+
name: :model,
|
|
67
|
+
type: :string,
|
|
68
|
+
label: "Model",
|
|
69
|
+
required: false,
|
|
70
|
+
hint: "Model identifier (supports OpenAI, Anthropic, and other model names)",
|
|
71
|
+
accepts_arbitrary: true
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
auth_modes: [:api_key],
|
|
75
|
+
openai_compatible: false
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
62
79
|
def capabilities
|
|
63
80
|
{
|
|
64
81
|
streaming: true,
|
|
@@ -160,6 +160,23 @@ module AgentHarness
|
|
|
160
160
|
"Anthropic Claude CLI"
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
+
def configuration_schema
|
|
164
|
+
{
|
|
165
|
+
fields: [
|
|
166
|
+
{
|
|
167
|
+
name: :model,
|
|
168
|
+
type: :string,
|
|
169
|
+
label: "Model",
|
|
170
|
+
required: false,
|
|
171
|
+
hint: "Claude model to use (e.g. claude-3-5-sonnet-20241022)",
|
|
172
|
+
accepts_arbitrary: false
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
auth_modes: [:oauth],
|
|
176
|
+
openai_compatible: false
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
|
|
163
180
|
def capabilities
|
|
164
181
|
{
|
|
165
182
|
streaming: true,
|
|
@@ -87,10 +87,16 @@ module AgentHarness
|
|
|
87
87
|
#
|
|
88
88
|
# @param prompt [String] the prompt to send
|
|
89
89
|
# @param options [Hash] additional options
|
|
90
|
+
# @option options [ProviderRuntime, Hash, nil] :provider_runtime per-request
|
|
91
|
+
# runtime overrides (model, base_url, api_provider, env, flags, metadata).
|
|
92
|
+
# A plain Hash is automatically coerced into a ProviderRuntime.
|
|
90
93
|
# @return [Response] the response
|
|
91
94
|
def send_message(prompt:, **options)
|
|
92
95
|
log_debug("send_message_start", prompt_length: prompt.length, options: options.keys)
|
|
93
96
|
|
|
97
|
+
# Coerce provider_runtime from Hash if needed
|
|
98
|
+
options = normalize_provider_runtime(options)
|
|
99
|
+
|
|
94
100
|
# Normalize and validate MCP servers
|
|
95
101
|
options = normalize_mcp_servers(options)
|
|
96
102
|
validate_mcp_servers!(options[:mcp_servers]) if options[:mcp_servers]&.any?
|
|
@@ -108,6 +114,23 @@ module AgentHarness
|
|
|
108
114
|
|
|
109
115
|
# Parse response
|
|
110
116
|
response = parse_response(result, duration: duration)
|
|
117
|
+
runtime = options[:provider_runtime]
|
|
118
|
+
# Runtime model is a per-request override and always takes precedence
|
|
119
|
+
# over both the config-level model and whatever parse_response returned.
|
|
120
|
+
# This is intentional: callers use runtime overrides to route a single
|
|
121
|
+
# provider instance through different backends on each request.
|
|
122
|
+
if runtime&.model
|
|
123
|
+
response = Response.new(
|
|
124
|
+
output: response.output,
|
|
125
|
+
exit_code: response.exit_code,
|
|
126
|
+
duration: response.duration,
|
|
127
|
+
provider: response.provider,
|
|
128
|
+
model: runtime.model,
|
|
129
|
+
tokens: response.tokens,
|
|
130
|
+
metadata: response.metadata,
|
|
131
|
+
error: response.error
|
|
132
|
+
)
|
|
133
|
+
end
|
|
111
134
|
|
|
112
135
|
# Track tokens
|
|
113
136
|
track_tokens(response) if response.tokens
|
|
@@ -158,10 +181,16 @@ module AgentHarness
|
|
|
158
181
|
|
|
159
182
|
# Build environment variables - override in subclasses
|
|
160
183
|
#
|
|
184
|
+
# Provider subclasses should call +super+ and merge their own env vars
|
|
185
|
+
# so that ProviderRuntime env overrides are always included.
|
|
186
|
+
#
|
|
161
187
|
# @param options [Hash] options
|
|
162
188
|
# @return [Hash] environment variables
|
|
163
189
|
def build_env(options)
|
|
164
|
-
|
|
190
|
+
runtime = options[:provider_runtime]
|
|
191
|
+
return {} unless runtime
|
|
192
|
+
|
|
193
|
+
runtime.env.dup
|
|
165
194
|
end
|
|
166
195
|
|
|
167
196
|
# Parse CLI output into Response - override in subclasses
|
|
@@ -211,6 +240,13 @@ module AgentHarness
|
|
|
211
240
|
|
|
212
241
|
private
|
|
213
242
|
|
|
243
|
+
def normalize_provider_runtime(options)
|
|
244
|
+
raw = options[:provider_runtime]
|
|
245
|
+
return options if raw.nil? || raw.is_a?(ProviderRuntime)
|
|
246
|
+
|
|
247
|
+
options.merge(provider_runtime: ProviderRuntime.wrap(raw))
|
|
248
|
+
end
|
|
249
|
+
|
|
214
250
|
def normalize_mcp_servers(options)
|
|
215
251
|
servers = options[:mcp_servers]
|
|
216
252
|
return options if servers.nil?
|
|
@@ -252,7 +288,7 @@ module AgentHarness
|
|
|
252
288
|
|
|
253
289
|
AgentHarness.token_tracker.record(
|
|
254
290
|
provider: self.class.provider_name,
|
|
255
|
-
model: @config.model,
|
|
291
|
+
model: response.model || @config.model,
|
|
256
292
|
input_tokens: response.tokens[:input] || 0,
|
|
257
293
|
output_tokens: response.tokens[:output] || 0,
|
|
258
294
|
total_tokens: response.tokens[:total]
|
|
@@ -59,6 +59,14 @@ module AgentHarness
|
|
|
59
59
|
"OpenAI Codex CLI"
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
def configuration_schema
|
|
63
|
+
{
|
|
64
|
+
fields: [],
|
|
65
|
+
auth_modes: [:api_key],
|
|
66
|
+
openai_compatible: true
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
62
70
|
def capabilities
|
|
63
71
|
{
|
|
64
72
|
streaming: false,
|
|
@@ -211,11 +219,26 @@ module AgentHarness
|
|
|
211
219
|
cmd += session_flags(options[:session])
|
|
212
220
|
end
|
|
213
221
|
|
|
222
|
+
runtime = options[:provider_runtime]
|
|
223
|
+
if runtime
|
|
224
|
+
cmd += ["--model", runtime.model] if runtime.model
|
|
225
|
+
cmd += runtime.flags unless runtime.flags.empty?
|
|
226
|
+
end
|
|
227
|
+
|
|
214
228
|
cmd << prompt
|
|
215
229
|
|
|
216
230
|
cmd
|
|
217
231
|
end
|
|
218
232
|
|
|
233
|
+
def build_env(options)
|
|
234
|
+
env = super
|
|
235
|
+
runtime = options[:provider_runtime]
|
|
236
|
+
return env unless runtime
|
|
237
|
+
|
|
238
|
+
env["OPENAI_BASE_URL"] = runtime.base_url if runtime.base_url
|
|
239
|
+
env
|
|
240
|
+
end
|
|
241
|
+
|
|
219
242
|
def default_timeout
|
|
220
243
|
300
|
|
221
244
|
end
|
|
@@ -93,6 +93,14 @@ module AgentHarness
|
|
|
93
93
|
"Cursor AI"
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
def configuration_schema
|
|
97
|
+
{
|
|
98
|
+
fields: [],
|
|
99
|
+
auth_modes: [:oauth],
|
|
100
|
+
openai_compatible: false
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
|
|
96
104
|
def capabilities
|
|
97
105
|
{
|
|
98
106
|
streaming: false,
|
|
@@ -163,23 +171,44 @@ module AgentHarness
|
|
|
163
171
|
def send_message(prompt:, **options)
|
|
164
172
|
log_debug("send_message_start", prompt_length: prompt.length, options: options.keys)
|
|
165
173
|
|
|
174
|
+
# Coerce provider_runtime from Hash if needed (same as Base#send_message)
|
|
175
|
+
options = normalize_provider_runtime(options)
|
|
176
|
+
runtime = options[:provider_runtime]
|
|
177
|
+
|
|
166
178
|
# Normalize and validate MCP servers (same as Base#send_message)
|
|
167
179
|
options = normalize_mcp_servers(options)
|
|
168
180
|
validate_mcp_servers!(options[:mcp_servers]) if options[:mcp_servers]&.any?
|
|
169
181
|
|
|
170
182
|
# Build command (without prompt in args - we send via stdin)
|
|
171
183
|
command = [self.class.binary_name, "-p"]
|
|
184
|
+
command.concat(runtime.flags) if runtime&.flags&.any?
|
|
172
185
|
|
|
173
186
|
# Calculate timeout
|
|
174
187
|
timeout = options[:timeout] || @config.timeout || default_timeout
|
|
175
188
|
|
|
176
189
|
# Execute command with prompt on stdin
|
|
190
|
+
env = build_env(options)
|
|
177
191
|
start_time = Time.now
|
|
178
|
-
result = @executor.execute(command, timeout: timeout, stdin_data: prompt)
|
|
192
|
+
result = @executor.execute(command, timeout: timeout, stdin_data: prompt, env: env)
|
|
179
193
|
duration = Time.now - start_time
|
|
180
194
|
|
|
181
195
|
# Parse response
|
|
182
196
|
response = parse_response(result, duration: duration)
|
|
197
|
+
# Runtime model is a per-request override and always takes precedence
|
|
198
|
+
# over both the config-level model and whatever parse_response returned.
|
|
199
|
+
# See Base#send_message for rationale.
|
|
200
|
+
if runtime&.model
|
|
201
|
+
response = Response.new(
|
|
202
|
+
output: response.output,
|
|
203
|
+
exit_code: response.exit_code,
|
|
204
|
+
duration: response.duration,
|
|
205
|
+
provider: response.provider,
|
|
206
|
+
model: runtime.model,
|
|
207
|
+
tokens: response.tokens,
|
|
208
|
+
metadata: response.metadata,
|
|
209
|
+
error: response.error
|
|
210
|
+
)
|
|
211
|
+
end
|
|
183
212
|
|
|
184
213
|
# Track tokens
|
|
185
214
|
track_tokens(response) if response.tokens
|
|
@@ -201,7 +230,7 @@ module AgentHarness
|
|
|
201
230
|
end
|
|
202
231
|
|
|
203
232
|
def build_env(options)
|
|
204
|
-
|
|
233
|
+
super
|
|
205
234
|
end
|
|
206
235
|
|
|
207
236
|
def default_timeout
|
|
@@ -83,6 +83,25 @@ module AgentHarness
|
|
|
83
83
|
"Google Gemini"
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
def configuration_schema
|
|
87
|
+
{
|
|
88
|
+
fields: [
|
|
89
|
+
{
|
|
90
|
+
name: :model,
|
|
91
|
+
type: :string,
|
|
92
|
+
label: "Model",
|
|
93
|
+
required: false,
|
|
94
|
+
hint: "Gemini model to use (e.g. gemini-2.5-pro, gemini-2.0-flash)",
|
|
95
|
+
# accepts_arbitrary is true because supports_model_family? accepts
|
|
96
|
+
# any string starting with "gemini-", not just discovered models.
|
|
97
|
+
accepts_arbitrary: true
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
auth_modes: [:api_key, :oauth],
|
|
101
|
+
openai_compatible: false
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
86
105
|
def capabilities
|
|
87
106
|
{
|
|
88
107
|
streaming: true,
|
|
@@ -47,6 +47,14 @@ module AgentHarness
|
|
|
47
47
|
"OpenCode CLI"
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def configuration_schema
|
|
51
|
+
{
|
|
52
|
+
fields: [],
|
|
53
|
+
auth_modes: [:api_key],
|
|
54
|
+
openai_compatible: true
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
50
58
|
def capabilities
|
|
51
59
|
{
|
|
52
60
|
streaming: false,
|
|
@@ -80,10 +88,25 @@ module AgentHarness
|
|
|
80
88
|
|
|
81
89
|
def build_command(prompt, options)
|
|
82
90
|
cmd = [self.class.binary_name, "run"]
|
|
91
|
+
|
|
92
|
+
runtime = options[:provider_runtime]
|
|
93
|
+
if runtime
|
|
94
|
+
cmd += runtime.flags unless runtime.flags.empty?
|
|
95
|
+
end
|
|
96
|
+
|
|
83
97
|
cmd << prompt
|
|
84
98
|
cmd
|
|
85
99
|
end
|
|
86
100
|
|
|
101
|
+
def build_env(options)
|
|
102
|
+
env = super
|
|
103
|
+
runtime = options[:provider_runtime]
|
|
104
|
+
return env unless runtime
|
|
105
|
+
|
|
106
|
+
env["OPENAI_BASE_URL"] = runtime.base_url if runtime.base_url
|
|
107
|
+
env
|
|
108
|
+
end
|
|
109
|
+
|
|
87
110
|
def default_timeout
|
|
88
111
|
300
|
|
89
112
|
end
|
data/lib/agent_harness.rb
CHANGED
|
@@ -138,6 +138,7 @@ end
|
|
|
138
138
|
# Core components
|
|
139
139
|
require_relative "agent_harness/errors"
|
|
140
140
|
require_relative "agent_harness/mcp_server"
|
|
141
|
+
require_relative "agent_harness/provider_runtime"
|
|
141
142
|
require_relative "agent_harness/configuration"
|
|
142
143
|
require_relative "agent_harness/command_executor"
|
|
143
144
|
require_relative "agent_harness/docker_command_executor"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agent-harness
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bart Agapinan
|
|
@@ -92,6 +92,7 @@ files:
|
|
|
92
92
|
- lib/agent_harness/orchestration/provider_manager.rb
|
|
93
93
|
- lib/agent_harness/orchestration/rate_limiter.rb
|
|
94
94
|
- lib/agent_harness/provider_health_check.rb
|
|
95
|
+
- lib/agent_harness/provider_runtime.rb
|
|
95
96
|
- lib/agent_harness/providers/adapter.rb
|
|
96
97
|
- lib/agent_harness/providers/aider.rb
|
|
97
98
|
- lib/agent_harness/providers/anthropic.rb
|