agent-harness 0.23.0 → 0.24.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: '0874add2f55550c3c9f9b723c84a4036a2dd79dd717dd62069062cfbde630820'
4
- data.tar.gz: 53c6335c3a47e4e0e18ea3151dad061f9302519013e27591c7541ebf6dfe51db
3
+ metadata.gz: 898eb630251f7c5f1f328b7e76d8d8e8ef587318b3fe013373329a719b2c9fec
4
+ data.tar.gz: c3e0c222e24f86e905765beb55439576dcc54c2e25b02b822f1fa350305cf8a3
5
5
  SHA512:
6
- metadata.gz: a7297580e4a5a34631421730cab4c0fc5712448864bacefe8c8f3c43c7652b5a42cb9f7c2a603f585f308d2a54117dff918804a45a30c3bb302ec223e545eed4
7
- data.tar.gz: 36428c8e91bee3395d235cd4ae309f6a43c177acc6991cdf3cd33ed4f0cc433a566849a8b282137466a455da5becdc63820a87a01a36eb8399f857ecbe457ef7
6
+ metadata.gz: ea46cfbe98a5d31c23e5a8a0feffc05ec2d478ea841c066553b1c05d6487d59a94cb3716a4bff29225e76a83b367b58917b77ee0e29db573cf9e89b8a8532ef3
7
+ data.tar.gz: 4cf59eaaeec3e2d4d0478e7ecf39ee849a0a66dda12c47449e99d89da852483c595cef6d99a163c947e58a8fdf19e396340b281a49e868d93e2a84d76613e776
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.23.0"
2
+ ".": "0.24.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@
5
5
  * add runner model compatibility contract (`AgentHarness.model_compatibility`) with structured `ModelCompatibility::Result` outcomes. Codex exposes static facts for CLI-gated models (e.g. `gpt-5.5` requires Codex CLI `>= 0.116.0`), a baseline supported-model list, supported auth modes, and a `DEFAULT_COMPATIBLE_MODEL_ID` fallback so downstream orchestrators can validate tier/model assignments before scheduling agent runs ([#259](https://github.com/viamin/agent-harness/issues/259)).
6
6
 
7
7
 
8
+ ## [0.24.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.23.0...agent-harness/v0.24.0) (2026-06-26)
9
+
10
+
11
+ ### Features
12
+
13
+ * **auth:** parse native Claude claudeAiOauth credentials shape ([#268](https://github.com/viamin/agent-harness/issues/268)) ([178edae](https://github.com/viamin/agent-harness/commit/178edae21e175c4744b75a151acfc6df3b4b3c5d)), closes [#264](https://github.com/viamin/agent-harness/issues/264)
14
+
8
15
  ## [0.23.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.22.5...agent-harness/v0.23.0) (2026-06-15)
9
16
 
10
17
 
@@ -163,12 +163,8 @@ module AgentHarness
163
163
  credentials = read_claude_credentials
164
164
  return {valid: false, expires_at: nil, error: "No credentials found"} unless credentials
165
165
 
166
- # Check if the credentials file has a token, preferring a non-blank oauth_token over apiKey
167
- oauth_token = credentials["oauth_token"]
168
- api_key = credentials["apiKey"]
169
- token = [oauth_token, api_key].find { |t| t.is_a?(String) && !t.strip.empty? }
166
+ token, expires_at = extract_claude_token(credentials)
170
167
  if token
171
- expires_at = parse_expiry(credentials["expiresAt"] || credentials["expires_at"])
172
168
  if expires_at && expires_at < Time.now
173
169
  {valid: false, expires_at: expires_at, error: "Session expired"}
174
170
  else
@@ -224,10 +220,20 @@ module AgentHarness
224
220
 
225
221
  credentials = read_claude_credentials
226
222
  credentials = {} unless credentials.is_a?(Hash)
227
- credentials["oauth_token"] = token.strip
228
- # Clear any existing expiry metadata so refreshed tokens are not treated as expired
229
- credentials.delete("expiresAt")
230
- credentials.delete("expires_at")
223
+
224
+ if credentials.key?("claudeAiOauth")
225
+ # Preserve the native claudeAiOauth shape
226
+ oauth = credentials["claudeAiOauth"]
227
+ oauth = {} unless oauth.is_a?(Hash)
228
+ oauth["accessToken"] = token.strip
229
+ oauth.delete("expiresAt")
230
+ credentials["claudeAiOauth"] = oauth
231
+ else
232
+ credentials["oauth_token"] = token.strip
233
+ # Clear any existing expiry metadata so refreshed tokens are not treated as expired
234
+ credentials.delete("expiresAt")
235
+ credentials.delete("expires_at")
236
+ end
231
237
 
232
238
  # Write under a file lock using tempfile + rename to avoid corruption and lost updates on concurrent refreshes
233
239
  tmpfile = Tempfile.new(".credentials", dir)
@@ -245,6 +251,36 @@ module AgentHarness
245
251
  {success: true}
246
252
  end
247
253
 
254
+ # Extract a usable token and expiry from Claude credentials,
255
+ # supporting both the native claudeAiOauth shape and the legacy
256
+ # top-level oauth_token/apiKey shape.
257
+ #
258
+ # @return [Array(String, Time)] token and parsed expiry, or nils
259
+ def extract_claude_token(credentials)
260
+ # Prefer the native claudeAiOauth nested shape written by the Claude CLI
261
+ if credentials.key?("claudeAiOauth")
262
+ oauth = credentials["claudeAiOauth"]
263
+ if oauth.is_a?(Hash)
264
+ access_token = oauth["accessToken"]
265
+ if non_blank?(access_token)
266
+ expires_at = parse_expiry(oauth["expiresAt"])
267
+ return [access_token, expires_at]
268
+ end
269
+ end
270
+ end
271
+
272
+ # Fall back to the legacy top-level shape
273
+ oauth_token = credentials["oauth_token"]
274
+ api_key = credentials["apiKey"]
275
+ token = [oauth_token, api_key].find { |t| non_blank?(t) }
276
+ expires_at = parse_expiry(credentials["expiresAt"] || credentials["expires_at"]) if token
277
+ [token, expires_at]
278
+ end
279
+
280
+ def non_blank?(value)
281
+ value.is_a?(String) && !value.strip.empty?
282
+ end
283
+
248
284
  def read_claude_credentials
249
285
  path = claude_credentials_path
250
286
  return nil unless File.exist?(path)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AgentHarness
4
- VERSION = "0.23.0"
4
+ VERSION = "0.24.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.23.0
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan