agent-harness 0.18.1 → 0.19.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: 54a70f890647d75ff53ab3b965cd3071fdab5fa42ccf1f6aad95387a5e4b1469
4
- data.tar.gz: 34dc8a3d3725d4d8d436671818716ef340ddc712412bd86fb73aa55cce5d2dd5
3
+ metadata.gz: 35805e09786006c623325c10d579dc95c8be7863536ae52b0440332403f108da
4
+ data.tar.gz: 162638a405a7b1cfadbd5d03396cd898142ecf4fa57a9d811726644c0329e485
5
5
  SHA512:
6
- metadata.gz: 055d9ef0f856eae6105e54ab76236c8b1fa3b0e24679b0874b1ebdcf74d610d6a005862ab18ce586ce21657bb7e93042558175fc3e8df2b41f5d2aa52cc7ac44
7
- data.tar.gz: ee320df58fd1ed97c931ed079068971918890dfed3ca338b835db5ad39b204de053e19e6c100da9dfcfe199fd9271fe91944f25abb0735c34f246ddcdeb0a901
6
+ metadata.gz: b624b8380ae5d7e0759be8ef02b1affafff2180773172e4cd7d9d715678570d6e04d1090ffad50461c5d9a8500f756191a0c7705df042603d02d359dc45ac8e6
7
+ data.tar.gz: 8c8de046b361f532a701d5121550fe41ce47b4f9bab3799b5754dfb7db01803a42e3f898782af8dfdf2e7b5d783f9981e42ddd95db7d3ccf3775a70e74272cf6
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.18.1"
2
+ ".": "0.19.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.19.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.18.2...agent-harness/v0.19.0) (2026-05-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * opencode-ai install contract should support postinstall (native binary download) ([b3eed97](https://github.com/viamin/agent-harness/commit/b3eed971b9bc46ce2bb2976b70dfd65b15ae5e9b))
9
+ * **opencode:** add requires_postinstall and postinstall_command to install contract ([d6808fd](https://github.com/viamin/agent-harness/commit/d6808fd62476289b199202b0c20c65fc4966bfcc)), closes [#223](https://github.com/viamin/agent-harness/issues/223)
10
+
11
+ ## [0.18.2](https://github.com/viamin/agent-harness/compare/agent-harness/v0.18.1...agent-harness/v0.18.2) (2026-05-17)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **release:** require conventional PR titles for release-please ([4f0054f](https://github.com/viamin/agent-harness/commit/4f0054f4c9f35251c3185fa8bbf645fcace130b5))
17
+ * **release:** require conventional PR titles for release-please ([fd6633d](https://github.com/viamin/agent-harness/commit/fd6633dff3710ad1575af9bd172f047a93a8f647))
18
+
3
19
  ## [0.18.1](https://github.com/viamin/agent-harness/compare/agent-harness/v0.18.0...agent-harness/v0.18.1) (2026-05-12)
4
20
 
5
21
 
@@ -124,7 +124,7 @@ module AgentHarness
124
124
  :idle_timeout
125
125
  when /rate.?limit|too many requests|\b429\b/i
126
126
  :rate_limited
127
- when /quota|usage.?limit|billing/i
127
+ when /quota|usage.?limit|billing|(?:weekly|monthly)(?:\/(?:weekly|monthly))?\s+limit\s+exhausted/i
128
128
  :quota_exceeded
129
129
  when /auth|unauthorized|forbidden|invalid.*(key|token)|\b401\b|\b403\b/i
130
130
  :auth_expired
@@ -31,11 +31,12 @@ module AgentHarness
31
31
 
32
32
  # Rate limiting and circuit breaker errors
33
33
  class RateLimitError < Error
34
- attr_reader :reset_time, :provider
34
+ attr_reader :reset_time, :provider, :error_category
35
35
 
36
- def initialize(message = nil, reset_time: nil, provider: nil, **kwargs)
36
+ def initialize(message = nil, reset_time: nil, provider: nil, error_category: :rate_limited, **kwargs)
37
37
  @reset_time = reset_time
38
38
  @provider = provider
39
+ @error_category = error_category
39
40
  super(message, **kwargs)
40
41
  end
41
42
  end
@@ -22,7 +22,7 @@ module AgentHarness
22
22
  source = contract[:source]
23
23
  install_command = contract[:install_command]&.dup
24
24
 
25
- {
25
+ normalized = {
26
26
  provider: provider_name.to_sym,
27
27
  source_type: normalize_metadata_source_type(contract[:source_type] || source),
28
28
  package_name: metadata_package_name(contract, source),
@@ -35,6 +35,11 @@ module AgentHarness
35
35
  install_command: install_command,
36
36
  install_command_string: contract[:install_command_string] || install_command&.join(" ")
37
37
  }
38
+
39
+ normalized[:requires_postinstall] = contract[:requires_postinstall] if contract.key?(:requires_postinstall)
40
+ normalized[:postinstall_command] = contract[:postinstall_command] if contract.key?(:postinstall_command)
41
+
42
+ normalized
38
43
  end
39
44
 
40
45
  def self.normalize_metadata_source_type(source)
@@ -838,9 +843,11 @@ module AgentHarness
838
843
  quota: [
839
844
  /requires more credits/i,
840
845
  /insufficient credits/i,
846
+ /insufficient balance/i,
841
847
  /credit.*exceeded/i,
842
848
  /spend limit.*reached/i,
843
- /billing.*limit/i
849
+ /billing.*limit/i,
850
+ /(?:weekly|monthly)(?:\/(?:weekly|monthly))?\s+limit\s+exhausted/i
844
851
  ]
845
852
  }
846
853
  end
@@ -1130,7 +1137,7 @@ module AgentHarness
1130
1137
  rescue AuthenticationError => e
1131
1138
  failure_smoke_test_result(e.message, :auth_expired)
1132
1139
  rescue RateLimitError => e
1133
- failure_smoke_test_result(e.message, :rate_limited)
1140
+ failure_smoke_test_result(e.message, e.error_category || :rate_limited)
1134
1141
  rescue ProviderError => e
1135
1142
  failure_smoke_test_result(e.message, classify_smoke_test_message(e.message))
1136
1143
  end
@@ -26,6 +26,7 @@ module AgentHarness
26
26
  class Base
27
27
  include Adapter
28
28
  include Extensions::DeepDupable
29
+ include RateLimitResetParsing
29
30
 
30
31
  DEFAULT_SMOKE_TEST_CONTRACT = {
31
32
  prompt: "Reply with exactly OK.",
@@ -52,7 +53,8 @@ module AgentHarness
52
53
  quota_exceeded: [
53
54
  /quota.*exceeded/i,
54
55
  /insufficient.*quota/i,
55
- /billing/i
56
+ /billing/i,
57
+ /(?:weekly|monthly)(?:\/(?:weekly|monthly))?\s+limit\s+exhausted/i
56
58
  ],
57
59
  transient: [
58
60
  /timeout/i,
@@ -432,10 +434,6 @@ module AgentHarness
432
434
  #
433
435
  # @param text [String, nil] error output text
434
436
  # @return [Time, nil] UTC reset time, or nil if not parseable
435
- def parse_rate_limit_reset(text)
436
- nil
437
- end
438
-
439
437
  # Run a lightweight provider-owned preflight check before committing to a
440
438
  # full prompt execution.
441
439
  #
@@ -1038,8 +1036,13 @@ module AgentHarness
1038
1036
 
1039
1037
  def map_to_error_class(classification, original_error)
1040
1038
  case classification
1041
- when :rate_limited
1042
- RateLimitError.new(original_error.message, original_error: original_error)
1039
+ when :rate_limited, :quota_exceeded
1040
+ RateLimitError.new(
1041
+ original_error.message,
1042
+ reset_time: parse_rate_limit_reset(original_error.message),
1043
+ error_category: classification,
1044
+ original_error: original_error
1045
+ )
1043
1046
  when :auth_expired
1044
1047
  AuthenticationError.new(
1045
1048
  original_error.message,
@@ -189,7 +189,7 @@ module AgentHarness
189
189
  non_interactive_flag: nil,
190
190
  legitimate_exit_codes: [0],
191
191
  stderr_is_diagnostic: true,
192
- parses_rate_limit_reset: false
192
+ parses_rate_limit_reset: true
193
193
  }
194
194
  end
195
195
 
@@ -14,6 +14,7 @@ module AgentHarness
14
14
  SUPPORTED_CLI_REQUIREMENT = Gem::Requirement.new(">= #{SUPPORTED_CLI_VERSION}", "< 1.4.0").freeze
15
15
  INSTALL_COMMAND_PREFIX = ["npm", "install", "-g", "--ignore-scripts"].freeze
16
16
  SUPPORTED_CLI_VERSIONS = [SUPPORTED_CLI_VERSION].freeze
17
+ POSTINSTALL_COMMAND = "node $(npm root -g)/opencode-ai/postinstall.mjs"
17
18
  VERSION_REQUIREMENT_STRINGS = SUPPORTED_CLI_REQUIREMENT.requirements
18
19
  .map { |op, ver| "#{op} #{ver}".freeze }
19
20
  .freeze
@@ -89,6 +90,8 @@ module AgentHarness
89
90
  binary_name: binary_name,
90
91
  install_command_prefix: INSTALL_COMMAND_PREFIX,
91
92
  install_command: install_command,
93
+ requires_postinstall: true,
94
+ postinstall_command: POSTINSTALL_COMMAND,
92
95
  supported_versions: SUPPORTED_CLI_VERSIONS
93
96
  }
94
97
 
@@ -194,7 +197,7 @@ module AgentHarness
194
197
  non_interactive_flag: nil,
195
198
  legitimate_exit_codes: [0],
196
199
  stderr_is_diagnostic: true,
197
- parses_rate_limit_reset: false
200
+ parses_rate_limit_reset: true
198
201
  }
199
202
  end
200
203
 
@@ -24,6 +24,7 @@ module AgentHarness
24
24
 
25
25
  parse_retry_after(text) ||
26
26
  parse_reset_at(text) ||
27
+ parse_reset_at_datetime(text) ||
27
28
  parse_resets_time(text) ||
28
29
  parse_resets_date_time(text)
29
30
  end
@@ -46,6 +47,16 @@ module AgentHarness
46
47
  Time.at(match[1].to_i).utc
47
48
  end
48
49
 
50
+ # "reset at 2026-05-18 11:22:32"
51
+ def parse_reset_at_datetime(text)
52
+ match = text.match(/reset\s+at\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})/i)
53
+ return unless match
54
+
55
+ Time.strptime("#{match[1]} #{match[2]} UTC", "%Y-%m-%d %H:%M:%S %Z").utc
56
+ rescue ArgumentError
57
+ nil
58
+ end
59
+
49
60
  # "resets 5am (UTC)" / "resets 5:00am (UTC)"
50
61
  def parse_resets_time(text)
51
62
  match = text.match(/resets\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)\s*\(UTC\)/i)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AgentHarness
4
- VERSION = "0.18.1"
4
+ VERSION = "0.19.0"
5
5
  end
data/lib/agent_harness.rb CHANGED
@@ -372,9 +372,9 @@ require_relative "agent_harness/provider_health_check"
372
372
  # Provider layer
373
373
  require_relative "agent_harness/providers/registry"
374
374
  require_relative "agent_harness/providers/adapter"
375
- require_relative "agent_harness/providers/base"
376
375
  require_relative "agent_harness/providers/token_usage_parsing"
377
376
  require_relative "agent_harness/providers/rate_limit_reset_parsing"
377
+ require_relative "agent_harness/providers/base"
378
378
  require_relative "agent_harness/providers/mcp_config_file_support"
379
379
  require_relative "agent_harness/providers/anthropic"
380
380
  require_relative "agent_harness/providers/aider"
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.18.1
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
169
  - !ruby/object:Gem::Version
170
170
  version: '0'
171
171
  requirements: []
172
- rubygems_version: 4.0.6
172
+ rubygems_version: 4.0.10
173
173
  specification_version: 4
174
174
  summary: Unified interface for CLI-based AI coding agents
175
175
  test_files: []