agent-harness 0.29.0 → 0.30.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: e929940987f238b75b654eadf2d882b0a554fb755c3badac115c7ae218875677
4
- data.tar.gz: a83fefa5de68968675304ed70316ccfa37a7b3063bd58a1edf3b8c493f017daf
3
+ metadata.gz: ae26a14b4826681fbe36862291bfc6342afab2dd709e2386312ab072582fc3cb
4
+ data.tar.gz: b53f1b28bd4662e1522db2670e7f698e71937b600d80d1fa30de056a3e3d9deb
5
5
  SHA512:
6
- metadata.gz: a08f5ce50f0b0c200034d53908ded075808f7a2e374aff7365d54240cf1dc291d1cde28d4e1d4e76cc2d16273aa30bb17f81384243d626ce60fd5c5fd6d2f702
7
- data.tar.gz: 615fec572434e6a0653f3ae3cd1723f551d7578eb30ef2f1ee0a2714c6221be8acd29c06e3830d10f0e88c040c31faab51658c26b09b79e18215f295587c644e
6
+ metadata.gz: e16d723dd4945831271b4d10cfc1de69042181796fecde7500c840060fe18f4ba637d567cec2ca342c015b3dde4d288e4716ae0471cded2cd43572f13bb7208f
7
+ data.tar.gz: 751f5a515d4ff3764a24fd21dba24733545d4da3fe06803d02d2d270504a7fcce12ea2a9dc1a08ede985aca4bd0e76c07e75fcb5be6e03ac7067e6ee005c38ae
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.29.0"
2
+ ".": "0.30.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.30.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.29.0...agent-harness/v0.30.0) (2026-07-16)
10
+
11
+
12
+ ### Features
13
+
14
+ * Implement Oh My Pi execution, auth, and smoke-test contract ([#300](https://github.com/viamin/agent-harness/issues/300)) ([dc3f5ec](https://github.com/viamin/agent-harness/commit/dc3f5ecce4508f103a8b769727e31aaa3d694c7e))
15
+
9
16
  ## [0.29.0](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.6...agent-harness/v0.29.0) (2026-07-16)
10
17
 
11
18
 
@@ -31,6 +31,71 @@ module AgentHarness
31
31
  SUPPORTED_BUN_VERSION = "1.3.14"
32
32
  BUN_REQUIREMENT_STRING = ">= #{SUPPORTED_BUN_VERSION}".freeze
33
33
 
34
+ # Smoke-test contract suitable for container health probes.
35
+ #
36
+ # Oh My Pi runs non-interactively via `-p` with `--no-session`, so the
37
+ # probe is a single deterministic round trip. The contract is tuned for
38
+ # a tight probe budget: a short prompt that elicits a predictable reply
39
+ # and a bounded timeout that accommodates a cold Bun runtime start.
40
+ SMOKE_TEST_CONTRACT = {
41
+ prompt: "Reply with exactly OK.",
42
+ expected_output: "OK",
43
+ timeout: 30,
44
+ require_output: true,
45
+ success_message: "Oh My Pi smoke test passed"
46
+ }.freeze
47
+
48
+ # Oh My Pi is a multi-provider CLI: it routes to a backend through
49
+ # `--provider` and surfaces that backend's error vocabulary. The
50
+ # patterns below extend the shared HTTP-style set with Oh My Pi /
51
+ # upstream-Pi specific phrasing observed for auth expiry, quota and
52
+ # rate-limit surfaces, model resolution failures, and transient
53
+ # network faults.
54
+ ERROR_PATTERNS = {
55
+ rate_limited: COMMON_ERROR_PATTERNS[:rate_limited],
56
+ auth_expired: COMMON_ERROR_PATTERNS[:auth_expired] + [
57
+ /\b401\b/,
58
+ /session.*(?:expired|invalid)/i,
59
+ /log(?:ged)?.?in.*required/i,
60
+ /api.?key.*(?:invalid|missing|expired|revoked)/i,
61
+ /token.*(?:expired|invalid|revoked)/i,
62
+ /credentials.*(?:expired|invalid|missing)/i
63
+ ],
64
+ quota_exceeded: COMMON_ERROR_PATTERNS[:quota_exceeded],
65
+ # Model resolution is surfaced by both the omp CLI (unknown
66
+ # `--model`/`--provider` combination) and the upstream backend.
67
+ # Treat it as a distinct, non-retryable category so callers can
68
+ # fall back to a known-good model instead of retrying the same id.
69
+ model_not_found: [
70
+ /model.*not.*found/i,
71
+ /no.*such.*model/i,
72
+ /unknown.*model/i,
73
+ /model.*does.*not.*exist/i,
74
+ /invalid.*model/i,
75
+ /model.*unavailable/i,
76
+ /provider.*does.*not.*support.*model/i
77
+ ],
78
+ transient: COMMON_ERROR_PATTERNS[:transient] + [
79
+ /connection.*reset/i,
80
+ /econnreset/i,
81
+ /socket.*hang.*up/i,
82
+ /network.*error/i
83
+ ]
84
+ }.tap { |patterns| patterns.each_value(&:freeze) }.freeze
85
+
86
+ # Non-actionable output that Oh My Pi writes while the Bun runtime and
87
+ # the agent bootstrap. Downstream consumers use these to filter probe
88
+ # and run output so startup banners do not masquerade as failures.
89
+ NOISY_OUTPUT_PATTERNS = [
90
+ /oh.?my.?pi\b/i,
91
+ /pi(?:-coding.?agent)?\s+v?\d+\.\d+/i,
92
+ /\bbun\s+v?\d+\.\d+/i,
93
+ /\bloading/i,
94
+ /\binitializing/i,
95
+ /\bwarming.?up/i,
96
+ /fetching.*model/i
97
+ ].freeze
98
+
34
99
  class << self
35
100
  def provider_name
36
101
  :omp
@@ -49,7 +114,22 @@ module AgentHarness
49
114
  {
50
115
  auth: {
51
116
  service: :omp,
52
- api_family: :multi_provider
117
+ api_family: :multi_provider,
118
+ # Oh My Pi routes to a backend through `--provider` and reads
119
+ # that backend's API key from its conventional env var. The
120
+ # harness never reuses the upstream Pi credential store
121
+ # (paid_pi_auth_entry); callers pass backend keys per request
122
+ # through ProviderRuntime#env, which build_env materializes into
123
+ # the subprocess environment. This keeps the omp runner an
124
+ # independent entity from :pi.
125
+ #
126
+ # Deliberately do NOT advertise a harness-managed credential
127
+ # store here. Authentication has no omp read/write/validate path,
128
+ # so auth_status(:omp) reports "not implemented"; exposing
129
+ # credential_store would imply a session store that does not
130
+ # exist and let callers infer harness-managed session auth.
131
+ # Surface the implemented per-request env model instead.
132
+ api_key_source: :provider_runtime_env
53
133
  }
54
134
  }
55
135
  end
@@ -158,7 +238,7 @@ module AgentHarness
158
238
  end
159
239
 
160
240
  def smoke_test_contract
161
- Base::DEFAULT_SMOKE_TEST_CONTRACT
241
+ SMOKE_TEST_CONTRACT
162
242
  end
163
243
  end
164
244
 
@@ -202,13 +282,60 @@ module AgentHarness
202
282
  # Oh My Pi's non-interactive CLI currently exposes only text print mode.
203
283
  # Keep JSON mode disabled until the CLI ships a structured output flag.
204
284
  json_mode: false,
285
+ # MCP capability decision: the omp non-interactive print-mode path
286
+ # (`omp --no-session -p`) does not accept an MCP server config flag
287
+ # in the harness's supported CLI version. Keep MCP disabled here so
288
+ # callers do not attempt to attach servers that the runtime would
289
+ # reject. Revisit once omp ships a stable `--mcp-config` flag.
205
290
  mcp: false,
206
291
  dangerous_mode: false
207
292
  }
208
293
  end
209
294
 
210
295
  def error_patterns
211
- COMMON_ERROR_PATTERNS
296
+ ERROR_PATTERNS
297
+ end
298
+
299
+ # Downstream-facing error classification. Augments the shared quota set
300
+ # with auth-expiry, model-resolution, and authentication-specific
301
+ # phrasing so consumers can route omp failures without re-deriving the
302
+ # CLI vocabulary. The inherited `:quota` set is deliberately preserved
303
+ # (not overridden) so omp's multi-provider backends surface their full
304
+ # credit/balance vocabulary (requires more credits, insufficient
305
+ # balance, spend limit reached, billing limit, etc.).
306
+ def error_classification_patterns
307
+ super.merge(
308
+ auth_expired: ERROR_PATTERNS[:auth_expired],
309
+ authentication: [
310
+ /api.?key.*not.*(?:set|configured)/i,
311
+ /no.*api.?key/i,
312
+ /missing.*credentials/i,
313
+ /log(?:ged)?.?in.*required/i
314
+ ],
315
+ model_not_found: ERROR_PATTERNS[:model_not_found]
316
+ )
317
+ end
318
+
319
+ # Oh My Pi emits a startup banner (version, Bun runtime, model fetch)
320
+ # that is not actionable. Expose it so callers can strip probe noise.
321
+ def noisy_error_patterns
322
+ NOISY_OUTPUT_PATTERNS
323
+ end
324
+
325
+ def translate_error(message)
326
+ if ERROR_PATTERNS[:model_not_found].any? { |p| message.match?(p) }
327
+ "Oh My Pi could not resolve the requested model. Check --model/--provider."
328
+ elsif /api.?key.*not.*(?:set|configured)/i.match?(message) || /no.*api.?key/i.match?(message)
329
+ "Oh My Pi API key not set for the selected provider."
330
+ else
331
+ message
332
+ end
333
+ end
334
+
335
+ # Oh My Pi runs stateless through `--no-session`; the harness does not
336
+ # expose session persistence, so callers cannot resume a session.
337
+ def supports_sessions?
338
+ false
212
339
  end
213
340
 
214
341
  def supports_tool_control?
@@ -219,6 +346,36 @@ module AgentHarness
219
346
  :oauth
220
347
  end
221
348
 
349
+ # Conventional backend API-key env vars the omp CLI reads. Oh My Pi is
350
+ # multi-provider: the effective var is the one matching the selected
351
+ # `--provider`, supplied per request through ProviderRuntime#env.
352
+ def api_key_env_var_names
353
+ [
354
+ "ANTHROPIC_API_KEY",
355
+ "OPENAI_API_KEY",
356
+ "GEMINI_API_KEY",
357
+ "GOOGLE_API_KEY",
358
+ "XAI_API_KEY",
359
+ "DEEPSEEK_API_KEY",
360
+ "OPENROUTER_API_KEY",
361
+ "GROQ_API_KEY",
362
+ "MISTRAL_API_KEY"
363
+ ]
364
+ end
365
+
366
+ # omp routes through `--provider` rather than an env-driven proxy/base
367
+ # URL, so there are no known proxy header vars to scrub when a caller
368
+ # supplies its own key.
369
+ def api_key_unset_vars
370
+ []
371
+ end
372
+
373
+ # When running against an OAuth/subscription session, drop backend
374
+ # API-key env vars so the CLI prefers the stored session credentials.
375
+ def subscription_unset_vars
376
+ api_key_env_var_names
377
+ end
378
+
222
379
  def execution_semantics
223
380
  {
224
381
  prompt_delivery: :flag,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AgentHarness
4
- VERSION = "0.29.0"
4
+ VERSION = "0.30.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.29.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan