agent-harness 0.36.7 → 0.36.8
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 +7 -0
- data/lib/agent_harness/providers/anthropic.rb +68 -8
- data/lib/agent_harness/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b5a9c9ca2e2b03184fad17fea28af20996a3710d23ed5e34feed302be6f54b8
|
|
4
|
+
data.tar.gz: 888c84fe0cc58a5d9f20a0f396bb75e429fc84287fcb5e4e337340841d2d70a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93eadc79037f9d847ad418023ed8ed9d4920e32445d63e39f4a81b951a65f9587386bf1d2012b632c644f73b7173e4471578160161063570f93c6f8781d671d3
|
|
7
|
+
data.tar.gz: 6f7e1a3084562f773e415b9bbc23157f3151308e4995a5bb7812ef8080cd871d21da581371f550c6abeff4485c13ad75fd1d873bbb26d76715b2805edd16a1a2
|
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
|
* **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
|
+
## [0.36.8](https://github.com/viamin/agent-harness/compare/agent-harness/v0.36.7...agent-harness/v0.36.8) (2026-08-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Claude CLI Not logged in responses should raise AuthenticationError ([#372](https://github.com/viamin/agent-harness/issues/372)) ([8782aef](https://github.com/viamin/agent-harness/commit/8782aef42b0ec6f8e550dc98e79d316bb58b6044))
|
|
14
|
+
|
|
8
15
|
## [0.36.7](https://github.com/viamin/agent-harness/compare/agent-harness/v0.36.6...agent-harness/v0.36.7) (2026-08-25)
|
|
9
16
|
|
|
10
17
|
|
|
@@ -216,6 +216,25 @@ module AgentHarness
|
|
|
216
216
|
|
|
217
217
|
private
|
|
218
218
|
|
|
219
|
+
# Message fragments the Claude CLI emits when the local session is
|
|
220
|
+
# missing or expired. Detected in the envelope's +result+ field even
|
|
221
|
+
# when +subtype+ is "success", since Claude reports the failure
|
|
222
|
+
# inside the payload rather than through the transport layer.
|
|
223
|
+
#
|
|
224
|
+
# Scoped to explicit expired / login-required phrases. A bare
|
|
225
|
+
# "authentication" match would also fire on transient service
|
|
226
|
+
# outages like "authentication service was unavailable", which
|
|
227
|
+
# belong on the retry / provider-fallback path, not the re-auth
|
|
228
|
+
# path.
|
|
229
|
+
AUTH_FAILURE_FRAGMENTS = [
|
|
230
|
+
"oauth token",
|
|
231
|
+
"not logged in",
|
|
232
|
+
"please run /login",
|
|
233
|
+
"login required",
|
|
234
|
+
"credentials expired",
|
|
235
|
+
"session expired"
|
|
236
|
+
].freeze
|
|
237
|
+
|
|
219
238
|
def classify_error_message(message)
|
|
220
239
|
msg_lower = message.downcase
|
|
221
240
|
|
|
@@ -223,13 +242,20 @@ module AgentHarness
|
|
|
223
242
|
"Rate limit exceeded"
|
|
224
243
|
elsif msg_lower.include?("deprecat") || msg_lower.include?("end-of-life")
|
|
225
244
|
"Model deprecated"
|
|
226
|
-
elsif
|
|
245
|
+
elsif authentication_failure_message?(msg_lower)
|
|
227
246
|
"Authentication error"
|
|
228
247
|
else
|
|
229
248
|
message
|
|
230
249
|
end
|
|
231
250
|
end
|
|
232
251
|
|
|
252
|
+
def authentication_failure_message?(message)
|
|
253
|
+
return false if message.nil? || message.empty?
|
|
254
|
+
|
|
255
|
+
msg_lower = message.downcase
|
|
256
|
+
AUTH_FAILURE_FRAGMENTS.any? { |fragment| msg_lower.include?(fragment) }
|
|
257
|
+
end
|
|
258
|
+
|
|
233
259
|
def extract_tokens(parsed)
|
|
234
260
|
usage = parsed["usage"]
|
|
235
261
|
return nil unless usage
|
|
@@ -654,22 +680,38 @@ module AgentHarness
|
|
|
654
680
|
def parse_response(result, duration:)
|
|
655
681
|
output = result.stdout
|
|
656
682
|
error = nil
|
|
683
|
+
auth_source = nil
|
|
657
684
|
tokens = nil
|
|
658
685
|
metadata = {}
|
|
659
686
|
|
|
687
|
+
parsed = parse_json_output(output)
|
|
688
|
+
|
|
689
|
+
# Claude CLI can return `subtype: "success"` with `is_error: true`
|
|
690
|
+
# (e.g. "Not logged in · Please run /login"); trust `is_error` and the
|
|
691
|
+
# process exit code rather than the envelope subtype.
|
|
692
|
+
envelope_error = parsed && parsed["is_error"]
|
|
693
|
+
envelope_message = parsed && parsed["result"]
|
|
694
|
+
|
|
660
695
|
if result.failed?
|
|
661
696
|
combined = [result.stdout, result.stderr].compact.join("\n")
|
|
662
697
|
error = classify_error_message(combined)
|
|
698
|
+
# With an envelope present, restrict auth detection to stderr so
|
|
699
|
+
# assistant `result` text mentioning "authentication" can't trip
|
|
700
|
+
# a false positive. Without an envelope, the combined output is
|
|
701
|
+
# our only window into CLI-reported auth failures.
|
|
702
|
+
auth_source ||= parsed ? result.stderr : combined
|
|
663
703
|
end
|
|
664
704
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
#
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
end
|
|
705
|
+
if envelope_error
|
|
706
|
+
error ||= classify_error_message(envelope_message || "Unknown Claude CLI error")
|
|
707
|
+
# Prefer the envelope's own message for auth detection to avoid
|
|
708
|
+
# false positives from assistant text that mentions "authentication".
|
|
709
|
+
auth_source = envelope_message
|
|
710
|
+
end
|
|
672
711
|
|
|
712
|
+
raise_if_authentication_failure(auth_source)
|
|
713
|
+
|
|
714
|
+
if parsed
|
|
673
715
|
output = parsed["result"] || output
|
|
674
716
|
tokens = extract_tokens(parsed)
|
|
675
717
|
metadata = extract_envelope_metadata(parsed)
|
|
@@ -786,6 +828,24 @@ module AgentHarness
|
|
|
786
828
|
self.class.send(:classify_error_message, message)
|
|
787
829
|
end
|
|
788
830
|
|
|
831
|
+
def authentication_failure_message?(message)
|
|
832
|
+
self.class.send(:authentication_failure_message?, message)
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
# Raise AuthenticationError so callers (e.g. Conductor) can route to
|
|
836
|
+
# re-auth handling instead of retrying through generic provider-error
|
|
837
|
+
# paths. Fires when the source message (envelope's +result+ field, or
|
|
838
|
+
# the raw combined process output when there is no envelope) matches
|
|
839
|
+
# a Claude "not logged in" / session-expired signal.
|
|
840
|
+
def raise_if_authentication_failure(source_message)
|
|
841
|
+
return unless authentication_failure_message?(source_message)
|
|
842
|
+
|
|
843
|
+
message = source_message.to_s.strip
|
|
844
|
+
message = "Claude CLI reported an authentication failure" if message.empty?
|
|
845
|
+
|
|
846
|
+
raise AuthenticationError.new(message, provider: self.class.provider_name)
|
|
847
|
+
end
|
|
848
|
+
|
|
789
849
|
def parse_claude_mcp_output(output)
|
|
790
850
|
servers = []
|
|
791
851
|
return servers unless output
|