agent-harness 0.28.6 → 0.29.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0462522eb82540a1cc498c27663441e6aab837cabab94921e7deed4f31e2f29
4
- data.tar.gz: 14a84df84773fd4a64597d46733e4c4a0297ec3b4102dde5a9ded015e3a451e1
3
+ metadata.gz: e929940987f238b75b654eadf2d882b0a554fb755c3badac115c7ae218875677
4
+ data.tar.gz: a83fefa5de68968675304ed70316ccfa37a7b3063bd58a1edf3b8c493f017daf
5
5
  SHA512:
6
- metadata.gz: 34f7e74dd1f62aed14782936a1d794c4e2406ddc05f274dbe39e28ac7938f7b00387cfdc05e7fb4c94bd1c3e79294fd9cbdfe25a0e8c235d6340b04de47e048f
7
- data.tar.gz: 7a3946d5a75fa84c6502e02ce409325e870c0bbd0a23a7ea8319cffbfe75128a9bf8c1889b8d9747c0f8fef13f04dc9e625ce7969f60a92b3728e75041e496ac
6
+ metadata.gz: a08f5ce50f0b0c200034d53908ded075808f7a2e374aff7365d54240cf1dc291d1cde28d4e1d4e76cc2d16273aa30bb17f81384243d626ce60fd5c5fd6d2f702
7
+ data.tar.gz: 615fec572434e6a0653f3ae3cd1723f551d7578eb30ef2f1ee0a2714c6221be8acd29c06e3830d10f0e88c040c31faab51658c26b09b79e18215f295587c644e
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.28.6"
2
+ ".": "0.29.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -6,6 +6,13 @@
6
6
  * **auth:** add provider-owned PKCE code-exchange API for Claude OAuth (`AgentHarness::Authentication.exchange_code`). Takes an authorization code plus PKCE verifier (and `redirect_uri`/`client_id`), posts an `authorization_code` grant to the Claude token endpoint, and persists the resulting access/refresh tokens in the native `claudeAiOauth` shape. Adds `exchange_code_supported?` and a `code_exchange` key to `auth_capabilities` ([#266](https://github.com/viamin/agent-harness/issues/266)).
7
7
 
8
8
 
9
+ ## [0.29.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.6...agent-harness/v0.29.0) (2026-07-16)
10
+
11
+
12
+ ### Features
13
+
14
+ * Add Oh My Pi provider metadata and install contract ([#298](https://github.com/viamin/agent-harness/issues/298)) ([3be62a8](https://github.com/viamin/agent-harness/commit/3be62a884f50ceeb903e199f4a9906cf3fecf014))
15
+
9
16
  ## [0.28.6](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.5...agent-harness/v0.28.6) (2026-07-12)
10
17
 
11
18
 
data/README.md CHANGED
@@ -5,7 +5,7 @@ A unified Ruby interface for CLI-based AI coding agents like Claude Code, Cursor
5
5
  ## Features
6
6
 
7
7
  - **Unified Interface**: Single API for multiple AI coding agents
8
- - **10 Built-in Providers**: Claude Code, Cursor, Gemini CLI, GitHub Copilot, Codex, Pi, Aider, OpenCode, Kilocode, Mistral Vibe
8
+ - **11 Built-in Providers**: Claude Code, Cursor, Gemini CLI, GitHub Copilot, Codex, Pi, Oh My Pi, Aider, OpenCode, Kilocode, Mistral Vibe
9
9
  - **Full Orchestration**: Provider switching, circuit breakers, rate limiting, and health monitoring
10
10
  - **Flexible Configuration**: YAML, Ruby DSL, or environment variables
11
11
  - **Token Tracking**: Monitor usage across providers for cost and limit management
@@ -105,6 +105,7 @@ end
105
105
  | `:github_copilot` | `copilot` | GitHub Copilot CLI |
106
106
  | `:codex` | `codex` | OpenAI Codex CLI |
107
107
  | `:pi` | `pi` | Pi coding agent CLI |
108
+ | `:omp` | `omp` | Oh My Pi coding agent CLI (Bun-based fork of Pi) |
108
109
  | `:aider` | `aider` | Aider coding assistant |
109
110
  | `:opencode` | `opencode` | OpenCode CLI |
110
111
  | `:kilocode` | `kilo` | Kilocode CLI |
@@ -188,7 +189,7 @@ puts contract[:supported_versions][:requirement]
188
189
 
189
190
  # List all registered providers
190
191
  AgentHarness::Providers::Registry.instance.all
191
- # => [:claude, :cursor, :gemini, :github_copilot, :pi, :codex, :opencode, :kilocode, :aider, :mistral_vibe]
192
+ # => [:claude, :cursor, :gemini, :github_copilot, :pi, :omp, :codex, :opencode, :kilocode, :aider, :mistral_vibe]
192
193
  ```
193
194
 
194
195
  For Claude, the install contract is the first-class source of truth for:
@@ -39,9 +39,35 @@ module AgentHarness
39
39
  normalized[:requires_postinstall] = contract[:requires_postinstall] if contract.key?(:requires_postinstall)
40
40
  normalized[:postinstall_command] = contract[:postinstall_command] if contract.key?(:postinstall_command)
41
41
 
42
+ if contract.key?(:runtime_requirements)
43
+ normalized[:runtime_requirements] =
44
+ normalize_metadata_runtime_requirements(contract[:runtime_requirements])
45
+ end
46
+
42
47
  normalized
43
48
  end
44
49
 
50
+ # Normalize an installation contract's `runtime_requirements` so the
51
+ # stable provider-metadata path exposes the same runtime prerequisites
52
+ # (e.g. Bun for the :omp provider) as the raw installation contract.
53
+ #
54
+ # Each entry is duplicated so consumers receive a self-contained hash
55
+ # they can mutate top-level keys on without aliasing the provider-owned
56
+ # contract, and the resulting array is frozen to signal that the
57
+ # normalized metadata is read-only.
58
+ #
59
+ # @param requirements [Array<Hash>, nil]
60
+ # @return [Array<Hash>, nil] normalized requirements, or nil when absent
61
+ def self.normalize_metadata_runtime_requirements(requirements)
62
+ return requirements unless requirements.is_a?(Array)
63
+
64
+ requirements.map do |requirement|
65
+ next requirement.dup if requirement.is_a?(Hash)
66
+
67
+ requirement
68
+ end.freeze
69
+ end
70
+
45
71
  def self.normalize_metadata_source_type(source)
46
72
  return source[:type]&.to_sym if source.is_a?(Hash)
47
73
 
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/requirement"
4
+
5
+ module AgentHarness
6
+ module Providers
7
+ # Oh My Pi coding agent CLI provider
8
+ #
9
+ # Provides integration with the Oh My Pi (can1357/oh-my-pi) terminal
10
+ # coding agent, a Bun-based fork of the Pi coding agent. Distinct from
11
+ # the {Pi} provider, which targets the upstream @mariozechner/pi-coding-agent
12
+ # CLI.
13
+ class OhMyPi < Base
14
+ CLI_PACKAGE = "@oh-my-pi/pi-coding-agent"
15
+ SUPPORTED_CLI_VERSION = "17.0.1"
16
+ SUPPORTED_CLI_REQUIREMENT = Gem::Requirement.new("= #{SUPPORTED_CLI_VERSION}").freeze
17
+
18
+ # Bun runtime requirements. The omp entrypoint is
19
+ # `#!/usr/bin/env bun` and the published package metadata requires
20
+ # Bun `>= 1.3.14`. Consumers must provision a compatible Bun runtime
21
+ # before installing the @oh-my-pi/pi-coding-agent package.
22
+ #
23
+ # The `bun` npm package relies on its `postinstall` script to fetch the
24
+ # platform binary; installing it with `npm install --ignore-scripts`
25
+ # ships only the Windows shims (`bin/bun.exe`, `bunx.exe`) and leaves
26
+ # Linux/macOS without a working `bun` binary. Provision Bun via the
27
+ # official installer script instead, which fetches the correct
28
+ # platform binary directly.
29
+ BUN_BINARY = "bun"
30
+ BUN_INSTALL_SCRIPT_URL = "https://bun.sh/install"
31
+ SUPPORTED_BUN_VERSION = "1.3.14"
32
+ BUN_REQUIREMENT_STRING = ">= #{SUPPORTED_BUN_VERSION}".freeze
33
+
34
+ class << self
35
+ def provider_name
36
+ :omp
37
+ end
38
+
39
+ def binary_name
40
+ "omp"
41
+ end
42
+
43
+ def available?
44
+ executor = AgentHarness.configuration.command_executor
45
+ !!executor.which(binary_name)
46
+ end
47
+
48
+ def provider_metadata_overrides
49
+ {
50
+ auth: {
51
+ service: :omp,
52
+ api_family: :multi_provider
53
+ }
54
+ }
55
+ end
56
+
57
+ def firewall_requirements
58
+ {
59
+ domains: [
60
+ "pi.dev"
61
+ ],
62
+ ip_ranges: []
63
+ }
64
+ end
65
+
66
+ def instruction_file_paths
67
+ [
68
+ {
69
+ path: "AGENTS.md",
70
+ description: "Oh My Pi agent instructions",
71
+ symlink: false
72
+ },
73
+ {
74
+ path: "SYSTEM.md",
75
+ description: "Oh My Pi system prompt override",
76
+ symlink: false
77
+ }
78
+ ]
79
+ end
80
+
81
+ def discover_models
82
+ return [] unless available?
83
+ []
84
+ end
85
+
86
+ def bun_runtime_contract
87
+ # The official Bun installer reads `BUN_VERSION` to pin the release
88
+ # it downloads, and fetches the platform-appropriate binary itself.
89
+ install_command_prefix = ["sh", "-c"].freeze
90
+ inner_script = "curl -fsSL #{BUN_INSTALL_SCRIPT_URL} | " \
91
+ "BUN_VERSION=#{SUPPORTED_BUN_VERSION} bash"
92
+ install_command = (install_command_prefix + [inner_script]).freeze
93
+
94
+ {
95
+ name: :bun,
96
+ binary_name: BUN_BINARY,
97
+ pinned_version: SUPPORTED_BUN_VERSION,
98
+ version_requirement: BUN_REQUIREMENT_STRING,
99
+ source: :script,
100
+ install_script_url: BUN_INSTALL_SCRIPT_URL,
101
+ install_command_prefix: install_command_prefix,
102
+ install_command: install_command,
103
+ install_command_string: inner_script,
104
+ rationale: "omp entrypoint is #!/usr/bin/env bun; the bun npm package relies on " \
105
+ "its postinstall script to fetch the platform binary, so install Bun " \
106
+ "via the official installer script rather than npm --ignore-scripts"
107
+ }.freeze
108
+ end
109
+
110
+ def installation_contract(version: SUPPORTED_CLI_VERSION)
111
+ version = version.strip if version.respond_to?(:strip)
112
+
113
+ unless version.is_a?(String) && !version.empty?
114
+ raise ArgumentError,
115
+ "Unsupported Oh My Pi CLI version #{version.inspect}; " \
116
+ "supported versions must satisfy #{SUPPORTED_CLI_REQUIREMENT}"
117
+ end
118
+
119
+ parsed_version = begin
120
+ Gem::Version.new(version)
121
+ rescue ArgumentError
122
+ raise ArgumentError,
123
+ "Unsupported Oh My Pi CLI version #{version.inspect}; " \
124
+ "supported versions must satisfy #{SUPPORTED_CLI_REQUIREMENT}"
125
+ end
126
+
127
+ unless SUPPORTED_CLI_REQUIREMENT.satisfied_by?(parsed_version)
128
+ raise ArgumentError,
129
+ "Unsupported Oh My Pi CLI version #{version.inspect}; " \
130
+ "supported versions must satisfy #{SUPPORTED_CLI_REQUIREMENT}"
131
+ end
132
+
133
+ package = "#{CLI_PACKAGE}@#{version}".freeze
134
+ install_command_prefix = ["npm", "install", "-g", "--ignore-scripts"].freeze
135
+ install_command = (install_command_prefix + [package]).freeze
136
+ supported_versions = [version].freeze
137
+ version_requirement = SUPPORTED_CLI_REQUIREMENT.requirements
138
+ .map { |op, ver| "#{op} #{ver}".freeze }
139
+ .freeze
140
+
141
+ contract = {
142
+ source: :npm,
143
+ package: package,
144
+ package_name: CLI_PACKAGE,
145
+ version: version,
146
+ version_requirement: version_requirement,
147
+ binary_name: binary_name,
148
+ install_command_prefix: install_command_prefix,
149
+ install_command: install_command,
150
+ supported_versions: supported_versions,
151
+ runtime_requirements: [bun_runtime_contract]
152
+ }
153
+
154
+ contract.each_value do |value|
155
+ value.freeze if value.is_a?(String)
156
+ end
157
+ contract.freeze
158
+ end
159
+
160
+ def smoke_test_contract
161
+ Base::DEFAULT_SMOKE_TEST_CONTRACT
162
+ end
163
+ end
164
+
165
+ def name
166
+ "omp"
167
+ end
168
+
169
+ def display_name
170
+ "Oh My Pi"
171
+ end
172
+
173
+ def configuration_schema
174
+ {
175
+ fields: [
176
+ {
177
+ name: :model,
178
+ type: :string,
179
+ label: "Model",
180
+ required: false,
181
+ hint: "Oh My Pi model pattern or ID passed to --model"
182
+ },
183
+ {
184
+ name: :provider,
185
+ type: :string,
186
+ label: "Provider",
187
+ required: false,
188
+ hint: "Oh My Pi provider name passed to --provider"
189
+ }
190
+ ],
191
+ auth_modes: %i[api_key oauth],
192
+ openai_compatible: false
193
+ }
194
+ end
195
+
196
+ def capabilities
197
+ {
198
+ streaming: false,
199
+ file_upload: true,
200
+ vision: true,
201
+ tool_use: true,
202
+ # Oh My Pi's non-interactive CLI currently exposes only text print mode.
203
+ # Keep JSON mode disabled until the CLI ships a structured output flag.
204
+ json_mode: false,
205
+ mcp: false,
206
+ dangerous_mode: false
207
+ }
208
+ end
209
+
210
+ def error_patterns
211
+ COMMON_ERROR_PATTERNS
212
+ end
213
+
214
+ def supports_tool_control?
215
+ true
216
+ end
217
+
218
+ def auth_type
219
+ :oauth
220
+ end
221
+
222
+ def execution_semantics
223
+ {
224
+ prompt_delivery: :flag,
225
+ output_format: :text,
226
+ sandbox_aware: false,
227
+ uses_subcommand: false,
228
+ non_interactive_flag: "-p",
229
+ legitimate_exit_codes: [0],
230
+ stderr_is_diagnostic: true,
231
+ parses_rate_limit_reset: false
232
+ }
233
+ end
234
+
235
+ protected
236
+
237
+ def build_command(prompt, options)
238
+ runtime = options[:provider_runtime]
239
+ provider = runtime&.api_provider || @config.provider
240
+ model = runtime&.model || @config.model
241
+
242
+ cmd = [self.class.binary_name, "--no-session"]
243
+ cmd += @config.default_flags if @config.default_flags&.any?
244
+ cmd += runtime.flags if runtime
245
+ cmd += ["--provider", provider] if provider
246
+ cmd += ["--model", model] if model
247
+ cmd << "--no-tools" if options[:tools] == :none
248
+ cmd += ["-p", prompt]
249
+
250
+ cmd
251
+ end
252
+
253
+ def default_timeout
254
+ 300
255
+ end
256
+ end
257
+ end
258
+ end
@@ -28,6 +28,7 @@ module AgentHarness
28
28
  aliases: [:copilot]
29
29
  },
30
30
  {name: :pi, require_path: "agent_harness/providers/pi", class_name: :Pi, aliases: []},
31
+ {name: :omp, require_path: "agent_harness/providers/omp", class_name: :OhMyPi, aliases: []},
31
32
  {name: :codex, require_path: "agent_harness/providers/codex", class_name: :Codex, aliases: []},
32
33
  {name: :opencode, require_path: "agent_harness/providers/opencode", class_name: :Opencode, aliases: []},
33
34
  {name: :kilocode, require_path: "agent_harness/providers/kilocode", class_name: :Kilocode, aliases: []},
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AgentHarness
4
- VERSION = "0.28.6"
4
+ VERSION = "0.29.0"
5
5
  end
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.28.6
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -134,6 +134,7 @@ files:
134
134
  - lib/agent_harness/providers/kilocode.rb
135
135
  - lib/agent_harness/providers/mcp_config_file_support.rb
136
136
  - lib/agent_harness/providers/mistral_vibe.rb
137
+ - lib/agent_harness/providers/omp.rb
137
138
  - lib/agent_harness/providers/opencode.rb
138
139
  - lib/agent_harness/providers/pi.rb
139
140
  - lib/agent_harness/providers/rate_limit_reset_parsing.rb