Class: AgentHarness::Providers::Codex
- Inherits:
-
Base
- Object
- Base
- AgentHarness::Providers::Codex
show all
- Defined in:
- lib/agent_harness/providers/codex.rb
Overview
OpenAI Codex CLI provider
Provides integration with the OpenAI Codex CLI tool.
Constant Summary
collapse
- SUPPORTED_CLI_VERSION =
"0.116.0"
- SUPPORTED_CLI_REQUIREMENT =
Gem::Requirement.new(">= #{SUPPORTED_CLI_VERSION}", "< 0.117.0").freeze
Constants inherited
from Base
Base::COMMON_ERROR_PATTERNS, Base::DEFAULT_SMOKE_TEST_CONTRACT
Instance Attribute Summary
Attributes inherited from Base
#config, #executor, #logger
Class Method Summary
collapse
Instance Method Summary
collapse
Methods inherited from Base
#configure, #initialize, #sandboxed_environment?, #send_message
Methods included from Adapter
#auth_type, #build_mcp_flags, #fetch_mcp_servers, included, #parse_rate_limit_reset, #send_message, #smoke_test, #smoke_test_contract, #supported_mcp_transports, #supports_dangerous_mode?, #supports_mcp?, #validate_mcp_servers!
Class Method Details
.binary_name ⇒ Object
19
20
21
|
# File 'lib/agent_harness/providers/codex.rb', line 19
def binary_name
"codex"
end
|
.discover_models ⇒ Object
48
49
50
51
52
53
54
|
# File 'lib/agent_harness/providers/codex.rb', line 48
def discover_models
return [] unless available?
[
{name: "codex", family: "codex", tier: "standard", provider: "codex"}
]
end
|
.firewall_requirements ⇒ Object
28
29
30
31
32
33
34
35
36
|
# File 'lib/agent_harness/providers/codex.rb', line 28
def firewall_requirements
{
domains: [
"api.openai.com",
"openai.com"
],
ip_ranges: []
}
end
|
.installation_contract ⇒ Object
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
# File 'lib/agent_harness/providers/codex.rb', line 56
def installation_contract
default_package = "@openai/codex@#{SUPPORTED_CLI_VERSION}".freeze
install_command_prefix = ["npm", "install", "-g", "--ignore-scripts"].freeze
install_command = (install_command_prefix + [default_package]).freeze
supported_versions = [SUPPORTED_CLI_VERSION].freeze
version_requirement = SUPPORTED_CLI_REQUIREMENT.requirements
.map { |op, ver| "#{op} #{ver}".freeze }
.freeze
contract = {
source: :npm,
package: default_package,
package_name: "@openai/codex",
version: SUPPORTED_CLI_VERSION,
version_requirement: version_requirement,
binary_name: binary_name,
install_command_prefix: install_command_prefix,
install_command: install_command,
supported_versions: supported_versions
}
contract.each_value do |value|
value.freeze if value.is_a?(String)
end
contract.freeze
end
|
.instruction_file_paths ⇒ Object
38
39
40
41
42
43
44
45
46
|
# File 'lib/agent_harness/providers/codex.rb', line 38
def instruction_file_paths
[
{
path: "AGENTS.md",
description: "OpenAI Codex agent instructions",
symlink: false
}
]
end
|
.provider_name ⇒ Object
15
16
17
|
# File 'lib/agent_harness/providers/codex.rb', line 15
def provider_name
:codex
end
|
.smoke_test_contract ⇒ Object
Instance Method Details
#auth_status ⇒ Object
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
# File 'lib/agent_harness/providers/codex.rb', line 154
def auth_status
api_key = ENV["OPENAI_API_KEY"]
if api_key && !api_key.strip.empty?
if api_key.strip.start_with?("sk-")
return {valid: true, expires_at: nil, error: nil, auth_method: :api_key}
else
return {valid: false, expires_at: nil, error: "OPENAI_API_KEY is set but does not appear to be a valid OpenAI API key"}
end
end
credentials = read_codex_credentials
if credentials
key = credentials["api_key"] || credentials["apiKey"] || credentials["OPENAI_API_KEY"]
if key.is_a?(String) && !key.strip.empty?
if key.strip.start_with?("sk-")
return {valid: true, expires_at: nil, error: nil, auth_method: :config_file}
else
return {valid: false, expires_at: nil, error: "Config file API key is set but does not appear to be a valid OpenAI API key"}
end
end
end
{valid: false, expires_at: nil, error: "No OpenAI API key found. Set OPENAI_API_KEY or configure in #{codex_config_path}"}
rescue IOError, JSON::ParserError => e
{valid: false, expires_at: nil, error: e.message}
end
|
#capabilities ⇒ Object
104
105
106
107
108
109
110
111
112
113
114
|
# File 'lib/agent_harness/providers/codex.rb', line 104
def capabilities
{
streaming: false,
file_upload: false,
vision: false,
tool_use: true,
json_mode: false,
mcp: false,
dangerous_mode: true
}
end
|
#configuration_schema ⇒ Object
96
97
98
99
100
101
102
|
# File 'lib/agent_harness/providers/codex.rb', line 96
def configuration_schema
{
fields: [],
auth_modes: [:api_key],
openai_compatible: true
}
end
|
#dangerous_mode_flags ⇒ Object
116
117
118
|
# File 'lib/agent_harness/providers/codex.rb', line 116
def dangerous_mode_flags
["--full-auto"]
end
|
#display_name ⇒ Object
92
93
94
|
# File 'lib/agent_harness/providers/codex.rb', line 92
def display_name
"OpenAI Codex CLI"
end
|
#error_patterns ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
|
# File 'lib/agent_harness/providers/codex.rb', line 142
def error_patterns
COMMON_ERROR_PATTERNS.merge(
auth_expired: COMMON_ERROR_PATTERNS[:auth_expired] + [/401/, /incorrect.*api.*key/i],
transient: COMMON_ERROR_PATTERNS[:transient] + [/connection.*reset/i],
sandbox_failure: [
/bwrap.*no permissions/i,
/no permissions to create a new namespace/i,
/unprivileged.*namespace/i
]
)
end
|
#execution_semantics ⇒ Object
120
121
122
123
124
125
126
127
128
129
130
131
|
# File 'lib/agent_harness/providers/codex.rb', line 120
def execution_semantics
{
prompt_delivery: :arg,
output_format: :text,
sandbox_aware: true,
uses_subcommand: true,
non_interactive_flag: nil,
legitimate_exit_codes: [0],
stderr_is_diagnostic: true,
parses_rate_limit_reset: false
}
end
|
#health_status ⇒ Object
181
182
183
184
185
186
187
188
189
190
191
192
|
# File 'lib/agent_harness/providers/codex.rb', line 181
def health_status
unless self.class.available?
return {healthy: false, message: "Codex CLI not found in PATH. Install from https://github.com/openai/codex"}
end
auth = auth_status
unless auth[:valid]
return {healthy: false, message: auth[:error]}
end
{healthy: true, message: "Codex CLI available and authenticated"}
end
|
#name ⇒ Object
88
89
90
|
# File 'lib/agent_harness/providers/codex.rb', line 88
def name
"codex"
end
|
#session_flags(session_id) ⇒ Object
137
138
139
140
|
# File 'lib/agent_harness/providers/codex.rb', line 137
def session_flags(session_id)
return [] unless session_id && !session_id.empty?
["--session", session_id]
end
|
#supports_sessions? ⇒ Boolean
133
134
135
|
# File 'lib/agent_harness/providers/codex.rb', line 133
def supports_sessions?
true
end
|
#validate_config ⇒ Object
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
# File 'lib/agent_harness/providers/codex.rb', line 194
def validate_config
errors = []
flags = @config.default_flags
unless flags.nil?
if flags.is_a?(Array)
invalid = flags.reject { |f| f.is_a?(String) }
errors << "default_flags contains non-string values" if invalid.any?
else
errors << "default_flags must be an array of strings"
end
end
{valid: errors.empty?, errors: errors}
end
|