agent-cli-runtime 0.1.1 → 0.2.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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +139 -7
- data/agent-cli-runtime.gemspec +3 -2
- data/lib/agent_cli_runtime/errors.rb +7 -0
- data/lib/agent_cli_runtime/opencode/inspection.rb +32 -0
- data/lib/agent_cli_runtime/opencode/overlay.rb +573 -0
- data/lib/agent_cli_runtime/opencode/probe.rb +280 -0
- data/lib/agent_cli_runtime/opencode/result_parser.rb +402 -0
- data/lib/agent_cli_runtime/profile.rb +36 -2
- data/lib/agent_cli_runtime/profiles.rb +70 -1
- data/lib/agent_cli_runtime/runtime.rb +16 -2
- data/lib/agent_cli_runtime/values.rb +432 -0
- data/lib/agent_cli_runtime/version.rb +1 -1
- data/lib/agent_cli_runtime.rb +23 -3
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38323f74e355a6a9da1dfb45ccee48c253ffbd0c9693a16cd62910ced381c7cb
|
|
4
|
+
data.tar.gz: 2e38766e9b737df1e5360ac8013849cf2108dab3f8b937f24ec393c001aba669
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 95e1213e6586ea162e2a12294b350d4ea5190c8f09239e276fe4de17ceb1a7787c0c4979e3e71c6acb0f8e318facea099820a221130b123549d8a123c13e62b7
|
|
7
|
+
data.tar.gz: 1cca33e3af240a7abf5b8627d69256ee43647557fc2aeeab4f74692c7ba08ff74a0b0a5cdef202796f4559cc04d0498138762003fd14845c61029bd0b7603231
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.0 - 2026-08-15
|
|
6
|
+
|
|
7
|
+
- Add OpenCode `1.18.16+` as a fifth immutable built-in profile with exact
|
|
8
|
+
`provider/model` routing and faithful model-variant validation.
|
|
9
|
+
- Add route-aware offline probing for the required run/export flags, selected
|
|
10
|
+
authentication source, cached model inventory, and exact requested route.
|
|
11
|
+
- Add invocation-owned OpenCode config/data/cache/state overlays with
|
|
12
|
+
deny-first `read-only` and `workspace-write` policies, explicit credential
|
|
13
|
+
forwarding, owner-private resources, and idempotent cleanup.
|
|
14
|
+
- Add strict run/export correlation and typed outcomes for completion,
|
|
15
|
+
authentication/configuration/CLI failure, malformed output, cancellation,
|
|
16
|
+
and timeout while preserving requested versus actual route identity.
|
|
17
|
+
- Preserve unavailable separately from numeric zero for input, output,
|
|
18
|
+
cache-read, cache-write, reasoning, and cost evidence.
|
|
19
|
+
- Keep process spawning, streaming, timeout/cancellation supervision,
|
|
20
|
+
process-tree cleanup, retries, and post-run inspection execution with the
|
|
21
|
+
caller; the component returns commands and normalizes captured evidence.
|
|
22
|
+
|
|
3
23
|
## 0.1.1 - 2026-08-11
|
|
4
24
|
|
|
5
25
|
- Expose each profile's immutable credential-environment key inventory so
|
data/README.md
CHANGED
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
`agent-cli-runtime` is a small Ruby library for tools that integrate with
|
|
4
4
|
locally installed headless agent CLIs. It ships immutable profiles for Claude
|
|
5
|
-
Code, Codex CLI, Pi,
|
|
6
|
-
argv/stdin. It also reports typed capability evidence, extracts
|
|
7
|
-
provider JSON events, and exposes an honest local diagnostic
|
|
5
|
+
Code, Codex CLI, Pi, Grok CLI, and OpenCode and compiles provider-neutral
|
|
6
|
+
requests into argv/stdin. It also reports typed capability evidence, extracts
|
|
7
|
+
usage from provider JSON events, and exposes an honest local diagnostic
|
|
8
|
+
command. OpenCode adds an invocation-owned overlay and strict captured-result
|
|
9
|
+
normalizer while leaving process supervision with the caller.
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
13
|
```ruby
|
|
12
|
-
gem "agent-cli-runtime", "~> 0.
|
|
14
|
+
gem "agent-cli-runtime", "~> 0.2.0"
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
```ruby
|
|
16
18
|
require "agent_cli_runtime"
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
Ruby 3.4 or newer is required.
|
|
21
|
+
Ruby 3.4 or newer is required. The 0.2.x line is tested on Linux and macOS.
|
|
20
22
|
|
|
21
23
|
## Compile an invocation
|
|
22
24
|
|
|
@@ -56,6 +58,14 @@ represent.
|
|
|
56
58
|
- `extract_usage(profile, event)` normalizes provider usage when present and
|
|
57
59
|
returns `nil` when usage is absent or malformed.
|
|
58
60
|
- `observe(profile, result)` normalizes bounded, redacted result metadata.
|
|
61
|
+
- `prepare!(open_code_preparation)` creates and probes an isolated OpenCode
|
|
62
|
+
overlay and returns a `PreparedInvocation`; it does not spawn OpenCode.
|
|
63
|
+
- `parse_run(profile, stdout:)` parses a successful OpenCode JSONL capture
|
|
64
|
+
into the session and terminal-message identity required for inspection.
|
|
65
|
+
- `prepare_inspection(prepared, parsed_run)` compiles the non-model sanitized
|
|
66
|
+
session-export command without executing it.
|
|
67
|
+
- `normalize(profile, captured, requested_route:)` returns one typed OpenCode
|
|
68
|
+
outcome from caller-captured run, termination, and inspection evidence.
|
|
59
69
|
|
|
60
70
|
Provider arguments accept a built-in name or an `AgentCliRuntime::Profile`.
|
|
61
71
|
Unknown built-in names raise `AgentCliRuntime::UnknownProvider`; they are not
|
|
@@ -108,6 +118,128 @@ where the CLI owns its subscription/session state. `configuration_directory`
|
|
|
108
118
|
resolves that location from a caller-supplied home and environment without
|
|
109
119
|
reading credentials or deciding authentication policy.
|
|
110
120
|
|
|
121
|
+
## Prepare and normalize OpenCode
|
|
122
|
+
|
|
123
|
+
OpenCode `1.18.16+` requires an exact `provider/model` route and an explicit,
|
|
124
|
+
read-only configuration source. Configuration may define providers and an
|
|
125
|
+
exact default model, but it must not contain credential values. Name the
|
|
126
|
+
credential environment variables the caller is allowed to forward instead.
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
require "agent_cli_runtime"
|
|
130
|
+
require "tmpdir"
|
|
131
|
+
|
|
132
|
+
route = "anthropic/claude-sonnet-4-5"
|
|
133
|
+
profile = AgentCliRuntime::Profiles.fetch(:opencode)
|
|
134
|
+
request = AgentCliRuntime::Request.new(
|
|
135
|
+
profile: profile,
|
|
136
|
+
prompt: "Make the requested atomic edit",
|
|
137
|
+
permission_mode: "workspace-write",
|
|
138
|
+
model: route,
|
|
139
|
+
effort: "high"
|
|
140
|
+
)
|
|
141
|
+
preparation = AgentCliRuntime::OpenCodePreparationRequest.new(
|
|
142
|
+
request: request,
|
|
143
|
+
working_directory: Dir.pwd,
|
|
144
|
+
invocation_root: File.join(Dir.tmpdir, "my-opencode-invocation"),
|
|
145
|
+
configuration: {
|
|
146
|
+
"model" => route,
|
|
147
|
+
"provider" => {
|
|
148
|
+
"anthropic" => { "npm" => "@ai-sdk/anthropic" }
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
credential_environment_keys: ["ANTHROPIC_API_KEY"],
|
|
152
|
+
additional_read_roots: [Dir.pwd],
|
|
153
|
+
additional_write_roots: [Dir.pwd]
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
prepared = AgentCliRuntime.prepare!(preparation)
|
|
157
|
+
begin
|
|
158
|
+
# The caller owns spawning, capture, timeout/cancellation, and process-tree
|
|
159
|
+
# cleanup. Forward only this selected environment to the child.
|
|
160
|
+
run_argv = prepared.invocation.argv
|
|
161
|
+
run_stdin = prepared.invocation.stdin_data
|
|
162
|
+
run_environment = prepared.environment_for(env: ENV)
|
|
163
|
+
|
|
164
|
+
# After a zero main-process exit, parse the captured JSONL and run the
|
|
165
|
+
# separately compiled, non-model sanitized export under the same overlay.
|
|
166
|
+
parsed = AgentCliRuntime.parse_run(:opencode, stdout: run_stdout)
|
|
167
|
+
inspection = AgentCliRuntime.prepare_inspection(prepared, parsed)
|
|
168
|
+
inspection_argv = inspection.argv
|
|
169
|
+
inspection_environment = inspection.environment_for(env: ENV)
|
|
170
|
+
|
|
171
|
+
captured = AgentCliRuntime::CapturedResult.new(
|
|
172
|
+
stdout: run_stdout,
|
|
173
|
+
stderr: run_stderr,
|
|
174
|
+
termination: AgentCliRuntime::TerminationEvidence.new(exit_code: 0),
|
|
175
|
+
inspection_output: sanitized_export_stdout
|
|
176
|
+
)
|
|
177
|
+
outcome = AgentCliRuntime.normalize(
|
|
178
|
+
:opencode, captured, requested_route: prepared.requested_route
|
|
179
|
+
)
|
|
180
|
+
ensure
|
|
181
|
+
prepared.cleanup! if prepared
|
|
182
|
+
end
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`run_stdout`, `run_stderr`, and `sanitized_export_stdout` above are captures
|
|
186
|
+
provided by the caller's process supervisor. Execute the inspection only after
|
|
187
|
+
a successful main run. For a timeout, cancellation, signal, or non-zero exit,
|
|
188
|
+
construct the matching `TerminationEvidence` and normalize without pretending
|
|
189
|
+
that incomplete output is a successful result.
|
|
190
|
+
|
|
191
|
+
Preparation creates owner-private config, data, cache, and state paths below
|
|
192
|
+
the fresh invocation root; redirects OpenCode into them; disables ambient
|
|
193
|
+
project/default discovery and remote model refresh; checks version, required
|
|
194
|
+
flags, selected auth, cached route, and requested variant locally; and returns
|
|
195
|
+
discrete argv/environment values. It never sends a prompt or model request.
|
|
196
|
+
`PreparedInvocation#cleanup!` removes only invocation-owned paths and is safe
|
|
197
|
+
to call twice. Call it from the process owner's `ensure` path after every
|
|
198
|
+
pre-spawn and post-spawn outcome.
|
|
199
|
+
|
|
200
|
+
`read-only` denies edits, shell, unsafe tools, and external writes.
|
|
201
|
+
`workspace-write` permits edits only under the declared write roots and still
|
|
202
|
+
denies unrestricted shell. A `nil` permission mode is rejected unless the
|
|
203
|
+
consumer supplies an explicit typed `OpenCodePermissionPolicy`; the ordinary
|
|
204
|
+
preparation API never silently falls back to a bypass. Plugin sources are
|
|
205
|
+
explicit, and `--pure` remains enabled when no plugin was selected.
|
|
206
|
+
|
|
207
|
+
A completed outcome contains one bounded final assistant message, requested
|
|
208
|
+
and sanitized-export-observed routes, and nullable input/output/cache
|
|
209
|
+
read/cache write/reasoning/cost fields. Missing evidence stays `nil`; numeric
|
|
210
|
+
zero stays zero. Other outcome kinds are `authentication_failure`,
|
|
211
|
+
`configuration_failure`, `cli_failure`, `malformed_output`, `cancelled`, and
|
|
212
|
+
`timed_out`. Diagnostics and unknown-event summaries are bounded and redacted.
|
|
213
|
+
|
|
214
|
+
Maintainers can run the installed-CLI offline contract without a prompt or
|
|
215
|
+
model request:
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
bundle exec ruby -Itest test/opencode_offline_smoke_test.rb
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
If an installation command is itself a package-manager shim, set
|
|
222
|
+
`AGENT_CLI_RUNTIME_OPENCODE_OFFLINE_BIN` to the already-installed native
|
|
223
|
+
OpenCode executable so the smoke cannot trigger shim installation or refresh
|
|
224
|
+
behavior.
|
|
225
|
+
|
|
226
|
+
The authenticated atomic-edit smoke is separately gated and refuses to run
|
|
227
|
+
without an explicit route, config path, credential variable name, opt-in, and
|
|
228
|
+
non-empty selected credential:
|
|
229
|
+
|
|
230
|
+
```sh
|
|
231
|
+
AGENT_CLI_RUNTIME_OPENCODE_LIVE=1 \
|
|
232
|
+
AGENT_CLI_RUNTIME_OPENCODE_LIVE_ROUTE=anthropic/claude-sonnet-4-5 \
|
|
233
|
+
AGENT_CLI_RUNTIME_OPENCODE_LIVE_CONFIG=/absolute/path/opencode.json \
|
|
234
|
+
AGENT_CLI_RUNTIME_OPENCODE_LIVE_CREDENTIAL_ENV=ANTHROPIC_API_KEY \
|
|
235
|
+
ANTHROPIC_API_KEY=... \
|
|
236
|
+
bundle exec ruby -Itest test/opencode_live_test.rb
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The live test records only route, CLI version, outcome/usage availability, and
|
|
240
|
+
cleanup state. It does not print the credential or raw selected config. A
|
|
241
|
+
missing opt-in input is an explicit skip, not a deterministic-suite failure.
|
|
242
|
+
|
|
111
243
|
## Inspect local prerequisites
|
|
112
244
|
|
|
113
245
|
```ruby
|
|
@@ -126,7 +258,7 @@ agent-runtime probe --all --json
|
|
|
126
258
|
```
|
|
127
259
|
|
|
128
260
|
The JSON contract is `{"schema_version":1,"probes":[...]}` and always orders
|
|
129
|
-
all-provider output as `claude`, `codex`, `pi`, `grok`.
|
|
261
|
+
all-provider output as `claude`, `codex`, `pi`, `grok`, `opencode`.
|
|
130
262
|
|
|
131
263
|
- Exit `0`: every requested local probe is ready.
|
|
132
264
|
- Exit `1`: at least one requested local prerequisite is unavailable.
|
|
@@ -172,7 +304,7 @@ own provider-health policy.
|
|
|
172
304
|
## Compatibility
|
|
173
305
|
|
|
174
306
|
Provider flags, event formats, and public value-object fields are
|
|
175
|
-
SemVer-governed behavior. Additive fields are compatible within 0.
|
|
307
|
+
SemVer-governed behavior. Additive fields are compatible within 0.2.x; removing
|
|
176
308
|
or changing an existing field or meaning requires a new minor version while the
|
|
177
309
|
gem remains pre-1.0.
|
|
178
310
|
|
data/agent-cli-runtime.gemspec
CHANGED
|
@@ -9,8 +9,9 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.description = <<~DESC
|
|
10
10
|
Agent CLI Runtime provides immutable profiles, invocation compilation,
|
|
11
11
|
local prerequisite probes, capability evidence, usage extraction, and
|
|
12
|
-
result normalization for Claude Code, Codex CLI, Pi,
|
|
13
|
-
not spawn agents or claim live provider health, quota,
|
|
12
|
+
result normalization for Claude Code, Codex CLI, Pi, Grok CLI, and
|
|
13
|
+
OpenCode. It does not spawn agents or claim live provider health, quota,
|
|
14
|
+
or credential validity.
|
|
14
15
|
DESC
|
|
15
16
|
spec.homepage = "https://github.com/ivankuznetsov/agent-cli-runtime"
|
|
16
17
|
spec.license = "MIT"
|
|
@@ -14,4 +14,11 @@ module AgentCliRuntime
|
|
|
14
14
|
class ProbeError < Error; end
|
|
15
15
|
class CompilationError < Error; end
|
|
16
16
|
class UnknownProvider < Error; end
|
|
17
|
+
class PreparationError < Error; end
|
|
18
|
+
class ConfigurationError < PreparationError; end
|
|
19
|
+
class AuthenticationError < ProbeError; end
|
|
20
|
+
class RouteUnavailable < ProbeError; end
|
|
21
|
+
class UnsafePathError < PreparationError; end
|
|
22
|
+
class ResultError < Error; end
|
|
23
|
+
class MalformedOutput < ResultError; end
|
|
17
24
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module AgentCliRuntime
|
|
2
|
+
module OpenCode
|
|
3
|
+
module Inspection
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
def compile(prepared, parsed_run)
|
|
7
|
+
unless prepared.is_a?(PreparedInvocation)
|
|
8
|
+
raise ArgumentError,
|
|
9
|
+
"prepared must be an AgentCliRuntime::PreparedInvocation"
|
|
10
|
+
end
|
|
11
|
+
unless parsed_run.is_a?(ParsedRun)
|
|
12
|
+
raise ArgumentError, "parsed_run must be an AgentCliRuntime::ParsedRun"
|
|
13
|
+
end
|
|
14
|
+
unless prepared.invocation.provider == :opencode
|
|
15
|
+
raise ConfigurationError,
|
|
16
|
+
"sanitized export inspection requires an OpenCode invocation"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
InspectionCommand.new(
|
|
20
|
+
argv: [
|
|
21
|
+
prepared.executable, "export", parsed_run.session_id, "--sanitize"
|
|
22
|
+
],
|
|
23
|
+
stdin_data: nil,
|
|
24
|
+
environment: prepared.environment,
|
|
25
|
+
credential_environment_keys: prepared.credential_environment_keys,
|
|
26
|
+
session_id: parsed_run.session_id,
|
|
27
|
+
message_id: parsed_run.terminal_message_id
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|